Modern API Design with Node.js & TypeScript: Building for High Concurrency
Designing resilient backend APIs that never stumble during viral traffic spikes. Learn how to architect clean Node.js and TypeScript services with input validation, graceful error handling, and rate limiting.
The High-Concurrency Challenge for Modern Backends
In modern software development, writing an API that functions correctly on a developer's localhost machine is relatively straightforward. A few lines of Express or Fastify code, a basic database query, and you have a working endpoint.
However, the real test of backend engineering begins when your service transitions to production:
In unoptimized backends, these stresses cause the dreaded Event Loop Lag, memory leaks, unhandled promise rejections, and complete server outages.
Building scalable APIs requires strict discipline: compile-time type safety, runtime input validation, non-blocking asynchronous workflows, intelligent caching, circuit breakers, and graceful failure isolation.
In this technical blueprint, we share the battle-tested API architecture patterns we use at Plexid to build services that handle tens of thousands of concurrent users with sub-50ms response times.
Core Principles of Production-Grade API Engineering
Client Request
|
v
[1. Rate Limiting & DDoS Shield] (Prevent abuse & bot hammering)
|
v
[2. Zod Runtime Schema Validation] (Reject malformed payloads with 400 Bad Request)
|
v
[3. Idempotency Key Verification] (Ensure duplicate requests don't double charge)
|
v
[4. Authentication & RBAC Middleware] (Verify JWT / Session tokens)
|
v
[5. Controller Layer] (Orchestrate input & output, no raw business logic)
|
v
[6. Service / Domain Layer] (Pure business logic, unit-testable)
|
v
[7. Data Access Layer (ORM / Query Builder)] (Read-replicas & Redis caching)Principle 1: End-to-End Type Safety with TypeScript and Zod
One of the most dangerous anti-patterns in backend JavaScript is trusting incoming HTTP request bodies. If a client sends an unexpected data type (such as passing a string where an integer is expected, or omitting an email field), your database queries can throw runtime errors or execute unintended logic.
Using TypeScript alone is insufficient because TypeScript types are completely stripped away at runtime. You need runtime schema validation synchronized with compile-time types.
The Zod Validation Pattern:
import { z } from 'zod';
import { Request, Response, NextFunction } from 'express';
// 1. Define the runtime validation schema
export const CreateOrderSchema = z.object({
body: z.object({
productId: z.string().uuid("Invalid product identifier"),
quantity: z.number().int().positive().max(100, "Maximum 100 units per order"),
shippingPincode: z.string().regex(/^[1-9][0-9]{5}$/, "Invalid 6-digit Indian PIN code"),
customerPhone: z.string().regex(/^[6-9]d{9}$/, "Invalid 10-digit Indian mobile number")
})
});
// Infer TypeScript type directly from the schema (Single Source of Truth!)
export type CreateOrderInput = z.infer<typeof CreateOrderSchema>['body'];
// 2. Reusable validation middleware
export const validate = (schema: z.AnyZodObject) =>
async (req: Request, res: Response, next: NextFunction) => {
try {
await schema.parseAsync({
body: req.body,
query: req.query,
params: req.params,
});
return next();
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({
status: 'fail',
errors: error.errors.map(err => ({ field: err.path.join('.'), message: err.message }))
});
}
return next(error);
}
};By enforcing this middleware before your controller executes, you guarantee that your business logic only ever receives pristine, valid data. Invalid requests are rejected in under 2 milliseconds without touching your database.
Principle 2: Protecting the Node.js Single-Threaded Event Loop
Node.js executes JavaScript on a single thread using an event loop. While asynchronous I/O operations (like database queries and network calls) are offloaded to background worker threads, CPU-intensive operations are executed directly on the main event loop.
If your API handler performs heavy synchronous operations—such as:
The event loop becomes completely blocked. While that single calculation is running, no other incoming request can be accepted or processed, causing latency to spike for every user connected to your service.
Best Practices for Event Loop Health:
bcrypt.hashSync or crypto.pbkdf2Sync in request handlers. Always use their asynchronous promise-based variants (await bcrypt.hash).clinic.js or Prometheus metrics to monitor nodejs_eventloop_lag_seconds. In a healthy service, event loop lag should remain below 20 milliseconds.Principle 3: Idempotency Keys for Financial & Critical Mutations
On mobile cellular connections across India, mobile networks frequently drop connection right as an order is submitted. The customer taps "Pay Now", the bank debits their account, but the mobile network disconnects before receiving the API confirmation.
The customer panics and taps "Pay Now" a second time. Without idempotency protection, the backend processes a duplicate charge and creates two identical orders.
The Idempotency Key Architecture:
// Idempotency Middleware using Redis
export const idempotencyMiddleware = async (req: Request, res: Response, next: NextFunction) => {
const idempotencyKey = req.headers['x-idempotency-key'] as string;
if (!idempotencyKey) return next();
const cacheKey = `idempotency:${idempotencyKey}`;
const cachedResponse = await redis.get(cacheKey);
if (cachedResponse) {
// Return the exact cached response from the previous successful execution
const parsed = JSON.parse(cachedResponse);
return res.status(parsed.status).json(parsed.data);
}
// Intercept the response to cache it upon completion
const originalJson = res.json.bind(res);
res.json = (body: any) => {
redis.setex(cacheKey, 86400, JSON.stringify({ status: res.statusCode, data: body }));
return originalJson(body);
};
next();
};Principle 4: Circuit Breaker Pattern for External HTTP Calls
Modern applications rely on external third-party APIs for SMS OTPs, GST lookups, and payment gateway webhooks.
If an external payment gateway experiences an outage and begins taking 30 seconds to fail, your backend servers will keep hundreds of HTTP connections open waiting for responses. Within minutes, your server exhausts its socket pool and crashes completely.
The Fix: Circuit Breakers (using Opossum)
A circuit breaker monitors external calls. If an external service fails more than 50% of the time over a 10-second window, the circuit trips open.
Subsequent calls fail immediately with a clean fallback message, preventing your application from freezing or exhausting server memory. Once the external service recovers, the circuit automatically closes and normal traffic resumes.
Principle 5: Graceful Shutdowns & Health Checks
When deploying new code or restarting cloud containers in Kubernetes or AWS ECS, the cloud orchestrator sends a SIGTERM signal to your Node.js process.
If your application abruptly exits upon receiving SIGTERM, any active database transactions are violently severed, leading to corrupted data states or lost customer orders.
Implementing Graceful Shutdown in Node.js:
// Graceful shutdown handler
function setupGracefulShutdown(server: any, dbConnection: any) {
const shutdown = async (signal: string) => {
console.log("Received " + signal + ". Starting graceful shutdown...");
// 1. Stop accepting new incoming HTTP connections
server.close(async () => {
console.log('HTTP server closed.');
// 2. Close active database connections cleanly
try {
await dbConnection.close();
console.log('Database connections closed.');
process.exit(0);
} catch (err) {
console.error('Error during database close:', err);
process.exit(1);
}
});
// 3. Force exit if shutdown hangs longer than 10 seconds
setTimeout(() => {
console.error('Graceful shutdown timed out. Forcing process exit.');
process.exit(1);
}, 10000);
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
}High-Concurrency Benchmarking Blueprint: Testing with Autocannon
Never deploy an API to production without subjecting it to real-world stress testing. Using autocannon, you can simulate thousands of concurrent connections from your terminal:
# Run a 30-second benchmark simulating 500 concurrent connections
npx autocannon -c 500 -d 30 -m POST \
--headers "content-type=application/json" \
--body '{"productId":"d840ef90-502a-45c1-a5d6-8480db80a37e","quantity":1,"shippingPincode":"800001","customerPhone":"9876543210"}' \
http://localhost:5000/api/v1/ordersKey metrics to evaluate:
Production API Architecture Checklist
| Architecture Component | Implementation Standard | Recommended Solution |
|---|---|---|
| Language & Typing | Strict TypeScript (No any types permitted) | TypeScript 5.x |
| Input Validation | Runtime validation schemas with auto-type generation | Zod / TypeBox |
| Idempotency | X-Idempotency-Key support for payment and order routes | Redis Key Expiry (24 hours) |
| Circuit Breakers | Fault isolation on all external HTTP dependencies | Opossum Circuit Breaker |
| Rate Limiting | Tiered rate limiting by IP and authenticated user token | express-rate-limit + Redis store |
| Background Processing | Decoupled Redis queues for asynchronous operations | BullMQ / AWS SQS |
| Logging & Tracing | Structured JSON logging with unique correlation IDs | Pino / Winston |
Conclusion: Engineering Discipline at Scale
Building high-throughput backend APIs is not about writing clever, cryptic code. It is about applying consistent engineering discipline: validating inputs rigorously, guarding the event loop, handling failures gracefully, and writing clean, type-safe services that your team can maintain and scale with confidence.
When your backend architecture is built right from day one, traffic spikes become causes for celebration rather than midnight engineering emergencies.
Engineers at Plexid specialize in high-concurrency microservices, real-time telephony AI pipelines, distributed web architectures, and sub-second Next.js web applications.
Related Architecture Publications
How to Choose the Right Software Development Agency in India (Without Getting Burned)
Hiring a development partner shouldn't feel like a high-stakes gamble. Learn the critical questions, contract clauses, and red flags to protect your budget, timeline, and intellectual property.
Cybersecurity Checklist for Growing Indian Companies: Protecting Customer Data in 2026
With India's Digital Personal Data Protection (DPDP) Act in full force, data security is no longer optional. Discover the essential technical safeguards every Indian business must implement.