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.

Note

H3 composes the middleware chain once, on the first request. Middleware a plugin adds to nitroApp.h3["~middleware"] during startup is included, but it runs before Nitro's route rules when placed at the front of the array. If a plugin changes the array after the first request was handled, it must also reset nitroApp.h3["~dispatch"] and nitroApp.h3["~composed"] to undefined so the chain is recomposed.

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

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:

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

Nitro  builds full-stack servers that deploy anywhere.