Introduction
Most applications need to call external APIs. NestJS wraps Axios in the HttpModule, providing a configured, injectable HttpService that returns Observables and integrates seamlessly with the NestJS ecosystem.
Key Concepts
- HttpModule: Module that provides HttpService
- HttpService: Injectable wrapper around Axios
- Observable: Returns Observables (convert to Promise if needed)
- Interceptors: Axios interceptors for request/response modification
Real World Context
Modern backends often aggregate data from multiple external APIs — payment gateways, email services, analytics. HttpModule provides a consistent, testable interface for all outbound HTTP calls with built-in timeout and retry capabilities.
Deep Dive
Setup
bashnpm install @nestjs/axios axios
typescriptimport { HttpModule } from '@nestjs/axios'; @Module({ imports: [HttpModule], providers: [ExternalApiService], }) export class AppModule {}
Basic Usage
typescriptimport { HttpService } from '@nestjs/axios'; import { firstValueFrom } from 'rxjs'; @Injectable() export class ExternalApiService { constructor(private httpService: HttpService) {} // Using Observable getDataObservable(): Observable<AxiosResponse<Data>> { return this.httpService.get<Data>('https://api.example.com/data'); } // Using Promise async getDataPromise(): Promise<Data> { const { data } = await firstValueFrom( this.httpService.get<Data>('https://api.example.com/data'), ); return data; } }
Configuration
typescriptHttpModule.register({ timeout: 5000, maxRedirects: 5, headers: { 'User-Agent': 'MyApp/1.0', }, })
Async Configuration
typescriptHttpModule.registerAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => ({ timeout: configService.get('HTTP_TIMEOUT'), baseURL: configService.get('API_BASE_URL'), headers: { 'X-API-Key': configService.get('API_KEY'), }, }), inject: [ConfigService], })
Error Handling
typescriptimport { catchError, map } from 'rxjs/operators'; import { AxiosError } from 'axios'; async fetchData(): Promise<Data> { try { const { data } = await firstValueFrom( this.httpService.get<Data>('/endpoint').pipe( catchError((error: AxiosError) => { this.logger.error('API call failed', error.response?.data); throw new HttpException('External API error', HttpStatus.BAD_GATEWAY); }), ), ); return data; } catch (error) { throw error; } }
Axios Interceptors
typescript@Injectable() export class ApiService implements OnModuleInit { constructor(private httpService: HttpService) {} onModuleInit() { this.httpService.axiosRef.interceptors.request.use((config) => { config.headers['X-Request-ID'] = uuid(); return config; }); this.httpService.axiosRef.interceptors.response.use( (response) => response, (error) => { this.logger.error('API Error', error.response?.status); return Promise.reject(error); }, ); } }
Common Pitfalls
- Observable vs Promise: HttpService returns Observables. Use firstValueFrom() for Promises.
- Unsubscribed Observables: Observables not subscribed don't execute.
- Missing error handling: Network errors can crash your app without try/catch.
Best Practices
- Use firstValueFrom() for simple request/response patterns
- Configure timeouts to prevent hanging requests
- Add logging interceptors for debugging
- Handle errors gracefully with meaningful messages
Summary
HttpModule provides HttpService for making HTTP requests. It returns Observables—use firstValueFrom() for Promise-style code. Configure timeouts, base URLs, and headers. Handle errors gracefully and use interceptors for cross-cutting concerns.
Code Examples
typescript
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
@Injectable()
export class ExternalApiService {
constructor(private httpService: HttpService) {}
async getData(): Promise<Data> {
const { data } = await firstValueFrom(
this.httpService.get<Data>('https://api.example.com/data'),
);
return data;
}
}