# 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:

<table>
<thead>
  <tr>
    <th>
      Property
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        hooks
      </code>
    </td>
    
    <td>
      <a href="https://github.com/unjs/hookable" rel="nofollow">
        <code>
          HookableCore
        </code>
      </a>
    </td>
    
    <td>
      Hook system for registering lifecycle callbacks.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        h3
      </code>
    </td>
    
    <td>
      <code>
        H3Core
      </code>
    </td>
    
    <td>
      The underlying <a href="https://github.com/h3js/h3" rel="nofollow">
        H3
      </a>
      
       application instance.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fetch
      </code>
    </td>
    
    <td>
      <code>
        (req: Request) => Response | Promise<Response>
      </code>
    </td>
    
    <td>
      The app's internal fetch handler.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        captureError
      </code>
    </td>
    
    <td>
      <code>
        (error: Error, context) => void
      </code>
    </td>
    
    <td>
      Programmatically capture errors into the error hook pipeline.
    </td>
  </tr>
</tbody>
</table>

## 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

<table>
<thead>
  <tr>
    <th>
      Hook
    </th>
    
    <th>
      Signature
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        request
      </code>
    </td>
    
    <td>
      <code>
        (event: HTTPEvent) => void | Promise<void>
      </code>
    </td>
    
    <td>
      Called at the start of each request.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        response
      </code>
    </td>
    
    <td>
      <code>
        (res: Response, event: HTTPEvent) => void | Promise<void>
      </code>
    </td>
    
    <td>
      Called after the response is created.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        error
      </code>
    </td>
    
    <td>
      <code>
        (error: Error, context: { event?: HTTPEvent, tags?: string[] }) => void
      </code>
    </td>
    
    <td>
      Called when an error is captured.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        close
      </code>
    </td>
    
    <td>
      <code>
        () => void
      </code>
    </td>
    
    <td>
      Called when the Nitro server is shutting down.
    </td>
  </tr>
</tbody>
</table>

<note>

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

</note>

### 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");
    }
  });
});
```
