Circuit breaker pattern designed to use the native API of the ky library
npm install ky-circuit-breakerKyInstance. There are other options available for adopting a circuit breaker pattern, however, my goal was simplicity in the sense that I wanted to pass my KyInstance via the constructor and the library do the rest.State machine logic was adopted from albertllousas work over @ https://github.com/albertllousas/circuit-breaker-typescript and the kudos go to him for the state machine.
If you find this work helpful please consider buying me a coffee.![]()
____________________________
If this resilience pattern does not sound familiar to you, take a look on these resources:
- Circuit breaker wikipedia
- Circuit breaker - Martin Fowler
- Release It!
``bash`
npm i ky-circuit-breaker
The below approach will protect all of the following automagically.
- ky.get
- ky.post
- ky.put
- ky.patch
- ky.head
- ky.delete
`js
import ky from 'ky';
import { CircuitBreaker } from 'ky-circuit-breaker';
const circuitBreaker = new CircuitBreaker(ky);
// that's it, your native KyInstance functions
// are now protected with a CircuitBreaker using the default settings.
const result = await ky.get('https://httpbin.org/get').json();
// make calls the normal way.
`
#### Promises
Let's assume you have an http call and you want to fail-fast gracefully without waiting for TCP connection timeout in
case of the service eventually is not available...you can protect that individual promise with the example below:
`js
const unprotectedPromise = () => fetch(someUrl).then(response => response.json());
`
Protecting it is pretty straight forward:
`js
const circuitBreaker = new CircuitBreaker();
const protectedPromise = circuitBreaker.protectPromise(unprotectedPromise);
//normal use
protectedPromise().then(...);
`
Enum for circuitStatus
shape:
`js
export enum CircuitStatusFlag {
CLOSED,
HALF,
OPEN
}
`
Create a new instance of a circuit breaker. It accepts the following config options:
`json`
maxFailures: number;
timeoutLimit: number;
hooks?: {
beforeRequest?: { (): void }[],
afterPromiseComplete?: { (recoveryAttempts: number, recoverySuccessful: boolean, recoveryFailed: boolean): void }[]
}
Number of errors after the circuit trips to open and starts short-circuiting requests and failing-fast.
Default Value: 5
Time in milliseconds in which after tripping to open the circuit will remain failing fast.
Default Value: 5000
Hooks specific to the circuit breaker.
beforeRequest - Synonymous to ky before request hook - function callback before every request.afterPromiseComplete - function callback after every promise is complete.
typescriptimport { CircuitBreaker, CircuitStatusFlag } from 'ky-circuit-breaker';
private setHttpClient(): void {
const circuitBreakerStateHandler = (recoveryAttempts?, recoveryFailed?) => {
if (this.circuitBreaker.circuitStatus === CircuitStatusFlag.OPEN && recoveryAttempts === this.circuitRecoveryThreshold) {
/ Shut down the app /
return;
}
switch (this.circuitBreaker.circuitStatus) {
case CircuitStatusFlag.CLOSED: {
/ Handle circuit breaker closure /
if (recoverySuccessful) {
/ Handle recovery successful /
}
break;
}
case CircuitStatusFlag.OPEN: {
/ Handle circuit breaker opening /
if (recoveryFailed) {
/ Handle recovery failure /
}
break;
}
case CircuitStatusFlag.HALF: {
/ Handle circuit breaker half open/
break;
}
default:
break;
}
};
this.httpClient = ky.extend({
prefixUrl,
timeout: false,
retry: {
limit: 5,
methods: ['post', 'get', 'put']
},
});
this.circuitBreaker = new CircuitBreaker(this.httpClient, {
maxFailures: 5,
timeoutLimit: 5000,
hooks: {
beforeRequest: [circuitBreakerStateHandler],
afterPromiseComplete: [circuitBreakerStateHandler]
}
});
}
}
``