Plugins

Use runtime plugins to extend Nitro's runtime behavior.

Runtime plugins are executed once during server startup, making them the right place for one-time initialization and for registering lifecycle hooks. Each plugin receives the nitroApp context.

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

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:

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:

PropertyTypeDescription
hooksHookableCoreHook system for registering lifecycle callbacks.
h3H3CoreThe underlying H3 application instance.
fetch(req: Request) => Response | Promise<Response>The app's internal fetch handler.
captureError(error: Error, context) => voidProgrammatically capture errors into the error hook pipeline.

Nitro runtime hooks

Use Nitro hooks to run custom functions at specific points in the request lifecycle. Register them inside plugins with nitroApp.hooks.hook():

plugins/close.ts
import { definePlugin } from "nitro";

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

Available hooks

HookSignatureDescription
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[] }) => voidCalled when an error is captured.
close() => voidCalled when the Nitro server is shutting down.
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:

plugins/example.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.

plugins/errors.ts
import { definePlugin } from "nitro";

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook("error", async (error, { event }) => {
    console.error(`${event?.req.url} 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:

plugins/capture.ts
import { definePlugin } from "nitro";

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

Graceful shutdown

The server shuts down gracefully, waiting for any pending background tasks started with event.waitUntil. Use the close hook to clean up your own resources:

plugins/shutdown.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:

plugins/lifecycle.ts
import { definePlugin } from "nitro";

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

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

Modifying response headers

plugins/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");
    }
  });
});