Node / Express Guide
Routes
Route file conventions, router factory pattern, and REST method conventions.
Routes wire HTTP paths to controller functions and compose middleware. Each resource has its own route file.
TL;DR — Each route file exports a factory function returning an Express
Router. The route index exports useRoutes() mounted as app.use('/api', router()). accessTokenSecret is read from process.env in route files. Auth and role guards are built per-router, not shared globally.Pattern
Route files export a factory function that returns an Express Router:
// src/routes/auth.route.ts
import express from "express";
import { authenticate } from "@codisolutions23/node-utils";
import { useRevokedTokenRepo } from "../repositories/revoked-token.repo";
import { useAuthController } from "../controllers/auth.controller";
import { useUserController } from "../controllers/user.controller";
import { requireBranchManagerOrAbove } from "../middleware/role-guard.middleware";
import resolveOptionalTenant, {
resolveRequiredTenant,
} from "../middleware/tenant.middleware";
const router = express.Router();
export default function useAuthRoute() {
const { existsByJti } = useRevokedTokenRepo();
const {
login,
refreshToken,
logout,
getCurrentUser,
getCurrentUserOrganization,
} = useAuthController();
const { createCustomer } = useUserController();
const accessTokenSecret = process.env.ACCESS_TOKEN_SECRET || "";
const authMiddleware = authenticate(accessTokenSecret, existsByJti);
const branchManagerMiddleware = requireBranchManagerOrAbove(
accessTokenSecret,
existsByJti,
);
// Public routes
router.post("/login", resolveOptionalTenant, login);
router.post("/refresh", refreshToken);
// Registration routes
router.post("/register/customer", resolveRequiredTenant, createCustomer);
// Protected routes
router.get("/user", authMiddleware, getCurrentUser);
router.get(
"/organization",
branchManagerMiddleware,
getCurrentUserOrganization,
);
router.delete("/logout", authMiddleware, logout);
return router;
}
The
router is declared at module scope (not inside the factory). accessTokenSecret is read from process.env directly in route files — this is the current pattern even though src/config.ts exports it as ACCESS_TOKEN_SECRET.Route Index
All routes are mounted in src/routes/index.ts — the index exports a factory function useRoutes():
// src/routes/index.ts
import express from "express";
import useAuthRoute from "./auth.route";
import useBookingRoute from "./booking.route";
import useOrganizationRoute from "./organization.route";
import useUserRoute from "./user.route";
// ... more route imports
const router = express.Router();
router.get("/v1", (_, res) => {
res.json({ message: "Welcome to Booking API." });
});
export default function useRoutes() {
router.use("/auth", useAuthRoute());
router.use("/bookings", useBookingRoute());
router.use("/organizations", useOrganizationRoute());
router.use("/users", useUserRoute());
// ...
return router;
}
Mounted in src/app.ts as a function call:
app.use("/api", router()); // router is useRoutes — called with ()
Middleware Composition
Middleware is composed per route — not globally applied unless it truly applies to all routes:
// src/routes/resource.route.ts
export default function useResourceRoute() {
const { existsByJti } = useRevokedTokenRepo();
const { getAll, create, update, remove } = useResourceController();
const accessTokenSecret = process.env.ACCESS_TOKEN_SECRET || "";
const authMiddleware = authenticate(accessTokenSecret, existsByJti);
const branchManagerMiddleware = requireBranchManagerOrAbove(
accessTokenSecret,
existsByJti,
);
const adminMiddleware = requireAdmin(accessTokenSecret, existsByJti);
router.get("/", authMiddleware, getAll);
router.post("/", branchManagerMiddleware, create);
router.patch("/:id", branchManagerMiddleware, update);
router.delete("/:id", adminMiddleware, remove);
return router;
}
REST Conventions
| Operation | Method | Path |
|---|---|---|
| List | GET | /resources |
| Get one | GET | /resources/:id |
| Create | POST | /resources |
| Full replace | PUT | /resources/:id |
| Partial update | PATCH | /resources/:id |
| Soft delete | DELETE | /resources/:id |
| Sub-resource action | PATCH | /resources/:id/action |
| Nested resource | POST | /resources/:id/related |
