Skip to content

Commit 74c581f

Browse files
committed
feat: add circuit breaker support
1 parent c327db8 commit 74c581f

File tree

4 files changed

+31
-2
lines changed

4 files changed

+31
-2
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import CircuitBreaker from 'opossum';
2+
3+
export class CircuitBreakerBuilder {
4+
constructor(private readonly options?: CircuitBreaker.Options) {}
5+
6+
async build(executor: () => Promise<Response>): Promise<Response> {
7+
if (this.options == null) {
8+
return await executor();
9+
}
10+
11+
return await new CircuitBreaker(executor, this.options).fire();
12+
}
13+
}

lib/decorators/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export const REQUEST_BODY_METADATA = Symbol('REQUEST_BODY_METADATA');
66
export const REQUEST_FORM_METADATA = Symbol('REQUEST_FORM_METADATA');
77
export const REQUEST_HEADER_METADATA = Symbol('REQUEST_HEADER_METADATA');
88
export const RESPONSE_BODY_METADATA = Symbol('RESPONSE_BODY_METADATA');
9+
export const CIRCUIT_BREAKER_METADATA = Symbol('CIRCUIT_BREAKER_METADATA');

lib/supports/node-fetch.injector.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { Injectable, type OnModuleInit } from '@nestjs/common';
22
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
33
import { type InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
4+
import { CircuitBreakerBuilder } from '../builders/circuit-breaker.builder';
45
import { type HttpRequestBuilder } from '../builders/http-request.builder';
56
import { type ResponseBodyBuilder } from '../builders/response-body.builder';
67
import {
8+
CIRCUIT_BREAKER_METADATA,
79
HTTP_EXCHANGE_METADATA,
810
HTTP_INTERFACE_METADATA,
911
RESPONSE_BODY_METADATA,
@@ -43,14 +45,25 @@ export class NodeFetchInjector implements OnModuleInit {
4345
return;
4446
}
4547

48+
const circuitBreaker: CircuitBreakerBuilder =
49+
Reflect.getMetadata(CIRCUIT_BREAKER_METADATA, prototype, methodName) ??
50+
new CircuitBreakerBuilder(
51+
this.httpInterfaceConfig?.circuitBreakerOption,
52+
);
4653
const responseBodyBuilder: ResponseBodyBuilder<unknown> | undefined =
4754
Reflect.getMetadata(RESPONSE_BODY_METADATA, prototype, methodName);
4855

4956
httpRequestBuilder.setBaseUrl(baseUrl);
5057

5158
wrapper.instance[methodName] = async (...args: never[]) =>
52-
await this.httpClient
53-
.request(httpRequestBuilder.build(args), httpRequestBuilder.options)
59+
await circuitBreaker
60+
.build(
61+
async () =>
62+
await this.httpClient.request(
63+
httpRequestBuilder.build(args),
64+
httpRequestBuilder.options,
65+
),
66+
)
5467
.then(async (response) => {
5568
if (responseBodyBuilder != null) {
5669
const res = await response.json();

lib/types/http-interface-config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { type ClassTransformOptions } from 'class-transformer';
2+
import type CircuitBreaker from 'opossum';
23
import { type HttpClient } from './http-client.interface';
34

45
export interface HttpInterfaceConfig {
56
timeout?: number;
67
httpClient?: HttpClient;
78
transformOption?: ClassTransformOptions;
9+
circuitBreakerOption?: CircuitBreaker.Options;
810
}

0 commit comments

Comments
 (0)