dimah-s3v0.4.1

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/recent

Context is on s3.context[id] and flattened as s3[id] (same as s3.db).

Contract

FieldPurpose
idUnique 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} (pluginEndpointPath in @dimah-s3/core)
  • Global guard runs 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.

On this page