File-Based Routing (app.autoRoute) β
Velociradix includes a built-in File-System Router that turns files and folders inside a routes/ directory into HTTP endpoints automaticallyβeliminating the need to register each route manually in server.ts.
π 1. Default Project Structure β
my-velociradix-app/
βββ package.json
βββ tsconfig.json # (If using TypeScript)
βββ server.ts (or server.mjs) # Entry point (calls app.autoRoute & app.listen)
βββ routes/ # File-system routes folder
βββ index.ts # β GET /
βββ health.ts # β GET /health
βββ about.ts # β GET /about
βββ api/
β βββ version.ts # β GET /api/version
βββ users/
β βββ index.ts # β GET /users, POST /users
β βββ profile.ts # β GET /users/profile
β βββ [id].ts # β GET /users/:id, PATCH /users/:id, DELETE /users/:id
βββ static/
βββ [...slug].ts # β GET /static/* (Wildcard catch-all)πΊοΈ 2. Route Path Mapping Rules β
File Path in routes/ | Generated HTTP Route | Description |
|---|---|---|
routes/index.ts | / | Root endpoint of the application. |
routes/health.ts | /health | Direct literal path matching file name. |
routes/users/index.ts | /users | Directory index maps to directory name. |
routes/users/profile.ts | /users/profile | Nested sub-route. |
routes/users/[id].ts | /users/:id | Dynamic path parameter (ctx.params.id). |
routes/posts/[category]/[slug].ts | /posts/:category/:slug | Multiple dynamic parameters. |
routes/static/[...all].ts | /static/* | Wildcard catch-all route (ctx.params['*']). |
βοΈ 3. How to Write Route Files β
Every file inside routes/ exports named functions matching HTTP methods (GET, POST, PUT, DELETE, PATCH, ALL) or a default handler.
Example A: Main Page (routes/index.ts) β
import type { Context } from 'velociradix';
// GET /
export function GET(ctx: Context) {
return ctx.json({ message: 'Welcome to Velociradix API' });
}Example B: User Resource & Middlewares (routes/users/index.ts) β
You can export route-level middlewares array to protect endpoints defined in this file:
import type { Context } from 'velociradix';
import { rateLimit } from 'velociradix';
// Middlewares applied to all methods in this file
export const middlewares = [rateLimit({ max: 50 })];
// GET /users
export function GET(ctx: Context) {
return ctx.json([
{ id: '1', name: 'Omar' },
{ id: '2', name: 'Sara' }
]);
}
// POST /users
export async function POST(ctx: Context) {
const body = await ctx.body();
return ctx.status(201).json({ created: true, user: body });
}Example C: Dynamic Parameter Route (routes/users/[id].ts) β
Dynamic segments wrapped in [paramName] become available in ctx.params:
import type { Context } from 'velociradix';
import { jwtAuth } from 'velociradix';
// Require JWT authorization for user modifications
export const middlewares = [jwtAuth({ secret: 'app-jwt-secret' })];
// GET /users/:id
export function GET(ctx: Context) {
return ctx.json({ id: ctx.params.id, name: 'Omar Hassan' });
}
// PATCH /users/:id
export async function PATCH(ctx: Context) {
const updates = await ctx.body();
return ctx.json({ id: ctx.params.id, updated: true, updates });
}
// DELETE /users/:id
export function DELETE(ctx: Context) {
return ctx.json({ id: ctx.params.id, deleted: true });
}Example D: Wildcard Route (routes/static/[...slug].ts) β
import type { Context } from 'velociradix';
// GET /static/*
export function GET(ctx: Context) {
const filePath = ctx.params['*'];
return ctx.sendFile(`./public/${filePath}`);
}Example E: Route Metadata & OpenAPI/Swagger (routes/orders.ts) β
Export an options object to generate OpenAPI/Swagger documentation automatically:
import type { Context } from 'velociradix';
export const options = {
name: 'Create Order',
description: 'Places a new customer order and generates invoice',
body: { productId: '123', quantity: 2 }
};
export async function POST(ctx: Context) {
const data = await ctx.body();
return ctx.status(201).json({ orderId: 'ord_99', data });
}β‘ 4. Loading Routes in server.ts β
Synchronous Loading (app.autoRoute): β
import { createApp, logger, helmet, cors } from 'velociradix';
const app = createApp();
// Global Middlewares
app.use(logger());
app.use(helmet());
app.enableCors({ origin: '*' });
// Auto-register routes from ./routes folder
app.autoRoute('./routes');
// Optional: Mount under a base prefix like /api/v1
// app.autoRoute('./routes/api', '/api/v1');
app.listen(3000, () => {
console.log('β‘ Server running at http://localhost:3000');
});Asynchronous Loading with Promise (app.autoRouteAsync): β
If you want to ensure all route modules are fully imported before opening server sockets:
import { createApp } from 'velociradix';
const app = createApp();
async function start() {
await app.autoRouteAsync('./routes');
app.listen(3000, () => {
console.log('β‘ Server ready on http://localhost:3000');
});
}
start();π₯ 5. Development & Hot-Reloading (tsx watch / node --watch) β
Because autoRoute loads route modules dynamically at runtime via directory scanning, watcher tools (tsx watch or node --watch) must be configured to watch the routes/ directory so they detect newly created or modified files.
Recommended Watch Commands: β
1. Using tsx watch (Recommended for TypeScript): β
npx tsx watch --include "routes/**" server.ts2. Using native node --watch (Node.js 20+): β
node --watch --watch-path=routes server.ts3. Recommended package.json Setup: β
{
"name": "my-app",
"type": "module",
"scripts": {
"dev": "tsx watch --include \"routes/**\" server.ts",
"dev:node": "node --watch --watch-path=routes server.ts",
"start": "tsx server.ts"
},
"dependencies": {
"velociradix": "^7.3.0"
},
"devDependencies": {
"tsx": "^4.19.0",
"typescript": "^5.7.0"
}
}TIP
Always export HTTP handlers as named exports (e.g. export function GET, export const POST) for optimal clarity, type completion, and explicit method mapping.