# Plugins

> Use plugins to extend Nitro's runtime behavior.

Nitro plugins are **executed once** during server startup in order to allow extending Nitro's runtime behavior.
They receive `nitroApp` context, which can be used to hook into lifecycle events.

Plugins are auto-registered from the `plugins/` directory and run synchronously by file name order on the first Nitro initialization. Plugin functions themselves must be synchronous (return `void`), but the hooks they register can be async.

**Example:**

```ts [plugins/test.ts]
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  console.log('Nitro plugin', nitroApp)
})
```

If you have plugins in another directory, you can use the `plugins` option:

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  plugins: ['my-plugins/hello.ts']
})
```

## The `nitroApp` context

The plugin function receives a `nitroApp` object with the following properties:

| Property | Type | Description |
| --- | --- | --- |
| `hooks` | [`HookableCore`](https://github.com/unjs/hookable) | Hook system for registering lifecycle callbacks. |
| `h3` | `H3Core` | The underlying [H3](https://github.com/h3js/h3) application instance. |
| `fetch` | `(req: Request) => Response \| Promise<Response>` | The app's internal fetch handler. |
| `captureError` | `(error: Error, context) => void` | Programmatically capture errors into the error hook pipeline. |

## Nitro runtime hooks

You can use Nitro [hooks](https://github.com/unjs/hookable) to extend the default runtime behaviour of Nitro by registering custom functions to the lifecycle events within plugins.

**Example:**

```ts
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook("close", async () => {
    // Will run when nitro is being closed
  });
})
```

### Available hooks

| Hook | Signature | Description |
| --- | --- | --- |
| `request` | `(event: HTTPEvent) => void \| Promise<void>` | Called at the start of each request. |
| `response` | `(res: Response, event: HTTPEvent) => void \| Promise<void>` | Called after the response is created. |
| `error` | `(error: Error, context: { event?: HTTPEvent, tags?: string[] }) => void` | Called when an error is captured. |
| `close` | `() => void` | Called when the Nitro server is shutting down. |

> [!NOTE]
> The `NitroRuntimeHooks` interface is augmentable. Deployment presets (such as Cloudflare) can extend it with platform-specific hooks like `cloudflare:scheduled` and `cloudflare:email`.

### Unregistering hooks

The `hook()` method returns an unregister function that can be called to remove the hook:

```ts
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  const unregister = nitroApp.hooks.hook("request", (event) => {
    // ...
  });

  // Later, remove the hook
  unregister();
});
```

## Examples

### Capturing errors

You can use plugins to capture all application errors.

```ts
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook("error", async (error, { event }) => {
    console.error(`${event?.path} Application error:`, error)
  });
})
```

The `context` object includes an optional `tags` array that identifies the error source (e.g., `"request"`, `"response"`, `"cache"`, `"plugin"`, `"unhandledRejection"`, `"uncaughtException"`).

### Programmatic error capture

You can use `captureError` to manually feed errors into the error hook pipeline:

```ts
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  nitroApp.captureError(new Error("something went wrong"), {
    tags: ["startup"],
  });
});
```

### Graceful shutdown

Server will gracefully shutdown and wait for any background pending tasks initiated by `event.waitUntil`.

```ts
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook("close", async () => {
    // Clean up resources, close connections, etc.
  });
});
```

### Request and response lifecycle

You can use plugins to register hooks that run on the request lifecycle:

```ts
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook("request", (event) => {
    console.log("on request", event.path);
  });

  nitroApp.hooks.hook("response", (res, event) => {
    // Modify or inspect the response
    console.log("on response", res.status);
  });
});
```

### Modifying response headers

```ts
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook("response", (res, event) => {
    const { pathname } = new URL(event.req.url);
    if (pathname.endsWith(".css") || pathname.endsWith(".js")) {
      res.headers.append("Vary", "Origin");
    }
  });
});
```
