Last active
April 7, 2025 16:47
-
-
Save estevan-ulian/2717c247c4387caf7eed180237b6efec to your computer and use it in GitHub Desktop.
HttpClient instance with Axios
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface IHttpError { | |
name: string; | |
message: string; | |
status: number; | |
details?: unknown; | |
} | |
const HttpMethod = { | |
GET: "GET", | |
POST: "POST", | |
PUT: "PUT", | |
DELETE: "DELETE", | |
PATCH: "PATCH", | |
} as const; | |
type IHttpMethod = (typeof HttpMethod)[keyof typeof HttpMethod]; | |
interface IHttpRequest<B> { | |
url?: string; | |
method?: IHttpMethod; | |
headers?: Record<string, string>; | |
body?: B; | |
} | |
interface IHttpResponse<R> { | |
data: R | null; | |
error: IHttpError | null; | |
} | |
interface IHttpClient { | |
request<B, R>(request: IHttpRequest<B>): Promise<IHttpResponse<R>>; | |
} | |
class HttpClient implements IHttpClient { | |
private static instance: HttpClient; | |
private constructor( | |
private httpHandler: AxiosInstance, | |
private baseUrl: string, | |
private isDebugMode: boolean = false | |
) { | |
this.validateParams(); | |
this.setupInterceptors(); | |
} | |
static create(params: { httpHandler: AxiosInstance; baseUrl: string }) { | |
if (!HttpClient.instance) { | |
HttpClient.instance = new HttpClient(params.httpHandler, params.baseUrl); | |
} else { | |
if (HttpClient.instance.httpHandler !== params.httpHandler) { | |
throw new Error("httpHandler cannot be changed after initialization"); | |
} | |
HttpClient.instance.updateBaseUrl(params.baseUrl); | |
} | |
return HttpClient.instance; | |
} | |
updateBaseUrl(newBaseUrl: string) { | |
this.baseUrl = newBaseUrl; | |
} | |
private validateParams() { | |
if (!this.httpHandler) { | |
throw new Error("httpHandler is required"); | |
} | |
if (!this.baseUrl) { | |
throw new Error("baseUrl is required"); | |
} | |
} | |
private setupInterceptors() { | |
this.httpHandler.interceptors.request.use((config) => { | |
if (this.isDebugMode) { | |
const logStyle = "color: #2196F3; font-weight: bold;"; | |
const logMessage = `%cRequest: ${config.method?.toUpperCase()} ${ | |
config.url | |
}`; | |
const logMessageHeaders = `%cRequestHeaders: ${JSON.stringify( | |
config.headers, | |
null, | |
2 | |
)}`; | |
const logMessageData = `%cRequestData: ${JSON.stringify( | |
config.data, | |
null, | |
2 | |
)}`; | |
console.log(logMessage, logStyle); | |
console.log(logMessageHeaders, logStyle); | |
console.log(logMessageData, logStyle); | |
} | |
return config; | |
}); | |
this.httpHandler.interceptors.response.use( | |
(response) => { | |
if (this.isDebugMode) { | |
const { data } = response.data; | |
const logStyle = "color: #4CAF50; font-weight: bold;"; | |
const logMessageStatus = `%cResponseStatus: ${response.status} ${response.statusText}`; | |
const logMessageData = `%cResponseData: ${JSON.stringify( | |
data, | |
null, | |
2 | |
)}`; | |
console.log(logMessageStatus, logStyle); | |
console.log(logMessageData, logStyle); | |
} | |
return response; | |
}, | |
(error) => { | |
console.error(error); | |
return Promise.reject(error); | |
} | |
); | |
} | |
async request<B, R>(request: IHttpRequest<B>): Promise<IHttpResponse<R>> { | |
const { url = "", body = {}, headers = {}, method = "GET" } = request; | |
try { | |
const response = await this.httpHandler.request<R>({ | |
baseURL: this.baseUrl, | |
url, | |
method, | |
data: body, | |
headers, | |
}); | |
const data = { | |
data: response.data, | |
error: null, | |
}; | |
return data; | |
} catch (error: unknown) { | |
if (isAxiosError(error)) { | |
return this.handleAxiosError(error); | |
} else { | |
return this.handleUnexpectedError(error); | |
} | |
} | |
} | |
private handleAxiosError(error: AxiosError) { | |
return { | |
data: null, | |
error: { | |
name: error.name, | |
message: error.message, | |
status: error.response?.status || 400, | |
details: null, | |
}, | |
}; | |
} | |
private handleUnexpectedError(error: unknown) { | |
return { | |
data: null, | |
error: { | |
name: "UnexpectedError", | |
message: "An unexpected error occurred", | |
status: 500, | |
details: JSON.stringify(error), | |
}, | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment