All skills
antfu avatar

/nitro

@d02c484 official
by Anthony Fuantfu/skills5.9k stars
335

Nitro is the framework-agnostic server toolkit (powering Nuxt) for building and deploying web servers anywhere. Use when working with nitro.config, server routes/event handlers, route rules, caching, storage, tasks, websockets, or deploying to Node/Bun/Deno/Cloudflare/Vercel.

Use this Skill: https://skilld.dev/gh/antfu/skills/nitro

This session only. Nothing lands on disk.

referencescore-rendering.md

≈1.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Renderer & Server Entry

Two special handlers control requests that aren't matched by routes/: the server entry (runs first, for cross-cutting concerns) and the renderer (catch-all /**, lowest priority).

Matching order: request hook → route rules → middleware → specific routes → server entry → renderer.

Server entry

Auto-detected from server.ts (or .js/.mjs/.mts/.tsx/.jsx) in serverDir (if set) or the project root. It is registered as a catch-all (/**) that runs only for requests no route matched, right before the renderer — it is a fallback, not global middleware (use middleware for cross-cutting concerns). Its top-level code runs once at startup (before plugins). Return a Response to terminate, return nothing to continue.

export default {
  async fetch(req: Request) {
    const url = new URL(req.url);
    if (url.pathname === "/health") {
      return new Response("OK", { status: 200 });
    }
    // return nothing -> continue to routes/renderer
  },
};

Or use defineHandler for the H3 event + context:

import { defineHandler } from "nitro";

export default defineHandler((event) => {
  event.context.requestId = crypto.randomUUID();
  // no return -> continue
});

Configure explicitly with serverEntry (a string, or { handler, format }, or false to disable):

export default defineConfig({
  serverEntry: { handler: "./server.ts", format: "web" }, // "web" (default) or "node"
});

Typed server options

When the default export is a plain object, use defineServerEntry for typed srvx server options (port, hostname, tls, maxRequestBodySize, ...), applied by the Node/Bun/Deno presets:

import { defineServerEntry } from "nitro";

export default defineServerEntry({
  fetch: (req) => new Response("Hello"),
  port: 8080,
  maxRequestBodySize: 1024 * 1024,
});

NITRO_PORT/PORT, NITRO_HOST/HOST, NITRO_SSL_CERT/NITRO_SSL_KEY override the matching options. Options are read only from plain-object exports (not framework instances or server.node.ts).

Framework integration

Any framework exposing a web fetch(request): Response works as a server entry:

import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => c.text("Hello from Hono!"));
export default app;

For Node-style (req, res) frameworks (Express, Fastify), name the file server.node.ts (or set format: "node") — Nitro converts it via srvx:

import Express from "express";
const app = Express();
app.use("/", (_req, res) => res.send("Hello from Express!"));
export default app;

Renderer

A catch-all that serves HTML/SSR for unmatched routes. Configured via renderer, or auto-detected from index.html.

export default defineConfig({
  renderer: {
    template: "./index.html", // HTML template
    handler: "./renderer.ts", // OR a custom handler (template ignored if set)
    static: false,            // serve template as-is, skip processing
  },
});

Set renderer: false to disable entirely.

Auto-detected index.html (SPA)

If an index.html exists, Nitro serves it for all unmatched routes — the default SPA behavior with Vite. With a Vite ssr environment, add <!--ssr-outlet--> and Nitro injects SSR output.

Custom renderer handler

export default function renderer({ req }: { req: Request }) {
  const url = new URL(req.url);
  return new Response(
    `<!DOCTYPE html><html><body><h1>${url.pathname}</h1></body></html>`,
    { headers: { "content-type": "text/html; charset=utf-8" } },
  );
}

Rendu templates (experimental)

HTML templates support the rendu preprocessor:

<h1>Hello {{ $URL.pathname }}</h1>
<? if ($METHOD === "POST") { ?><p>Submitted!</p><? } ?>
<script server>
  const data = await fetch("https://api.example.com/data").then((r) => r.json());
</script>
<pre>{{ JSON.stringify(data) }}</pre>
  • {{ expr }} HTML-escaped, {{{ expr }}} / <?= expr ?> raw, <? ... ?> control flow.
  • Globals: $REQUEST, $METHOD, $URL, $HEADERS, $RESPONSE, $COOKIES.
  • Functions: setCookie, redirect, echo (streaming), htmlspecialchars.

Key Points

  • Server entry = global pre-routing handler (auth, logging); renderer = catch-all HTML/SSR.
  • Server entry returns Response to stop, or nothing to continue; keep it lightweight (runs every request).
  • Web-fetch frameworks plug into server.ts; Node (req,res) ones use server.node.ts.
  • A defined [...].ts catch-all route conflicts with the renderer (Nitro warns).
<!-- Source references: - https://nitro.build/docs/server-entry - https://nitro.build/docs/renderer - https://nitro.build/docs/lifecycle -->

Source: SKILL.md on GitHub

No alerts3d3 checks · Risk SAFE
  • Gen Agent Trust Hub3d

    The skill provides comprehensive documentation and guidelines for Nitro v3, a server toolkit for building web applications. It includes security best practices like using parameterized queries to prevent SQL injection and authenticating task execution. No security issues were detected.

  • Socket3d

    No alerts

  • Snyk3d

    Risk: LOW · No issues

Signed by skilld at d02c484. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 4 days ago
Other metadata
metadata
{
  "author": "Anthony Fu",
  "version": "2026.9.25",
  "source": "Generated from https://github.com/nitrojs/nitro, scripts located at https://github.com/antfu/skills"
}

README badge

README badge for antfu/skills/nitro