Harness-engineering nuxt-server-routes

Nuxt Server Routes

install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/nuxt-server-routes" ~/.claude/skills/intense-visions-harness-engineering-nuxt-server-routes-853bf6 && rm -rf "$T"
manifest: agents/skills/codex/nuxt-server-routes/SKILL.md
source content

Nuxt Server Routes

Build fully-typed server API endpoints using Nitro's event-handler model and H3 utilities

When to Use

  • You are creating REST API endpoints inside a Nuxt project under
    server/api/
    or
    server/routes/
  • You need to read request bodies, query parameters, or headers server-side
  • You want to return JSON, redirect, or stream responses from a Nuxt backend
  • You are migrating Express/Fastify route logic into Nuxt's Nitro layer
  • You need to understand how file-based routing maps to HTTP methods

Instructions

  1. Create files under
    server/api/
    to expose routes at
    /api/<name>
    . Files under
    server/routes/
    map to the root path with no
    /api/
    prefix.
  2. Name files using the HTTP method suffix to restrict the method:
    users.get.ts
    ,
    users.post.ts
    . Omit the suffix to handle all methods.
  3. Export a default
    defineEventHandler
    function — this is the required Nitro entry point:
// server/api/users.get.ts
export default defineEventHandler(async (event) => {
  const users = await fetchUsersFromDb();
  return users; // auto-serialized to JSON
});
  1. Read query parameters with
    getQuery
    , request body with
    readBody
    , route params with
    getRouterParam
    :
// server/api/users/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id');
  const query = getQuery(event); // ?include=posts
  const user = await db.user.findUnique({ where: { id } });
  if (!user) throw createError({ statusCode: 404, message: 'User not found' });
  return user;
});
  1. Throw errors with
    createError
    — Nuxt serializes these into structured JSON error responses:
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' });
  1. Handle POST/PUT bodies with
    readBody
    — always validate the shape:
// server/api/posts.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event);
  if (!body.title) throw createError({ statusCode: 400, message: 'title required' });
  return db.post.create({ data: body });
});
  1. Set response headers or status manually when needed:
setResponseStatus(event, 201);
setHeader(event, 'X-Custom-Header', 'value');
  1. Return raw non-JSON responses by setting content-type explicitly:
setHeader(event, 'Content-Type', 'text/plain');
return 'Hello plain text';
  1. Use
    server/utils/
    for shared server-side helpers — these are auto-imported within the
    server/
    tree.

Details

Nuxt's server layer is powered by Nitro, which itself uses H3 as its HTTP framework. H3 is a minimal, composable HTTP toolkit where every request is represented as an

H3Event
. All H3 utility functions accept this event as their first argument.

File-based routing:

server/
  api/
    users.get.ts       → GET  /api/users
    users.post.ts      → POST /api/users
    users/[id].get.ts  → GET  /api/users/:id
    auth/
      login.post.ts    → POST /api/auth/login
  routes/
    feed.xml.get.ts    → GET  /feed.xml

Dynamic catch-all routes:

Use

[...slug].ts
to match multiple path segments:

// server/api/[...slug].ts
export default defineEventHandler((event) => {
  const slug = getRouterParams(event).slug;
  return { path: slug };
});

Middleware within server routes:

You can apply server-side logic before any handler using

server/middleware/
. Files here run on every request automatically (no registration needed):

// server/middleware/auth.ts
export default defineEventHandler((event) => {
  const token = getHeader(event, 'authorization');
  if (!token) throw createError({ statusCode: 401 });
  event.context.user = verifyToken(token);
});

Streaming responses:

Nitro supports streaming via Web Streams API for large payloads or SSE:

export default defineEventHandler((event) => {
  setHeader(event, 'Content-Type', 'text/event-stream');
  return sendStream(event, createReadableStream());
});

Type safety with

$fetch
:

On the client side, use

$fetch
or
useFetch
— Nuxt infers the return type from the server handler automatically in full-stack TypeScript mode.

When NOT to use:

  • Heavy CPU tasks — Nitro runs in a single-threaded worker; offload to a queue or worker thread
  • File system access in edge deployments —
    adapter-cloudflare
    and
    adapter-vercel-edge
    have no Node.js
    fs
    module

Source

https://nuxt.com/docs/guide/directory-structure/server

Process

  1. Read the instructions and examples in this document.
  2. Apply the patterns to your implementation, adapting to your specific context.
  3. Verify your implementation against the details and edge cases listed above.

Harness Integration

  • Type: knowledge — this skill is a reference document, not a procedural workflow.
  • No tools or state — consumed as context by other skills and agents.

Success Criteria

  • The patterns described in this document are applied correctly in the implementation.
  • Edge cases and anti-patterns listed in this document are avoided.