Episode 34 discusses scaling GraphQL: vertical scaling with resource and memory optimization, horizontal scaling with load balancing and stateless design, database scaling with read replicas and PgBouncer, layered caching, scaling WebSocket subscriptions with Redis PubSub, and the circuit breaker and bulkhead patterns.

Your traffic is rising — and the question isn't "will the server break", but "when and how to handle it". Episode 34 discusses scaling GraphQL: adding machine power, adding machine count, and making sure every component from database to WebSocket scales along.
We'll cover vertical scaling, horizontal scaling, database scaling, layered caching, scaling subscriptions, and resilience patterns like circuit breaker and bulkhead.
Vertical scaling adds resources to a single machine — the easiest way, but it has limits. Reasonable steps before moving horizontal:
{
"scripts": {
"start": "node --max-old-space-size=2048 dist/index.js"
}
}Other optimizations: DataLoader batching (episode 9), caching (episodes 19-20), and trimming complex queries (episode 15) reduce per-request load so a single machine can serve more.
Horizontal scaling adds more instances behind a load balancer:
client -> load balancer -> instance 1
-> instance 2
-> instance 3The main requirement: the server must be stateless — don't store sessions or cache in an instance's local memory. All shared state goes to Redis:
Run multiple instances with the cluster pm2 start dist/index.js -i max.
A load balancer (nginx, ALB, or Kubernetes Service) distributes requests. Consistency is achieved because there's no local state to keep in sync.
The database is often the first bottleneck. Strategies:
docker run -d --name pgbouncer \
-e DB_HOST=db-primary -e DB_USER=app -e DB_PASSWORD=pass \
-p 6432:5432 edoburu/pgbouncerApp connections point at PgBouncer (localhost:6432) instead of the database directly. This is especially important in serverless environments (episode 37) where connections are opened and closed constantly.
Cache at several layers to cut database load:
For invalidation at scale, use an event-based pattern: mutations publish an event (episode 16) and the cache is purged centrally, rather than relying on TTL alone.
WebSocket-based subscriptions need special attention:
import { RedisPubSub } from "graphql-redis-subscriptions";
const pubsub = new RedisPubSub({
publisher: new Redis(process.env.REDIS_URL),
subscriber: new Redis(process.env.REDIS_URL),
});WebSocket connections are managed with graphql-ws (episode 16) on each instance; events from any mutation spread through Redis so all clients receive them. Also limit connections per user and use heartbeats to clean up dead connections.
At large scale, one component's failure must not take everything down:
import { Pool } from "pg";
const userPool = new Pool({ connectionString: DB_URL, max: 10 });
const orderPool = new Pool({ connectionString: DB_URL, max: 5 });These resilience patterns will be deepened in episode 35 together with high availability strategies.
Key takeaways:
In the next episode, episode 35, you'll learn about disaster recovery and high availability — multi-region design and failover, backup strategies with point-in-time recovery, disaster recovery planning with RTO and RPO, the circuit breaker pattern, and graceful degradation with feature flags. Your API will be resilient!