Episode 35 builds resilient GraphQL services: high availability design with multi-region 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.

Downtime isn't "if" but "when". Episode 35 builds resilient GraphQL services — able to survive database, provider, even whole-region failures. The main goal isn't to avoid failure, but to keep its impact as small as possible.
We'll cover high availability, backup strategies, disaster recovery planning, circuit breakers, and graceful degradation.
High availability (HA) means the service stays available even through failures. The core designs:
region-a (primary) <--> region-b (secondary)
| |
traffic standbyFor stateless GraphQL (episode 34), adding regions is relatively easy: deploy the same image, point DNS at it, and make sure Redis and the database are replicated across regions.
Combine liveness and readiness (episode 24) with redundancy: a database with a promotable replica, a Redis cluster that can survive node loss, and a caching strategy that doesn't stop the service when Redis is down (falling back to the database directly).
A proper backup isn't just a copy — it must be restorable quickly:
A manual PostgreSQL backup is pg_dump -Fc database-name > backup.dump.
For managed databases (RDS, Cloud SQL), enable automated backups with built-in PITR.
A DR plan is measured by two metrics:
RTO: how fast to get back to normal
RPO: how much data may be lostThe smaller RTO and RPO, the more expensive the infrastructure. Set realistic targets based on the business, then design the strategy (for example active-passive multi-region for low RTO, daily backups for non-critical data).
A good DR plan must be documented and tested periodically. Schedule DR drills: turn off the primary region, force a failover, measure recovery time, and compare against RTO/RPO targets. From the drill results, update the runbook and automation.
A circuit breaker prevents one failure from spreading. Three states:
class CircuitBreaker {
constructor(fn, { threshold = 5, cooldownMs = 30000 }) {
this.fn = fn;
this.failures = 0;
this.threshold = threshold;
this.cooldownMs = cooldownMs;
this.lastFailure = 0;
this.state = "CLOSED";
}
async call(...args) {
if (this.state === "OPEN" && Date.now() - this.lastFailure < this.cooldownMs) {
throw new Error("Circuit terbuka: dependency sedang bermasalah");
}
try {
const result = await this.fn(...args);
this.failures = 0;
this.state = "CLOSED";
return result;
} catch (err) {
this.failures += 1;
this.lastFailure = Date.now();
if (this.failures >= this.threshold) this.state = "OPEN";
throw err;
}
}
}Use a library like opossum for a production-ready implementation. Apply circuit breakers to external dependencies: payment gateways, email services, and third-party APIs. Complement with a fallback — for example returning cached data when the dependency is down.
Graceful degradation means the service keeps working (at reduced quality) when some components fail:
async function getTrending(ctx) {
try {
const data = await ctx.trendingService.fetch();
await ctx.redis.set("trending", JSON.stringify(data), "EX", 300);
return data;
} catch {
const cached = await ctx.redis.get("trending");
return cached ? JSON.parse(cached) : [];
}
}This fallback pattern keeps the API responding even when the primary source is troubled — much better than a total 500 error.
Key takeaways:
In the next episode, episode 36, you'll learn about GraphQL with microservices — gateway patterns for microservices, a federation deep dive, event-driven architecture with event sourcing and CQRS, message brokers like Kafka and RabbitMQ, and service discovery. Your architecture will scale to enterprise!