Custom plugins
Extend dimahS3 with definePlugin — hooks, endpoints, and context.
Official plugins like db() ship in their own docs. This page is for plugins you write with definePlugin.
Server plugins add lifecycle hooks, HTTP routes under plugins/{id}/…, and typed context on the dimahS3() instance. Pair server endpoints with a client plugin (defineClientPlugin + createS3Client) when the browser needs them.
Quick start
import { dimahS3, definePlugin, createEndpoint } from "@dimah-s3/server";
const audit = definePlugin({
id: "audit",
hooks: {
upload: {
onConfirmed: async ({ key, bucket }) => {
console.log("uploaded", bucket, key);
},
},
},
endpoints: {
recent: createEndpoint("recent", { method: "GET" }, async () => ({
events: [],
})),
},
context: {
log: (message: string) => console.log("[audit]", message),
},
});
export const s3 = dimahS3({
s3: s3Client,
defaultBucket,
upload: { enabled: true },
plugins: [audit],
});
s3.audit.log("ready");
// GET /api/s3/plugins/audit/recentContext is on s3.context[id] and flattened as s3[id] (same as s3.db).
Contract
| Field | Purpose |
|---|---|
id | Unique string (reserved: handler, api, context, getPlugin) |
hooks? | Lifecycle hooks — merged before your config hooks |
endpoints? | Routes under plugins/{id}/{path} via createEndpoint |
context? | Data exposed as s3[id] |
dependsOn? | Plugin ids that must be registered first |
init? | Sync validation after the context map is built |
enabled / method stay on DimahS3Config — plugins cannot override them.
Hook merge order: Composition.
Endpoints
endpoints: {
ping: createEndpoint("ping", { method: "GET" }, async ({ request }) => {
return { ok: true };
}),
},- Path:
plugins/{pluginId}/{path}(pluginEndpointPathin@dimah-s3/core) - Global
guardruns first; paths must not collide with core routes or another plugin
Use context for server-side data access; endpoints are the HTTP surface for browsers.
Client plugin
Mirror a server endpoint on the client:
import { defineClientPlugin, pluginEndpointPath } from "@dimah-s3/core";
export function auditClient() {
return defineClientPlugin({
id: "audit",
createMethods: (fetcher) => ({
recent: () => fetcher.get(pluginEndpointPath("audit", "recent")),
}),
});
}Register with createS3Client({ plugins: [auditClient()] }) — see React setup for S3Provider / useApi. Official packages use a light entry (e.g. @dimah-s3/db/client).
dependsOn and init
const quota = definePlugin({
id: "quota",
dependsOn: ["db"],
init({ config }) {
if (!config.upload?.enabled) {
throw new Error("quota plugin requires upload enabled");
}
},
hooks: { upload: { presignGuard: quotaGuard } },
});Register dependencies before dependents in the plugins array.