

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1# Node.js + Express + TypeScript — Cursor Rules2# Production-grade Express API patterns with TypeScript34# Project Context5You are building a Node.js REST API with Express and TypeScript. The project follows a layered6architecture (controller -> service -> repository) with strict TypeScript, proper error handling,7and production-ready middleware patterns.89# Project Structure10```11src/12 app.ts # Express app setup, middleware, routes13 server.ts # HTTP server startup, graceful shutdown14 config/15 index.ts # Environment config with validation16 database.ts # Database connection config17 routes/18 index.ts # Route aggregator19 user.routes.ts # Route definitions (thin layer)20 controllers/21 user.controller.ts # Request parsing, response formatting22 services/23 user.service.ts # Business logic24 repositories/25 user.repository.ts # Database queries26 models/27 user.model.ts # Database model / entity28 middleware/29 auth.middleware.ts30 error.middleware.ts31 validate.middleware.ts32 types/33 express.d.ts # Express type augmentations34 common.ts # Shared types35 utils/36 logger.ts # Structured logging (pino/winston)37 errors.ts # Custom error classes38 async-handler.ts # Async wrapper for Express routes39```4041# TypeScript Configuration42- Enable strict mode in tsconfig.json: `"strict": true`.43- Set `"noUncheckedIndexedAccess": true` for safer array/object access.44- Set `"exactOptionalPropertyTypes": true` to distinguish `undefined` from missing.45- Use `"moduleResolution": "node16"` or `"bundler"` (not "node").46- Use path aliases: `"@/*": ["src/*"]` with tsconfig-paths or a bundler.4748# Express Type Patterns49- Type request handlers with explicit generics:50```typescript51 type Handler<P = {}, ResBody = {}, ReqBody = {}, Query = {}> =52 RequestHandler<P, ResBody, ReqBody, Query>;53```54- Augment Express Request for custom properties (auth user, request ID):55```typescript56 declare global {57 namespace Express {58 interface Request {59 user?: AuthenticatedUser;60 requestId: string;61 }62 }63 }64```65- Type route parameters: `req: Request<{ id: string }>`.66- Type request body: `req: Request<{}, {}, CreateUserDto>`.67- Type query params: `req: Request<{}, {}, {}, { page: string; limit: string }>`.6869# Controller Patterns70- Controllers handle HTTP concerns only: parse request, call service, format response.71- NO business logic in controllers. No database calls.72- Use a consistent response format:73```typescript74 interface ApiResponse<T> {75 success: boolean;76 data: T;77 message?: string;78 meta?: { page: number; total: number };79 }80```81- Validate request body/params with zod or joi at the controller or middleware level.82- Use the asyncHandler wrapper to catch rejected promises:83```typescript84 const asyncHandler = (fn: RequestHandler): RequestHandler =>85 (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);86```8788# Service Layer89- Services contain business logic. They call repositories, not the database directly.90- Services receive typed DTOs, not Express Request objects.91- Services throw custom error classes, not HTTP errors.92- Services are classes or modules — injectable and testable.9394# Error Handling95- Define a custom error hierarchy:96```typescript97 class AppError extends Error {98 constructor(99 public statusCode: number,100 public code: string,101 message: string,102 public isOperational = true103 ) { super(message); }104 }105 class NotFoundError extends AppError { /* 404 */ }106 class ValidationError extends AppError { /* 422 */ }107 class UnauthorizedError extends AppError { /* 401 */ }108```109- Create a centralized error middleware (must have 4 parameters):110```typescript111 const errorHandler: ErrorRequestHandler = (err, req, res, next) => { ... }112```113- Register error middleware LAST in the middleware chain.114- Log unexpected errors (non-operational) with full stack traces.115- Never send stack traces to clients in production.116117# Middleware Patterns118- Auth middleware: verify JWT/session, attach user to request, call next().119- Validation middleware: validate request body against schema, return 422 on failure.120- Rate limiting: use express-rate-limit with Redis store for distributed setups.121- Request logging: use morgan or pino-http. Include request ID for tracing.122- CORS: configure explicitly. Never use `cors()` with no options in production.123- Helmet: always use helmet() for security headers.124125# Database Access126- Use an ORM (Prisma, TypeORM, Drizzle) or query builder (Knex) — never raw SQL strings with concatenation.127- Repository pattern: one repository per entity/table.128- Use transactions for multi-step mutations.129- Always parameterize queries to prevent SQL injection.130- Connection pooling: configure pool size based on expected concurrency.131132# Authentication & Security133- Store passwords hashed with bcrypt (min 12 rounds).134- Use JWT with short expiry (15min access + long-lived refresh token).135- Validate and sanitize ALL input — never trust req.body, req.params, or req.query.136- Set secure cookie flags: httpOnly, secure, sameSite.137- Implement CSRF protection for cookie-based auth.138139# Environment & Configuration140- Use dotenv for local development only. Never commit .env files.141- Validate all environment variables at startup with zod:142```typescript143 const envSchema = z.object({144 NODE_ENV: z.enum(['development', 'production', 'test']),145 PORT: z.coerce.number().default(3000),146 DATABASE_URL: z.string().url(),147 JWT_SECRET: z.string().min(32),148 });149 export const env = envSchema.parse(process.env);150```151- Fail fast: if required env vars are missing, crash on startup with a clear message.152153# Logging154- Use a structured logger (pino recommended for performance).155- Log levels: error, warn, info, debug. Set via environment variable.156- Include context in every log: requestId, userId, operation.157- DON'T: Use console.log in production code. Use the logger.158- DON'T: Log sensitive data (passwords, tokens, PII).159160# Testing161- Unit test services and utilities with Jest or Vitest.162- Integration test API endpoints with supertest.163- Mock the service layer when testing controllers.164- Mock the repository layer when testing services.165- Use factories for test data creation.166- Test error paths: invalid input, unauthorized, not found, server errors.167168# Graceful Shutdown169- Handle SIGTERM and SIGINT signals.170- Stop accepting new connections, finish in-flight requests.171- Close database connections and other resources.172- Exit with code 0 on clean shutdown.173174# Common Mistakes to Avoid175- DON'T: Forget to call next() in middleware — the request will hang.176- DON'T: Use express.json() without a body size limit: `express.json({ limit: '10kb' })`.177- DON'T: Catch errors and swallow them silently.178- DON'T: Use synchronous file operations (fs.readFileSync) in request handlers.179- DON'T: Store sessions in memory — use Redis or database-backed sessions.180- DON'T: Return different response shapes from different endpoints.181
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| survivorforge/cursor-rulesrules/ai-ml-python/.cursorrules · 16 | .cursorrules | teststylearchdeployment+2 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/langchain-ai/.cursorrules · 16 | .cursorrules | testlint-formatstylearch+4 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/aws-serverless/.cursorrules · 16 | .cursorrules | teststylearchtypes+6 | 73/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/tailwindcss/.cursorrules · 16 | .cursorrules | lint-formatstylearchui+3 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/mern-stack/.cursorrules · 16 | .cursorrules | setupteststylearch+6 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/api-design-rest/.cursorrules · 16 | .cursorrules | lint-formatstylesecurityapi+3 | 69/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/api-microservices/.cursorrules · 16 | .cursorrules | buildteststylearch+5 | 92/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/chrome-extension/.cursorrules · 16 | .cursorrules | teststylearchtesting-strategy+4 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/clean-code/.cursorrules · 16 | .cursorrules | styledo-notagent-behaviourdocs | 57/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/database-sql/.cursorrules · 16 | .cursorrules | styletypessecuritydatabase+3 | 65/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/devops-docker/.cursorrules · 16 | .cursorrules | setupbuildteststyle+4 | 93/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/devops-infrastructure/.cursorrules · 16 | .cursorrules | buildteststylesecurity+3 | 93/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/django-rest/.cursorrules · 16 | .cursorrules | buildteststylearch+5 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/docker-devops/.cursorrules · 16 | .cursorrules | setupteststylearch+6 | 85/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/flutter-dart/.cursorrules · 16 | .cursorrules | teststylearchtypes+5 | 89/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/fullstack-nextjs-prisma/.cursorrules · 16 | .cursorrules | teststylearchtypes+7 | 96/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/go-gin/.cursorrules · 16 | .cursorrules | testlint-formatstylearch+5 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/mobile-react-native/.cursorrules · 16 | .cursorrules | teststylearchtypes+7 | 89/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs-14-app-router/.cursorrules · 16 | .cursorrules | teststyletypestesting-strategy+3 | 71/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs-app-router/.cursorrules · 16 | .cursorrules | teststylearchtypes+5 | 81/100 | 13 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/survivorforge-cursor-rules-rules-nodejs-express-typescript-cursorrules)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.