# Plugins (https://s3.dimah.dev/docs/server/plugins)



Use `definePlugin` to create reusable server extensions (like [`db()`](https://s3.dimah.dev/docs/db)). Plugins can contribute lifecycle hooks, HTTP endpoints under your `basePath`, and typed instance context (`s3[id]`).

```ts title="lib/s3-audit-plugin.ts"
import { definePlugin, createS3Endpoint } from "@dimah-s3/server";

export const auditPlugin = definePlugin({
  id: "audit",
  hooks: {
    upload: {
      onConfirmed: async ({ key }) => {
        console.log("[audit] Upload confirmed:", key);
      },
    },
  },
  endpoints: {
    stats: createS3Endpoint("/audit/stats", { method: "GET" }, async () => ({
      status: "ok",
    })),
  },
  context: {
    log: (msg: string) => console.log("[audit]", msg),
  },
});
```

Register the plugin in your instance:

```ts title="lib/s3.ts"
import { dimahS3, route } from "@dimah-s3/server";
import { auditPlugin } from "./s3-audit-plugin";

export const s3 = dimahS3({
  client: awsS3,
  bucket: process.env.S3_BUCKET!,
  plugins: [auditPlugin],
  routes: {
    avatar: route({ upload: true }),
  },
});

// Access plugin context
s3.audit.log("Storage ready");
```

***

## Hook execution order [#hook-execution-order]

* **Guards**: Plugins execute first in registration order, followed by user config guards.
* **Lifecycle hooks (`onConfirmed`, `onDeleted`)**: User config hooks execute first, followed by plugin hooks.

## Frequently asked questions [#frequently-asked-questions]

<Accordions>
  <Accordion title="How do I create a client-side companion for my server plugin?">
    Use `defineClientPlugin` from `@dimah-s3/core`:

    ```ts
    import { defineClientPlugin, pluginPath } from "@dimah-s3/core";

    export function auditClient() {
      return defineClientPlugin({
        id: "audit",
        getActions: ($fetch) => ({
          getStats: () => $fetch(pluginPath("audit", "stats"), { method: "GET" }),
        }),
      });
    }
    ```
  </Accordion>

  <Accordion title="Can a route opt out of a global plugin?">
    Yes. Set `plugins: { [pluginId]: false }` in your route definition:

    ```ts
    avatar: route({
      upload: true,
      plugins: { audit: false },
    }),
    ```
  </Accordion>
</Accordions>
