- All route handlers must live in
src/routes/with one file per resource:src/routes/users.ts,src/routes/products.ts - All business logic must live in
src/services/with one file per domain:src/services/UserService.ts,src/services/ProductService.ts - All database queries must live in
src/repositories/with one file per entity:src/repositories/UserRepository.ts - All middleware must live in
src/middleware/with descriptive names:src/middleware/authMiddleware.ts,src/middleware/errorHandler.ts - All type definitions must live in
src/types/with one file per domain:src/types/user.ts,src/types/product.ts - All validation schemas must live in
src/validation/with one file per resource:src/validation/userSchema.ts - Never put more than one exported function per file in
src/middleware/ - Never put route definitions in
src/index.ts— import and mount them only - Test files must live in
tests/mirroring source structure:tests/services/UserService.test.ts
- Route files use lowercase plural:
users.ts,products.ts, neverUser.tsoruser-routes.ts - Service files use PascalCase with Service suffix:
UserService.ts,ProductService.ts, neveruserService.ts - Repository files use PascalCase with Repository suffix:
UserRepository.ts, neveruser-repo.ts - Middleware files use camelCase with Middleware suffix:
authMiddleware.ts,errorHandler.ts - Type files use lowercase:
user.ts,product.ts, neverUser.tsortypes.ts - Express Router instances must be named
router:const router = express.Router() - Never use
var— useconstfor all declarations,letonly when reassignment is required - Function names in services must be verb-first:
getUserById(),createUser(),updateUserEmail(), nevergetUser()oruser()
- Every Express route handler signature must be:
(req: Request, res: Response, next: NextFunction) => Promise<void> req.bodymust be validated with Zod before any use:const data = userSchema.parse(req.body)req.paramsmust be validated with Zod before any use:const { id } = paramsSchema.parse(req.params)req.querymust be validated with Zod before any use:const { page } = querySchema.parse(req.query)- Never access
req.body,req.params, orreq.querywithout prior Zod validation in the same function - Never use
anytype — useunknownand type guard it, or use a specific type - Never use
astype assertions outside of test files — use Zod.parse()or type guards instead - All function parameters must have explicit types, never rely on inference
- All function return types must be explicit, never use implicit
anyreturns
- Create
src/types/errors.tswith anAppErrorclass extending Error withstatusCode: numberandcode: stringproperties - All thrown errors must be instances of
AppError, neverErrorornew Error() - Every async route handler must have try/catch:
try { ... } catch (err) { next(err) } - Catch blocks must type-guard errors:
catch (err) { if (err instanceof AppError) { ... } else { next(err) } } - Error middleware must be the last middleware registered in
src/index.ts - Error middleware must never send raw
err.messageto client — senderr.codeanderr.statusCodeonly - Never log full error objects to client responses — log to server console only
- All database errors must be caught and converted to
AppErrorwith appropriatestatusCodeandcode - 404 errors must use
statusCode: 404andcode: 'NOT_FOUND' - Validation errors must use
statusCode: 400andcode: 'VALIDATION_ERROR' - Authentication errors must use
statusCode: 401andcode: 'UNAUTHORIZED' - Authorization errors must use
statusCode: 403andcode: 'FORBIDDEN'
- Never hardcode secrets in any file — all secrets must come from environment variables via
process.env - Create
src/config/env.tsthat validates all required env vars at startup using Zod - Application must fail to start if any required env var is missing
- Never log
process.envor any secret values to console or error responses - All user input from
req.body,req.params,req.querymust be validated before database queries - SQL queries must use parameterized queries only — never string concatenation
- Never return database error messages to clients — wrap in
AppErrorwith generic message - Authentication tokens must be validated on every protected route using middleware
- Protected routes must check
req.userexists before proceeding — never assume middleware ran
src/index.tsmust only contain: app creation, middleware registration, route mounting, error handler, and server start- Middleware must be registered in this order: body parser, auth, routes, 404 handler, error handler
- Use
express.json()with explicit size limit:express.json({ limit: '10kb' }) - Use
express.urlencoded()with explicit size limit:express.urlencoded({ limit: '10kb', extended: true }) - All routes must be mounted with explicit path prefix:
app.use('/api/users', userRoutes) - Never use
app.get(),app.post()directly insrc/index.ts— define in route files only - 404 handler must be registered before error handler:
app.use((req, res) => res.status(404).json({ code: 'NOT_FOUND' }))
- Every Service class must have a constructor that accepts its Repository as a dependency
- Services must never import database drivers directly — only use Repository methods
- Repositories must be the only files that import database drivers or query builders
- Service methods must return typed objects, never raw database rows
- Service methods must catch database errors and throw
AppErrorinstances - Repositories must return
nullfor not-found queries, never throw - Services must check for
nullreturns and throwAppErrorwithstatusCode: 404
- Test files must use
.test.tsextension:UserService.test.ts, never.spec.ts - Every Service class must have a corresponding test file with 100% function coverage
- Every route handler must have a corresponding integration test
- Mock all Repository dependencies in Service tests using Jest mocks
- Mock all Service dependencies in route handler tests
- Never test database directly in unit tests — use Repository mocks
- Test error cases explicitly: test that
AppErroris thrown with correctstatusCodeandcode
- Use named exports only:
export const getUserById = () => {}, neverexport default - Import statements must be grouped: Node modules, then local modules, then types
- Type imports must use
import type:import type { User } from '../types/user' - Never use wildcard imports:
import * as users from './users'— import specific exports only - All exports from route files must be the Router instance:
export const router - All exports from Service files must be the class:
export class UserService - All exports from Repository files must be the class:
export class UserRepository
Source: Codelibrium — the marketplace for AI behaviour files. Browse multiple rulesets at codelibrium.com or install via CLI:
npx codelibrium-cli install <ruleset-name>