-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Added an robust and functional http service using axios
- Loading branch information
Showing
4 changed files
with
284 additions
and
5 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { HttpService } from './http.service'; | ||
import { HttpService as NestHttpService, HttpModule } from '@nestjs/axios'; | ||
import { AxiosResponse, InternalAxiosRequestConfig } from 'axios'; | ||
import { of, throwError } from 'rxjs'; | ||
import { HttpException } from '@nestjs/common'; | ||
|
||
describe('HttpService', () => { | ||
let service: HttpService; | ||
let httpService: NestHttpService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
imports: [HttpModule], | ||
providers: [HttpService], | ||
}).compile(); | ||
|
||
service = module.get<HttpService>(HttpService); | ||
httpService = module.get<NestHttpService>(NestHttpService); | ||
}); | ||
|
||
describe('get', () => { | ||
it('should return data on successful GET request', async () => { | ||
const result: AxiosResponse = { | ||
data: { message: 'success' }, | ||
status: 200, | ||
statusText: 'OK', | ||
headers: {}, | ||
config: {} as InternalAxiosRequestConfig, | ||
}; | ||
jest.spyOn(httpService, 'get').mockImplementation(() => of(result)); | ||
|
||
expect(await service.get('https://example.com')).toEqual(result); | ||
}); | ||
|
||
it('should throw an HttpException on GET request failure', async () => { | ||
jest.spyOn(httpService, 'get').mockImplementation(() => | ||
throwError({ | ||
response: { | ||
status: 404, | ||
data: 'Not Found', | ||
}, | ||
}), | ||
); | ||
|
||
await expect(service.get('https://example.com')).rejects.toThrow( | ||
new HttpException( | ||
{ | ||
status: 404, | ||
message: 'Not Found', | ||
}, | ||
404, | ||
), | ||
); | ||
}); | ||
}); | ||
|
||
describe('post', () => { | ||
it('should return data on successful POST request', async () => { | ||
const result: AxiosResponse = { | ||
data: { message: 'success' }, | ||
status: 201, | ||
statusText: 'Created', | ||
headers: {}, | ||
config: {} as InternalAxiosRequestConfig, | ||
}; | ||
jest.spyOn(httpService, 'post').mockImplementation(() => of(result)); | ||
|
||
expect(await service.post('https://example.com', {})).toEqual(result); | ||
}); | ||
|
||
it('should throw an HttpException on POST request failure', async () => { | ||
jest.spyOn(httpService, 'post').mockImplementation(() => | ||
throwError({ | ||
response: { | ||
status: 400, | ||
data: 'Bad Request', | ||
}, | ||
}), | ||
); | ||
|
||
await expect(service.post('https://example.com', {})).rejects.toThrow( | ||
new HttpException( | ||
{ | ||
status: 400, | ||
message: 'Bad Request', | ||
}, | ||
400, | ||
), | ||
); | ||
}); | ||
}); | ||
|
||
describe('put', () => { | ||
it('should return data on successful PUT request', async () => { | ||
const result: AxiosResponse = { | ||
data: { message: 'success' }, | ||
status: 200, | ||
statusText: 'OK', | ||
headers: {}, | ||
config: {} as InternalAxiosRequestConfig, | ||
}; | ||
jest.spyOn(httpService, 'put').mockImplementation(() => of(result)); | ||
|
||
expect(await service.put('https://example.com', {})).toEqual(result); | ||
}); | ||
|
||
it('should throw an HttpException on PUT request failure', async () => { | ||
jest.spyOn(httpService, 'put').mockImplementation(() => | ||
throwError({ | ||
response: { | ||
status: 401, | ||
data: 'Unauthorized', | ||
}, | ||
}), | ||
); | ||
|
||
await expect(service.put('https://example.com', {})).rejects.toThrow( | ||
new HttpException( | ||
{ | ||
status: 401, | ||
message: 'Unauthorized', | ||
}, | ||
401, | ||
), | ||
); | ||
}); | ||
}); | ||
|
||
describe('delete', () => { | ||
it('should return data on successful DELETE request', async () => { | ||
const result: AxiosResponse = { | ||
data: { message: 'success' }, | ||
status: 200, | ||
statusText: 'OK', | ||
headers: {}, | ||
config: {} as InternalAxiosRequestConfig, | ||
}; | ||
jest.spyOn(httpService, 'delete').mockImplementation(() => of(result)); | ||
|
||
expect(await service.delete('https://example.com')).toEqual(result); | ||
}); | ||
|
||
it('should throw an HttpException on DELETE request failure', async () => { | ||
jest.spyOn(httpService, 'delete').mockImplementation(() => | ||
throwError({ | ||
response: { | ||
status: 403, | ||
data: 'Forbidden', | ||
}, | ||
}), | ||
); | ||
|
||
await expect(service.delete('https://example.com')).rejects.toThrow( | ||
new HttpException( | ||
{ | ||
status: 403, | ||
message: 'Forbidden', | ||
}, | ||
403, | ||
), | ||
); | ||
}); | ||
}); | ||
}); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; | ||
import { HttpService as NestHttpService } from '@nestjs/axios'; | ||
import { AxiosRequestConfig, AxiosResponse } from 'axios'; | ||
import { catchError, lastValueFrom } from 'rxjs'; | ||
|
||
@Injectable() | ||
export class HttpService { | ||
constructor(private readonly httpService: NestHttpService) {} | ||
|
||
async get(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> { | ||
return this.handleRequest(this.httpService.get(url, config)); | ||
} | ||
|
||
async post( | ||
url: string, | ||
data: any, | ||
config?: AxiosRequestConfig, | ||
): Promise<AxiosResponse> { | ||
return this.handleRequest(this.httpService.post(url, data, config)); | ||
} | ||
|
||
async put( | ||
url: string, | ||
data: any, | ||
config?: AxiosRequestConfig, | ||
): Promise<AxiosResponse> { | ||
return this.handleRequest(this.httpService.put(url, data, config)); | ||
} | ||
|
||
async delete( | ||
url: string, | ||
config?: AxiosRequestConfig, | ||
): Promise<AxiosResponse> { | ||
return this.handleRequest(this.httpService.delete(url, config)); | ||
} | ||
|
||
private async handleRequest(request: any): Promise<AxiosResponse> { | ||
return await lastValueFrom( | ||
request.pipe( | ||
catchError((error) => { | ||
if (error.response) { | ||
throw new HttpException( | ||
{ | ||
status: error.response.status, | ||
message: error.response.data, | ||
}, | ||
error.response.status, | ||
); | ||
} else if (error.request) { | ||
throw new HttpException( | ||
{ | ||
status: HttpStatus.SERVICE_UNAVAILABLE, | ||
message: 'No response received from server', | ||
}, | ||
HttpStatus.SERVICE_UNAVAILABLE, | ||
); | ||
} else { | ||
throw new HttpException( | ||
{ | ||
status: HttpStatus.INTERNAL_SERVER_ERROR, | ||
message: error.message, | ||
}, | ||
HttpStatus.INTERNAL_SERVER_ERROR, | ||
); | ||
} | ||
}), | ||
), | ||
); | ||
} | ||
} |