Best practices for building, testing, and hardening this DaloyJS REST API on Vercel (Node.js runtime). Use when adding or changing HTTP routes, Zod schemas, middleware, or error handling; regenerating the OpenAPI spec or the typed Hey API client; keeping the single Vercel Functions entrypoint, its root-routing rewrite, and the Web-Standard handler; or working on auth, rate limits, secrets, and the project's quality gates.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Best practices for building, testing, and hardening this DaloyJS REST API on Vercel (Node.js runtime). Use when adding or changing HTTP routes, Zod schemas, middleware, or error handling; regenerating the OpenAPI spec or the typed Hey API client; keeping the single Vercel Functions entrypoint, its root-routing rewrite, and the Web-Standard handler; or working on auth, rate limits, secrets, and the project's quality gates.
license
MIT
SKILL.md — DaloyJS best practices (Vercel, Node.js runtime)
Operational guidance and best practices for AI coding agents working in this
DaloyJS Vercel project on the Node.js runtime (Fluid Compute). This is
the project's single source of truth for how to add routes, write tests,
ship secure defaults, and run the quality gates. Read this in full before
making non-trivial changes.
When to use this skill
Use this skill when you need to:
Add, modify, or remove HTTP routes in this project.
Adjust middleware, validation, or error handling.
Run tests or typecheck the project.
Deploy or troubleshoot the Vercel Functions entrypoint.
Harden the API (auth, CORS, rate limits, secrets, dependency hygiene).
Do not use this skill for tasks unrelated to the API itself.
Core principles
DaloyJS is a contract-first framework. On Vercel, additionally:
Node.js runtime by default. The full Node API is available
(node:*, Buffer, fs), but prefer Web Standards (Request /
, , Web Crypto) so the same app can also run on the
Edge runtime or another adapter unchanged. Opt into Edge only when you
need it ( + ), and
then drop modules.
Response
fetch
export const runtime = "edge"
toWebHandler(app)
node:
The route definition is the contract. Method, path, request
schemas, and response schemas live in one place (app.route({...})).
Zod schemas validate at every boundary.
Preserve literal types. Return status: 200 as const.
Secure by default.requestId(), secureHeaders(), and
rateLimit() are registered before route definitions. Note the
in-memory rate limiter resets per instance — for high-traffic
deployments, back it with an external shared store (e.g. Upstash
Redis).
One entrypoint + root-routing rewrite.api/index.ts owns all
routing so DaloyJS can generate a unified OpenAPI spec. Vercel maps it
to /api, so vercel.json keeps a /(.*) → /api rewrite so every
path reaches the function at the site root (without it the root domain
returns a Vercel 404).
Project shape
api/index.ts — the Vercel Functions entrypoint. Builds the App,
registers routes/middleware, and exports default toFetchHandler(app)
(Node.js Functions expect a default export with a fetch method; Node.js
is the default runtime, so no runtime export is needed).
vercel.json — Vercel build/runtime configuration, including the
/(.*) → /api rewrite that routes every path to the function.
tests/ — test files (*.test.ts).
Commands cheat-sheet
npm run dev # local Vercel dev server on http://localhost:3000
npm run typecheck # tsc --noEmit
npm test# run test suite
npm run deploy # deploy to Vercel
npm audit # supply-chain audit
Always run npm run typecheck and npm test before declaring a task done.
OpenAPI & docs routes
When docs: true is set on new App({...}) (the default in this template),
three routes are auto-mounted off the spec generated from your route
definitions:
GET /openapi.json — OpenAPI 3.1 spec as JSON.
GET /openapi.yaml — OpenAPI 3.1 spec as YAML (served inline as
text/yaml; charset=utf-8, since @daloyjs/core 0.13.1).
GET /docs — Scalar API reference UI that loads the spec.
Customize via docs: { openapiPath, openapiYamlPath, path, ui }. Set
openapiYamlPath: false to disable just the YAML route, docs: "auto" to
mount only outside production, or docs: false to disable all three.
On Vercel the YAML serializer is pure-string (no extra deps) and adds
<1KB to the bundle. For hand-rolled mounting, openapiToYAML is exported
from @daloyjs/core/openapi.
Workflow: add a new route
Open api/index.ts.
Design schemas first. Use z.object({...}).strict() for inputs.
Call app.route({...}) with method, path, operationId,
tags, responses, handler (plus request when accepting input).
Return { status, body, headers? } with status: 200 as const.
Throw typed errors (NotFoundError, BadRequestError, etc.)
from @daloyjs/core.
Add a test under tests/ using in-process app.request(...).
Run the quality gates: npm run typecheck && npm test.
Throw typed errors from @daloyjs/core — they serialize to RFC 7807
problem responses.
Add a responses[code] entry for every error you throw.
Middleware
Register middleware before route definitions. Order matters.
Keep the secure baseline (requestId, secureHeaders, rateLimit).
Add CORS only when needed, with an explicit origin allowlist.
Testing best practices
Tests use in-process app.request(...) — no port, no Vercel runtime
needed for unit tests.
import { test } from"node:test";
import assert from"node:assert/strict";
import handler from"../api/index.ts";
// Either import the underlying app, or test via the handler's fetch// method (the default export is the Vercel `{ fetch }` object) by// passing a Web Request.test("GET /healthz returns ok", async () => {
const res = await handler.fetch(newRequest("http://local/healthz"));
assert.equal(res.status, 200);
});
Cover happy paths and unhappy paths for every route: valid input,
validation failures (400), auth failures (401/403), not-found (404),
conflict (409), rate limiting (429). For external services, inject an
in-memory fake during tests.
Aim for 100% line and function coverage on the routes you add.
Security best practices
Keep secureHeaders(), requestId(), and rateLimit() enabled. For
production traffic, back rate-limiting with Vercel KV or another
shared store so limits apply across instances.
Keep behindProxy set on new App({...}). A Vercel function always runs
behind Vercel's edge proxy (one hop) which sends x-forwarded-for; in
production the boot guard returns a 500 on every request until the
posture is declared. { hops: 1 } is correct for Vercel (override with
TRUST_PROXY_HOPS; e.g. Cloudflare → Vercel = 2). Satisfy the guard,
do not delete it.
Never log secrets — filter authorization, cookie, etc.
Read secrets from process.env (available on Node.js Functions).
Validate via Zod at module load.
For auth, verify JWT signatures with the Web Crypto API
(crypto.subtle, available on both Node.js and Edge). Never trust the
alg header from the token.
Validate redirects against an allowlist.
Set bodyLimitBytes and requestTimeoutMs on new App({...}) to
mitigate DoS.
Serverless functions still have bundle-size and cold-start costs; be
cautious about adding heavy dependencies. Inspect bundle size during
deploy.
Pin Vercel project settings (regions, memory, maxDuration) explicitly
in vercel.json rather than relying on dashboard defaults.
Logging & observability
Use ctx.log — it carries the request id.
console.log shows up in Vercel's runtime logs; the framework logger
emits structured JSON for log aggregators.
Configuration & secrets
Use Vercel project env vars; mirror required names in .env.example.
Validate process.env via a Zod schema at module load.
Pitfalls and guardrails
api/index.ts is the single Vercel Functions entrypoint and the
/(.*) → /api rewrite in vercel.json must stay so DaloyJS
handles routing at the site root (the root domain 404s without it). Do
not split routes into multiple Vercel API files
unless the user explicitly asks (it disables shared middleware and a
unified OpenAPI).
Use toFetchHandler(app) from @daloyjs/core/vercel for Node.js
Functions — never hand-roll a fetch(req) adapter. If you opt into the
Edge runtime, use toWebHandler(app) with export const runtime = "edge".
Do not import @daloyjs/core/node, @daloyjs/core/bun, etc. — only
@daloyjs/core and @daloyjs/core/vercel.
Node APIs (Buffer, fs, full process) are available on the Node.js
runtime, but keep handlers Web-Standard where practical so the app can
also run on the Edge runtime unchanged.
Do not weaken response literal types (as const).
Do not return errors as { status: 4xx, body }. Throw a typed error.
Add runtime dependencies with npm install <package> and rerun the quality gates after dependency changes.
Process expectations
Every new feature ships with happy-path and unhappy-path tests.
Bug fixes include a regression test.
npm run typecheck and npm test must pass before completion.
For deploys, ensure the user is logged in via vercel login; do not
authenticate on their behalf.
Keep README.md, this SKILL.md, and AGENTS.md consistent.