API routes

File-based routing

Files under server/routes/ become routes; server/routes/api/* is the API. The scaffolded examples are meant to be read, curled, and then deleted:

curl localhost:3000/api/posts/hello-zenness  # 200
curl localhost:3000/api/posts/Hello%20There  # 400, with a readable message

server/routes/api/posts/[slug].ts is the shortest example of a dynamic route: the matched segment arrives on event.context.params, still percent-encoded, which is why the handler decodes before it validates — using the magic-regexp slug pattern from server/utils/slug.ts, so nothing downstream ever sees a malformed segment.

useEvent()

server/utils/event.ts exports zenness’s useEvent(), a thin alias for Nitro’s useRequest(). It lets a helper reach the current request without having it threaded down from the handler:

export function requestInfo() {
  const request = useEvent() // no argument, no plumbing
  return { path: new URL(request.url).pathname }
}

This depends on experimental.asyncContext: true in nitro.config.ts. Turn it off and the call throws Nitro request context is not available.

It returns the current request — a web Request, so url is a string; use new URL(request.url) to read the path. Handlers that want the h3 event should use the argument defineHandler already gives them.

Auto-imports

imports.dirs in nitro.config.ts publishes everything under server/utils to the virtual #imports module:

import { requestInfo } from '#imports'

In this Nitro beta imports builds that barrel rather than injecting true globals, so an import statement is still required. The scaffolded code uses the @ alias (@/utils/event) instead, which resolves identically and typechecks without extra declarations.

Logging and errors

server/utils/logger.ts is one tagged, level-aware Consola instance; everything else logs through it. server/plugins/errors.ts covers the request lifecycle with it: request lines at debug, deliberate 4xx at warn without a stack trace, unhandled errors with one. The debug line in server/routes/api/hello.ts is meant to stay there — it is silent until you ask for it:

NITRO_LOG_LEVEL=4 npm run dev  # 3 is the default, 4 adds debug, 5 adds trace

Outbound calls

server/utils/http.ts is a preconfigured ofetch client: JSON in and out, a thrown FetchError (with the upstream’s parsed body on .data), retries with a timeout, and interceptors as the place for auth headers or tracing. server/routes/api/upstream.ts uses it, and translates the failure — an upstream 503 becomes a 502 from this service, because the caller asked this service for something. Point it somewhere real with NITRO_UPSTREAM_URL.