In the world of microservices and distributed systems, network failures are not a matter of "if", but "when". A single failing service can hang your entire application, consuming resources until it crashes.
To prevent this, the Circuit Breaker pattern is essential. But let's be honest: implementing it often leads to messy code, with try/catch blocks wrapping every single API call.
Today, I want to show you how to solve this elegantly using surge-kit, a lightweight, zero-dependency library I built for Node.js.
With the release of v0.5.0, we can now use TypeScript Decorators to handle resilience declaratively.
The Problem: "Wrapper Hell" 😫
Usually, protecting a method looks like this:
// The "Old Way"
async getUser(id: string) {
try {
// Manual wrapping... repetitive and verbose
return await circuitBreaker.fire(async () => {
return await axios.get(`/users/${id}`);
});
} catch (error) {
// Manual fallback logic...
return null;
}
}
It works, but it hurts readability.
The Solution: Decorators ✨
With surge-kit, we can strip away all that boilerplate.
- Installation First, install the package:
npm install surge-kit
(Note: Make sure you have "experimentalDecorators": true in your tsconfig.json)
2. Global Setup (Singleton)
You probably want to share the same Circuit Breaker configuration across your app. You can set a default instance once (e.g., in your main.ts or app.ts):
import { Relay } from 'surge-kit';
// Configure your breaker
const globalRelay = new Relay({
failureThreshold: 3, // Open after 3 failures
coolDownPeriod: 10000, // Wait 10s before retrying
useExponentialBackoff: true // Smart cooldown! 🧠
});
// Set it as the default
Relay.setDefault(globalRelay);
3. Protecting Your Methods
Now, you can simply decorate your methods. No more wrappers!
import { UseRelay, Fallback } from 'surge-kit';
class UserService {
@Fallback((err) => {
console.warn(`Service failed: ${err.message}`);
return { id: 0, name: "Guest User" }; // Return cached or default data
})
@UseRelay() // Automatically uses the default circuit breaker
async getUser(id: string) {
// Just your clean business logic!
const { data } = await axios.get(`https://api.example.com/users/${id}`);
return data;
}
}
If the API fails (or times out), surge-kit counts the failure. If the threshold is reached, the circuit opens, and subsequent calls fail fast (or use the fallback) without hitting the struggling API.
Why surge-kit?
I built this because existing solutions felt too heavy for simple use cases.
⚡ Zero Dependencies: It’s tiny and won’t bloat your node_modules.
🛡️ Production Ready: Includes Exponential Backoff (cooldown increases if the service stays down) and execution Timeouts.
🔍 Observability: It exposes metrics and emits events (OPEN, CLOSE, HALF_OPEN) so you can plug it into your logging system.
Try it out!
The project is Open Source and I'm actively looking for feedback and contributors.
📦 NPM: npmjs.com/package/surge-kit
🐙 GitHub: github.com/Dev-Etto/surge-kit
If you found this useful, leave a ⭐ on the repo! It helps a lot.
Happy coding! 🚀
Top comments (0)