@myria/api-request
Version:
Myria's Typescript api-request
248 lines (228 loc) • 7.87 kB
text/typescript
import {
HttpStatus,
NotFoundException,
BadRequestException,
UnauthorizedException,
InternalServerErrorException,
Logger,
ForbiddenException,
} from "@nestjs/common";
import axios, {
AxiosRequestConfig,
AxiosRequestHeaders,
AxiosResponse,
RawAxiosRequestHeaders,
} from "axios";
import { randomUUID } from "crypto";
import { ConfigOptions, RequestContext } from "../type";
export interface QueryParams {
[key: string]: string | number | undefined;
}
/* eslint @typescript-eslint/explicit-module-boundary-types: 0 */ // --> OFF
export abstract class BaseApiService {
public baseURL: string | undefined;
public context: RequestContext;
public configOptions: ConfigOptions;
constructor(baseURL: string | undefined, configOptions: ConfigOptions) {
this.baseURL = baseURL;
this.configOptions = configOptions;
this.context = {
correlationId: randomUUID(),
logger: new Logger("BaseApiService"),
};
}
private refreshCorrelationId(): void {
this.context.correlationId = randomUUID();
}
public handleError(error: any): void {
if (error.response) {
const { status, statusText } = error?.response;
if (error.response.status === HttpStatus.NOT_FOUND) {
this.context.logger.fatal(
{
...error.response.data,
correlationId: this.context.correlationId,
},
"Request to api",
);
throw new NotFoundException({
status,
statusText,
apiErrors: error.response.data,
});
}
if (error.response.status === HttpStatus.BAD_REQUEST) {
this.context.logger.fatal(
{
...error.response.data,
correlationId: this.context.correlationId,
},
"Request to api",
);
throw new BadRequestException({
status,
statusText,
apiErrors: error.response.data,
});
}
if (error.response.status === HttpStatus.UNAUTHORIZED) {
this.context.logger.fatal(
{
...error.response.data,
correlationId: this.context.correlationId,
},
"Request to api",
);
throw new UnauthorizedException({
status,
statusText,
apiErrors: error.response.data,
});
}
if (error.response.status === HttpStatus.FORBIDDEN) {
this.context.logger.fatal(
{
...error.response.data,
correlationId: this.context.correlationId,
},
"Request to api",
);
throw new ForbiddenException({
status,
statusText,
apiErrors: error.response.data,
});
}
throw new InternalServerErrorException({
status,
statusText,
apiErrors: error.response.data,
});
}
if (error.code === "ECONNREFUSED") {
throw new InternalServerErrorException({
message: "Connection refused",
});
}
this.context.logger.fatal(
{
error,
correlationId: this.context.correlationId,
},
"Request to api",
);
throw new InternalServerErrorException(error);
}
public async get(
url: string,
query?: QueryParams,
headers?: AxiosRequestHeaders | RawAxiosRequestHeaders,
): Promise<AxiosResponse<any, any> | undefined> {
try {
const headerRequest = this.buildHeaders(this.context, headers);
const response = await axios.get(url, {
params: query,
headers: headerRequest,
baseURL: this.baseURL,
timeout: this.configOptions.timeoutResponse,
});
return response;
} catch (error) {
this.handleError(error);
}
}
public async post(
url: string,
data: any,
headers?: AxiosRequestHeaders | RawAxiosRequestHeaders,
): Promise<AxiosResponse<any, any> | undefined> {
try {
const response = await axios.post(url, data, {
headers: this.buildHeaders(this.context, headers),
baseURL: this.baseURL,
timeout: this.configOptions.timeoutResponse,
});
return response;
} catch (error) {
this.handleError(error);
}
}
public async put(
url: string,
data: any,
headers?: AxiosRequestHeaders | RawAxiosRequestHeaders,
): Promise<AxiosResponse<any, any> | undefined> {
try {
const response = await axios.put(url, data, {
headers: this.buildHeaders(this.context, headers),
baseURL: this.baseURL,
timeout: this.configOptions.timeoutResponse,
});
return response;
} catch (error) {
this.handleError(error);
}
}
public async patch(
url: string,
data: any,
headers?: AxiosRequestHeaders | RawAxiosRequestHeaders,
): Promise<AxiosResponse<any, any> | undefined> {
try {
const response = await axios.patch(url, data, {
headers: this.buildHeaders(this.context, headers),
baseURL: this.baseURL,
timeout: this.configOptions.timeoutResponse,
});
return response;
} catch (error) {
this.handleError(error);
}
}
public async delete(
url: string,
data?: any,
headers?: AxiosRequestHeaders | RawAxiosRequestHeaders,
): Promise<AxiosResponse<any, any> | undefined> {
try {
const response = await axios.delete(url, {
headers: this.buildHeaders(this.context, headers),
timeout: this.configOptions.timeoutResponse,
baseURL: this.baseURL,
data,
});
return response;
} catch (error) {
this.handleError(error);
}
}
private buildHeaders(
context: RequestContext,
headers?: AxiosRequestHeaders | RawAxiosRequestHeaders,
): AxiosRequestHeaders {
if (!headers) {
return <AxiosRequestHeaders>{};
}
this.refreshCorrelationId();
headers["x-correlation-id"] = context.correlationId;
const { headers: headerParams } = <AxiosRequestConfig>context;
if (headerParams) {
if (headerParams["x-signature"]) {
headers["x-signature"] = String(headerParams["x-signature"]);
}
if (headerParams["x-timestamp"]) {
headers["x-timestamp"] = String(headerParams["x-timestamp"]);
}
if (headerParams["stark-key"]) {
headers["stark-key"] = String(headerParams["stark-key"]);
}
}
context.logger.debug(
JSON.stringify({
headers,
message: "Header config before request to out side",
}),
);
return <AxiosRequestHeaders>headers || {};
}
}