@sujit_rajan/express-route-forge
v1.0.2
Published
Production-grade Express router factory — fluent API, typed middleware, Zod validation, versioning, and route introspection. Used across microservices.
Maintainers
Readme
express-route-forge
Production-grade Express router factory. Fluent API, full TypeScript, Zod validation, API versioning, and live route introspection — used identically across every microservice.
Install
npm install express-route-forge
# Peer deps
npm install express
# Optional — for schema validation
npm install zodQuick start
import { route, createRouter, asyncHandler, validateBody } from 'express-route-forge';
import { z } from 'zod';
import { sendOtp, verifyOtp, getMe } from './controllers/auth.controller';
import { protect } from './middleware/auth.middleware';
const sendOtpSchema = z.object({
phone: z.string().regex(/^\+?[1-9]\d{7,14}$/),
role: z.enum(['customer', 'worker']).default('customer'),
});
export const authRouter = createRouter(
[
route('post', '/auth/send-otp', asyncHandler(sendOtp))
.middleware(validateBody(sendOtpSchema))
.describe('Send a 6-digit OTP to the given phone number')
.tag('public')
.build(),
route('post', '/auth/verify-otp', asyncHandler(verifyOtp))
.describe('Verify OTP and return a signed JWT token')
.tag('public')
.build(),
route('get', '/auth/me', asyncHandler(getMe))
.middleware(protect)
.describe('Return the currently authenticated user')
.tag('protected')
.build(),
],
{ version: 'v1' },
);
// In app.ts
app.use(authRouter);API
route(method, path, handler)
Entry point for the fluent builder.
route('post', '/auth/login', loginHandler)
.middleware(rateLimiter, validateBody(schema)) // runs before handler
.describe('Authenticate and return a JWT') // shown in /routes
.tag('auth', 'public') // for filtering
.version('v2') // override router version
.build() // returns RouteDefinitioncreateRouter(routes, options?)
Registers an array of RouteDefinitions on an Express Router.
const router = createRouter(routes, {
version: 'v1', // prefixes every path: /v1/auth/login
globalMiddlewares: [requestLogger], // applied to every route in this router
});Returns a ForgeRouter (Express Router + _routeMeta for introspection).
asyncHandler(fn)
Wraps an async handler so errors reach your global error middleware instead of crashing Node.
route('get', '/me', asyncHandler(getMe))validateBody(schema) · validateQuery(schema) · validateParams(schema)
Returns a RequestHandler that validates the respective part of the request against any Zod schema (or any object with a safeParse method).
On failure: returns 422 with per-field errors.
On success: replaces the validated portion with coerced data.
const schema = z.object({ phone: z.string(), role: z.enum(['customer','worker']) });
route('post', '/send-otp', sendOtp)
.middleware(validateBody(schema))
.build()routeListHandler(routers, options?)
Returns an Express handler that responds with a JSON list of every registered route.
// Mount on a protected admin endpoint
app.get('/v1/routes', protect, restrictTo('admin'), routeListHandler([authRouter, bookingRouter]));
// Filter by tag
routeListHandler([authRouter], { tag: 'public' })Response:
{
"success": true,
"total": 3,
"routes": [
{
"method": "POST",
"path": "/v1/auth/send-otp",
"description": "Send a 6-digit OTP to the given phone number",
"tags": ["public"],
"middlewares": ["otpLimiter", "validateBody"]
}
]
}extractRoutes(routers)
Returns the same RouteInfo[] without an HTTP response — useful for startup logging or OpenAPI generation.
import { extractRoutes } from 'express-route-forge';
const routes = extractRoutes([authRouter, bookingRouter, workerRouter]);
console.table(routes);Adding a route
// append one block — nothing else changes
route('delete', '/auth/sessions/:id', asyncHandler(revokeSession))
.middleware(protect, validateParams(z.object({ id: z.string().uuid() })))
.describe('Revoke a specific session by ID')
.tag('protected')
.build(),Adding a whole new service router
// booking.routes.ts
import { route, createRouter, asyncHandler, validateBody } from 'express-route-forge';
export const bookingRouter = createRouter([
route('post', '/bookings', asyncHandler(createBooking))
.middleware(protect, validateBody(createBookingSchema))
.describe('Create a new service booking')
.tag('bookings')
.build(),
// ...
], { version: 'v1' });Same package, same pattern, zero duplication across services.
Run tests
npm test
npm test -- --coveragePublish to npm
npm run build # compiles to dist/ (CJS + ESM + .d.ts)
npm publish # runs prepublishOnly: build + test firstFor a scoped private package:
# package.json → "name": "@homeserve/express-route-forge"
npm publish --access restrictedLicense
MIT
