Software Development 10 min read

Node.js REST API Best Practices: Security, Performance, and Structure in 2025

22 August 2024
10 min read

Why API Architecture Decisions Matter Early

A Node.js REST API that serves 100 requests per day and one that serves 10 million requests per day can start from the same Express boilerplate. The difference is whether the architectural decisions made at the start — project structure, authentication strategy, error handling, validation, logging, and database connection management — are made deliberately or accidentally. Retrofitting security and scalability into an API that has grown organically for two years is significantly more expensive than getting the fundamentals right from the beginning. This guide covers the patterns and practices that PCCVDI Solutions applies to every production Node.js API we build or audit, distilled from years of building APIs that handle production traffic for Indian enterprises and startups.

Project Structure

Organise your project by domain and feature, not by technical layer. The flat controllers-services-routes-models structure is intuitive for small projects but becomes unwieldy as the codebase grows. A domain-oriented structure scales better: each module owns its controller (request and response handling), service (business logic), repository (data access), routes (URL routing), and validation schema. This makes modules independently testable and allows teams to own features without stepping on each other's code.

Authentication and Authorisation

JWT (JSON Web Tokens) are the standard for stateless API authentication. Use short-lived access tokens (15–60 minutes) combined with longer-lived refresh tokens (7–30 days) stored in HttpOnly cookies. Never store tokens in localStorage — it is accessible to JavaScript and vulnerable to XSS attacks.

Key JWT implementation points:

  • Sign JWTs with RS256 (asymmetric RSA) rather than HS256 (symmetric HMAC) when multiple services need to verify tokens — the private key signs, public keys verify, and you never need to distribute the secret.
  • Include a jti (JWT ID) claim and maintain a token revocation list in Redis for logout functionality. Pure stateless JWT cannot support logout without this mechanism.
  • Validate the aud (audience) and iss (issuer) claims on every token verification to prevent token substitution attacks across services.
  • Use the jose library for token verification — do not implement JWT validation from scratch as subtle implementation bugs create serious security vulnerabilities.

Input Validation and Sanitisation

Never trust input. Every piece of data that enters your API — path parameters, query strings, request bodies, headers — must be validated before it reaches your business logic. Use zod (preferred for TypeScript projects) or joi for schema-based validation. Apply validation in a middleware layer before the controller so that invalid requests are rejected immediately with a structured 400 response, and your controllers can assume all input is valid and correctly typed. Use parameterised queries for all database operations — never string-concatenate user input into SQL queries.

Error Handling

Implement a global error handling middleware as the last middleware in your Express chain. Define a custom error hierarchy with AppError, NotFoundError, ValidationError, and UnauthorisedError classes that carry HTTP status codes and machine-readable error codes. The global error middleware catches these custom errors and formats consistent JSON error responses. Critically, it also catches unexpected errors and returns a generic 500 response without leaking stack traces to the client — while logging the full error details server-side with a correlation ID that can be shared with the user for support reference.

Rate Limiting and Security Headers

Apply rate limiting at multiple levels: globally to protect against DDoS, per-endpoint with stricter limits on authentication endpoints to prevent credential stuffing, and per-user to prevent authenticated API abuse. Use express-rate-limit with a Redis store (rate-limit-redis) for distributed rate limiting across multiple API instances.

Use helmet to set security-relevant HTTP headers on every response: Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, and Content-Security-Policy. These headers are a first line of defence against a range of web vulnerabilities and require only one line to enable: app.use(helmet()). Add CORS configuration with an explicit allowlist of permitted origins rather than using the wildcard asterisk.

Logging and Observability

Use a structured logger (pino or winston) that outputs JSON logs. Structured logs are parseable by log aggregation platforms (ELK Stack, Loki, CloudWatch) without complex regex parsing. Include a correlation ID (request-scoped UUID) in every log line so you can trace all log entries for a single API request across your distributed system. Log at appropriate levels: DEBUG for development-only detail, INFO for normal business events (order created, user logged in), WARN for recoverable anomalies (rate limit approaching, deprecated endpoint called), ERROR for failures requiring investigation. Never log PII — passwords, card numbers, Aadhaar numbers — implement log scrubbing middleware that redacts sensitive fields before they reach the log sink.

Performance Optimisation

For Node.js APIs handling concurrent requests, the most impactful performance practices are: using connection pooling for your database so you never open a new connection per request; caching expensive computations or database queries in Redis with appropriate TTLs; using async/await correctly to avoid accidentally serialising parallel operations that could run concurrently with Promise.all; and implementing HTTP response compression. Run Node.js in cluster mode or use PM2 with cluster mode to utilise all available CPU cores, since Node is single-threaded per process. Enable HTTP/2 on your reverse proxy (Nginx) for multiplexed request handling over persistent connections, which dramatically reduces latency for clients making multiple concurrent API calls.

Tags:Node.jsREST APIJavaScriptTypeScriptSecurityBackendExpress
P

PCCVDI Editorial Team

Technology Consultants · PCCVDI Solutions

Our editorial team comprises certified cloud architects, security specialists, and DevOps engineers based in New Delhi, India. We share practical insights from real-world enterprise technology engagements across India and globally.