You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Express application currently lacks essential middleware layers for production readiness. This issue covers the implementation of four critical middleware components: request logging, centralized error handling, rate limiting, and request validation. These middleware layers are fundamental to operating a secure, observable, and resilient API in production environments.
Request Logging Middleware: Every incoming HTTP request should be logged with a unique correlation ID, method, URL, response status code, response time (in milliseconds), and user agent. The logging should be structured (JSON format) and use the Pino logger (if available) or a similar high-performance logger. Sensitive headers like Authorization must be redacted.
Centralized Error Handling Middleware: A global error handler should catch all uncaught errors thrown in route handlers and middleware, returning consistent JSON error responses with appropriate HTTP status codes. Error types should be extended with a custom AppError class that includes a user-friendly message, an internal error code, and an HTTP status code. Development environments should include stack traces; production environments should suppress them.
Rate Limiting Middleware: Basic IP-based rate limiting should be applied globally with configurable window size and max request counts. The middleware must return a 429 Too Many Requests response with Retry-After header when the limit is exceeded. Future issues (Issue #29) will extend this to support tier-based limits.
Request Validation Middleware: A utility middleware pattern for validating request bodies, query parameters, and URL parameters against expected schemas (preferably using Zod, which will be fully adopted in Issue #8). Invalid requests should return a 400 status with detailed error messages listing which fields failed validation and why.
Architecture: Middleware files under src/middleware/: logger.js, errorHandler.js, rateLimiter.js, validate.js. The error handling middleware must be registered last in the middleware chain.
Impact: These are cross-cutting concerns that affect every request. They improve debuggability, user experience during errors, API protection from abuse, and data integrity. This issue must be completed before exposing the API to any real users or external services.
Create Logger Middleware: Write src/middleware/logger.js that initializes a Pino logger, generates a correlation ID via uuid.v4() for each request, attaches it to req.correlationId, and logs request start and completion with timing.
Create Error Handler: Write src/middleware/errorHandler.js with class AppError extends Error containing statusCode and code properties. Export the global error handler function that returns { error: { code, message, ...(dev && { stack }) } }. Wrap async route handlers with a utility asyncHandler(fn) to catch promise rejections.
Create Rate Limiter: Write src/middleware/rateLimiter.js using express-rate-limit with configurable window and max from environment variables. Include a key generator that uses IP + optional user ID if authenticated.
Create Validation Middleware: Write src/middleware/validate.js exporting validate(schema, source = 'body') that returns middleware validating against a Zod schema. For now, keep it schema-agnostic but design it for Zod compatibility.
Wire Middleware in index.js: Apply logger as first middleware, rate limiter early in chain, validation per-route, and error handler last. Verify ordering is correct.
Verification & Testing Steps
Start the server and make a request to GET / — verify the console outputs structured JSON logs with correlation ID, method, URL, and response time.
Make a request to a non-existent route (GET /nonexistent) — verify a 404 JSON response with { error: { code: "NOT_FOUND", message: "..." } }.
Make requests rapidly to exceed the rate limit — verify a 429 response with Retry-After header after exceeding the max request count.
Make a request with an invalid body to a validation-enabled route — verify a 400 response listing the specific validation failures.
Throw an unhandled error in a route handler — verify the global error handler catches it and returns a 500 JSON response (with stack trace only in development).
Description
The Express application currently lacks essential middleware layers for production readiness. This issue covers the implementation of four critical middleware components: request logging, centralized error handling, rate limiting, and request validation. These middleware layers are fundamental to operating a secure, observable, and resilient API in production environments.
Request Logging Middleware: Every incoming HTTP request should be logged with a unique correlation ID, method, URL, response status code, response time (in milliseconds), and user agent. The logging should be structured (JSON format) and use the Pino logger (if available) or a similar high-performance logger. Sensitive headers like
Authorizationmust be redacted.Centralized Error Handling Middleware: A global error handler should catch all uncaught errors thrown in route handlers and middleware, returning consistent JSON error responses with appropriate HTTP status codes. Error types should be extended with a custom
AppErrorclass that includes a user-friendly message, an internal error code, and an HTTP status code. Development environments should include stack traces; production environments should suppress them.Rate Limiting Middleware: Basic IP-based rate limiting should be applied globally with configurable window size and max request counts. The middleware must return a
429 Too Many Requestsresponse withRetry-Afterheader when the limit is exceeded. Future issues (Issue #29) will extend this to support tier-based limits.Request Validation Middleware: A utility middleware pattern for validating request bodies, query parameters, and URL parameters against expected schemas (preferably using Zod, which will be fully adopted in Issue #8). Invalid requests should return a 400 status with detailed error messages listing which fields failed validation and why.
Technical Context & Impact
pinofor logging,express-rate-limitfor rate limiting,uuidfor correlation IDs. Zod validation will be formally introduced in Issue [Security] Implement Input Validation with Zod Schemas and Request Sanitization #8 but the middleware pattern should anticipate it.src/middleware/:logger.js,errorHandler.js,rateLimiter.js,validate.js. The error handling middleware must be registered last in the middleware chain.Step-by-Step Implementation Guide
npm install pino express-rate-limit uuid. Create.enventries:LOG_LEVEL(defaultinfo),RATE_LIMIT_WINDOW_MS(default 900000),RATE_LIMIT_MAX(default 100).src/middleware/logger.jsthat initializes a Pino logger, generates a correlation ID viauuid.v4()for each request, attaches it toreq.correlationId, and logs request start and completion with timing.src/middleware/errorHandler.jswithclass AppError extends ErrorcontainingstatusCodeandcodeproperties. Export the global error handler function that returns{ error: { code, message, ...(dev && { stack }) } }. Wrap async route handlers with a utilityasyncHandler(fn)to catch promise rejections.src/middleware/rateLimiter.jsusingexpress-rate-limitwith configurable window and max from environment variables. Include a key generator that uses IP + optional user ID if authenticated.src/middleware/validate.jsexportingvalidate(schema, source = 'body')that returns middleware validating against a Zod schema. For now, keep it schema-agnostic but design it for Zod compatibility.Verification & Testing Steps
GET /— verify the console outputs structured JSON logs with correlation ID, method, URL, and response time.GET /nonexistent) — verify a 404 JSON response with{ error: { code: "NOT_FOUND", message: "..." } }.Retry-Afterheader after exceeding the max request count.