Skip to content

Commit f1b8547

Browse files
jbl428imdudu1
andcommitted
wip: implementing node fetch injector
Co-authored-by: imdudu1 <[email protected]>
1 parent dec5c95 commit f1b8547

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

lib/http-interface.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type DynamicModule } from "@nestjs/common";
22
import { DiscoveryModule } from "@nestjs/core";
3+
import { NodeFetchInjector } from "./node-fetch.injector";
34

45
export interface HttpInterfaceConfig {
56
timeout?: number;
@@ -10,7 +11,7 @@ export class HttpInterfaceModule {
1011
return {
1112
imports: [DiscoveryModule],
1213
module: HttpInterfaceModule,
13-
providers: [],
14+
providers: [NodeFetchInjector],
1415
};
1516
}
1617
}

lib/node-fetch.injector.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Injectable, type OnModuleInit } from "@nestjs/common";
2+
import { DiscoveryService, MetadataScanner } from "@nestjs/core";
3+
import { type InstanceWrapper } from "@nestjs/core/injector/instance-wrapper";
4+
import {
5+
HTTP_EXCHANGE_METADATA,
6+
HTTP_INTERFACE_METADATA,
7+
type HttpExchangeMetadata,
8+
PATH_VARIABLE_METADATA,
9+
type PathVariableMetadata,
10+
} from "./decorators";
11+
12+
@Injectable()
13+
export class NodeFetchInjector implements OnModuleInit {
14+
constructor(
15+
private readonly metadataScanner: MetadataScanner,
16+
private readonly discoveryService: DiscoveryService
17+
) {}
18+
19+
onModuleInit(): void {
20+
const httpProviders = this.getHttpProviders();
21+
22+
httpProviders.forEach((wrapper) => {
23+
const prototype = wrapper.metatype.prototype;
24+
const baseUrl: string = Reflect.getMetadata(
25+
HTTP_INTERFACE_METADATA,
26+
prototype
27+
);
28+
const methodNames = this.metadataScanner.getAllMethodNames(prototype);
29+
30+
methodNames.forEach((methodName) => {
31+
const getMetadata = this.makeMetadataGetter(prototype, methodName);
32+
const httpExchangeMetadata = getMetadata<HttpExchangeMetadata>(
33+
HTTP_EXCHANGE_METADATA
34+
);
35+
36+
if (typeof httpExchangeMetadata === "undefined") {
37+
return;
38+
}
39+
40+
const pathMetadata = getMetadata<PathVariableMetadata>(
41+
PATH_VARIABLE_METADATA
42+
);
43+
44+
wrapper.instance[methodName] = async (...args: any[]) => {
45+
const url = [...(pathMetadata?.entries() ?? [])].reduce(
46+
(url, [index, value]) => url.replace(value, args[index]),
47+
`${baseUrl}${httpExchangeMetadata.url}`
48+
);
49+
const request = new Request(url);
50+
51+
return await fetch(request).then(
52+
async (response) => await response.json()
53+
);
54+
};
55+
});
56+
});
57+
}
58+
59+
private getHttpProviders(): InstanceWrapper[] {
60+
return this.discoveryService.getProviders().filter((wrapper) => {
61+
const metadata = Reflect.getMetadata(
62+
HTTP_INTERFACE_METADATA,
63+
wrapper.metatype
64+
);
65+
return typeof metadata !== "undefined" || metadata !== null;
66+
});
67+
}
68+
69+
private makeMetadataGetter(
70+
prototype: object,
71+
methodName: string
72+
): <T>(key: symbol) => T | undefined {
73+
return (metadataKey: symbol) =>
74+
Reflect.getMetadata(metadataKey, prototype, methodName);
75+
}
76+
}

0 commit comments

Comments
 (0)