Building Scalable APIs with Node.js: Lessons from Production
What we learned building APIs that handle millions of requests. Architecture patterns, caching strategies, and monitoring approaches that actually work at scale.
After building APIs for multiple production applications serving millions of requests, we've learned that scalability isn't about following trends—it's about making deliberate architectural decisions from day one. This article shares the patterns, tools, and mindset shifts that actually work at scale.
Start with the Right Foundation
Every scalable API starts with a solid architectural foundation. We use a layered architecture pattern that separates concerns into controllers, services, and data access layers. This might seem like overengineering for an MVP, but it pays dividends when your user base grows 10x overnight.
Key decisions we make early:
• Use TypeScript from day one—catching type errors at compile time prevents countless production bugs
• Implement dependency injection for testability and flexibility
• Design your API around resources, not database tables
• Version your API from the first endpoint (we use URL versioning: /api/v1/)
Caching: The 80/20 of Performance
Redis has been our go-to caching solution, but the real art is in the caching strategy. Here's what works:
1. Cache frequently accessed, rarely changed data (user profiles, product catalogs, configuration)
2. Implement cache-aside pattern: check cache → if miss, fetch from DB → populate cache
3. Use Redis sorted sets for leaderboards and time-series data
4. Set appropriate TTLs—don't cache indefinitely
5. Implement cache stampede protection with probabilistic early recomputation
A real example: For OneCultur's analytics dashboard, we cache aggregated metrics with a 5-minute TTL. This reduced database load by 80% while keeping data fresh enough for HR teams.
Database Design for Scale
PostgreSQL is our default choice for relational data. MongoDB for flexible document stores. But the database choice matters less than how you use it:
• Index everything you query—use EXPLAIN ANALYZE to verify
• Implement connection pooling (we use pg-pool for PostgreSQL)
• Use read replicas for read-heavy workloads
• Paginate everything with cursor-based pagination
• Avoid N+1 queries with data loaders
For Fix234's marketplace, we handle 1,000+ concurrent searches using PostgreSQL full-text search with proper GIN indexes and materialized views for frequently accessed aggregations.
Error Handling That Doesn't Crash Your App
Graceful error handling is non-negotiable at scale. Our approach:
• Centralized error handling middleware
• Structured error responses with error codes
• Proper HTTP status codes (don't return 200 for errors)
• Async error catching with express-async-errors
• Dead letter queues for failed background jobs
• Circuit breakers for external service calls
We use a custom AppError class that extends Error with status codes and operational flags, making it easy to distinguish between expected errors and actual bugs.
Monitoring and Observability
You can't improve what you don't measure. Our monitoring stack:
• Winston for structured logging
• Morgan for HTTP request logging
• Custom performance metrics with Prometheus
• Error tracking with Sentry
• Uptime monitoring with Better Uptime
• Database query monitoring with pg_stat_statements
Set up alerts for:
• Error rate spikes
• P95 latency degradation
• Database connection pool exhaustion
• Memory/CPU threshold breaches
Security at Every Layer
Security isn't a feature—it's embedded in every decision:
• Rate limiting with express-rate-limit and Redis store
• Input validation with Joi or Zod
• CORS configured explicitly (no wildcards in production)
• Helmet.js for security headers
• JWT with short expiration and refresh token rotation
• SQL injection prevention with parameterized queries
• Regular dependency audits with npm audit
For enterprise clients, we also implement SAML 2.0 SSO and audit logging for compliance requirements.
Deployment and CI/CD
Automated deployments are essential for moving fast without breaking things:
• GitHub Actions for CI/CD pipelines
• Docker containers for consistent environments
• Blue-green deployments for zero-downtime releases
• Automated database migrations with rollback plans
• Feature flags for gradual rollouts
• Automated load testing before production deployments
Our typical pipeline: lint → test → build → deploy to staging → automated smoke tests → deploy to production → monitor for 15 minutes.
Key Takeaway
Building scalable APIs is an ongoing practice, not a one-time setup. Start with solid foundations, measure everything, and iterate based on real production data. The patterns we've shared here have been battle-tested across multiple applications serving millions of requests—they work at startup scale and enterprise scale alike.
Ready to build something great?
Let's discuss how we can help you avoid these mistakes and build your product the right way.
Book Discovery Call