Reusable, framework-agnostic building blocks for Node.js APIs — errors, auth, rate limiting, validation, config, logging, and OpenAPI docs, with Express and Hono adapters.
- Express
- Hono
- Auth
- OpenAPI
import { loadEnv } from '@rtorcato/api-config'
import { NotFoundError } from '@rtorcato/api-errors'
import { errorHandler } from '@rtorcato/api-errors-express'
import { ok } from '@rtorcato/api-response'
import { validate } from '@rtorcato/api-validation'
import { z } from 'zod'
const env = loadEnv(z.object({ PORT: z.coerce.number().default(3000) }))
app.post('/users', (req, res) => {
const body = validate(z.object({ name: z.string() }), req.body)
res.status(201).json(ok(body))
})
app.get('/users/:id', (req, res) => {
throw new NotFoundError('User not found')
})
app.use(errorHandler())
import { NotFoundError } from '@rtorcato/api-errors'
import { errorHandler } from '@rtorcato/api-errors-hono'
import { ok } from '@rtorcato/api-response'
import { validate } from '@rtorcato/api-validation'
import { Hono } from 'hono'
import { z } from 'zod'
const app = new Hono()
app.post('/users', async (c) => {
const body = validate(z.object({ name: z.string() }), await c.req.json())
return c.json(ok(body), 201)
})
app.get('/users/:id', () => {
throw new NotFoundError('User not found')
})
app.onError(errorHandler())
import { signToken, verifyToken } from '@rtorcato/api-auth'
import { requireAuth } from '@rtorcato/api-auth-express'
const token = signToken({ sub: user.id }, env.JWT_SECRET, {
expiresIn: '1h',
})
// Reject anything without a valid bearer token, then read the claims.
app.get('/me', requireAuth(env.JWT_SECRET), (req, res) => {
res.json(ok({ userId: req.auth.sub }))
})
const claims = verifyToken(token, env.JWT_SECRET)
import { buildOpenApiDocument } from '@rtorcato/api-openapi'
import { serveApiDocs } from '@rtorcato/api-openapi-express'
import { z } from 'zod'
const User = z.object({ id: z.uuid(), name: z.string(), email: z.email() })
// The same Zod schemas you validate with generate the spec — no drift.
const spec = buildOpenApiDocument({
info: { title: 'Users API', version: '1.0.0' },
routes: [
{
method: 'post',
path: '/users',
request: { body: User.pick({ name: true, email: true }) },
responses: { 201: { description: 'Created', schema: User } },
},
],
})
app.use('/docs', serveApiDocs(spec)) // → renders the Scalar UI below ↓
npm install @rtorcato/api-errorsDocs that can't drift
Build an OpenAPI 3.1 document from the same Zod schemas you validate requests with, then serve a Scalar or Swagger UI on Express or Hono. Change a schema and the reference changes with it — no hand-written spec to fall out of sync.
- Schema-first — one source for validation and docs
- Scalar & Swagger UI, Express & Hono adapters
ts-restcontracts render docs out of the box
One toolkit, one contract
Install only the pieces you need — each package is independent, with framework adapters as peer deps.
Framework-agnostic HTTP error classes: HttpError plus 400/401/403/404/409/500 subclasses, each carrying status + code.
JWT utilities — sign, verify, and extract bearer tokens — with an Express adapter for auth and optional-auth middleware.
In-memory sliding-window rate limiter with matching Express and Hono middleware adapters.
HTML generators for Swagger UI and Scalar API Reference, plus Express and Hono adapters to serve them from an OpenAPI spec.
Zod request validation that throws a BadRequestError on failure — wires straight into the error handler.
Consistent success-response envelope for JSON APIs — the success-path counterpart to api-errors.
Load and validate environment variables with dotenv + zod at startup, failing fast on bad config.
Pino logger factory — pretty output in development, structured JSON in production.
Opinionated CORS middleware for Express with sane defaults and a small config surface.
Small Express helpers — client IP extraction and route listing for boot-time logging.
Helpers for testing Express and Hono routes against the shared error and response contract.
Sibling projects
More from @rtorcato — same conventions, same release pipeline.
Tree-shakeable TypeScript wrappers around 40+ browser Web APIs — one subpath per spec.
Tree-shakeable TypeScript utilities — tiny bundles, full type safety, CLI included.
Shared Biome, TypeScript and Vitest presets that power the @rtorcato/* family.