The API package exposes a small Hono app for Node. It gives the backend a stable request surface before feature routes are added:
import { createApp } from "../../apps/api/src/app.js";
const app = createApp({
allowedOrigins: ["http://localhost:3000"],
nodeEnv: "development"
});Tests call app.request(...) directly, so route behavior does not require a live server.
Every API error uses this JSON shape:
{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found."
}
}When a caller can use extra detail safely, the body can include details:
{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid input.",
"details": {
"field": "name"
}
}
}AppError is the application-facing error type. Throw it when code already knows the public error code, HTTP status, message, and optional details:
throw new AppError("FORBIDDEN", 403, "You cannot access this resource.");The global error handler maps AppError to its status and canonical body. Unknown errors return 500 with INTERNAL and never expose the thrown message.
GET / identifies the service:
{
"name": "forgekit-api",
"version": "api:core:db:0.0.0"
}GET /health returns a lightweight liveness payload:
{
"status": "ok",
"timestamp": "2026-07-04T12:00:00.000Z"
}Unknown paths return 404 with NOT_FOUND.
Middleware runs in this order:
- Request id. Reads
x-request-idor creates a new UUID, stores it on the Hono context, and returns it in thex-request-idresponse header. - Logger. Writes a JSON request line in production, a concise readable line in development, and stays silent in tests.
- Security headers. Sets
X-Content-Type-Options: nosniff,X-Frame-Options: DENY, andReferrer-Policy: no-referreron every response. Outside development it also setsStrict-Transport-Securityand a strictContent-Security-Policy. - Cache control. Sets
Cache-Control: no-storebecause API responses should not be cached by default. - CSRF origin check. For
POST,PUT,PATCH, andDELETE, the request must include anOriginheader that matches the configuredallowedOrigins. Missing or untrusted origins return403withCSRF_ORIGIN_MISMATCH. Safe methods pass through.
The Origin check is intentionally simple. It blocks browser state-changing requests that come from untrusted origins while still letting non-state-changing reads through.
apps/api/src/index.ts starts the Node server with @hono/node-server. It reads:
PORT, defaulting to3001WEB_ORIGINS, as a comma-separated list, defaulting tohttp://localhost:3000NODE_ENV, defaulting todevelopment
Boot-time env validation is deferred until core re-exports the config schema and the API has real env to validate. For now the server entry reads PORT directly and keeps the defaults local to the API package.