NestJS requests to multiple services with different auth
How to handle customized @nest/axios HttpModule
NestJS is a popular backend framework built on Node.js.
Suppose your product is designed using a microservices architecture and uses NestJS for the Backend for Frontend (BFF). Due to its role, the BFF interacts with multiple services, which can include both internal servers and third-party SaaS providers. As a result, managing different types of authorizations simultaneously becomes essential.
In this article, I will share solutions for effectively handling these situations.
Sample code
https://github.com/maclt/sample-multiple-nest-http-auths
Overview
Step1. Set up customized HttpService
In this solution, we setup the customized HttpService.
Here is the example of HttpModule which use API Key to access to the backend server.
// src/common/module/apiKey.module.ts
import { Module, Global, OnModuleInit } from '@nestjs/common';
import { HttpModule, HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
export abstract class ApiKeyHttpService extends HttpService {}
interface EnvironmentVariables {
API_KEY: string;
}
@Global()
@Module({
imports: [HttpModule],
providers: [
{
provide: ApiKeyHttpService,
useExisting: HttpService,
},
],
exports: [ApiKeyHttpService],
})
export class ApiKeyHttpModule implements OnModuleInit {
constructor(
private httpService: HttpService,
private configService: ConfigService<EnvironmentVariables>,
) {}
onModuleInit() {
this.httpService.axiosRef.defaults.headers.common['x-api-key'] =
this.configService.get('API_KEY');
}
}
ApiKeyHttpService
… A customized HttpService which set the x-api-key
header.1
ApiKeyHttpModule
… A module which includes ApiKeyHttpService
and be imported to the appModule.
Step2. use customized HttpService accordingly
As a next step, import ApiKeyHttpService
into the repository, and use it according to the requirement of services.
// src/common/repository/sample.repository.ts
import { Injectable } from '@nestjs/common';
import { ApiKeyHttpService } from '../module/apiKey.module';
import { Auth0HttpService } from '../module/auth0Http.module';
import { Sigv4HttpService } from '../module/sigv4Http.modules';
@Injectable()
export class SampleRepository {
constructor(
private readonly apiKeyHttp: ApiKeyHttpService,
private readonly auth0Http: Auth0HttpService,
private readonly siv4Http: Sigv4HttpService,
) {}
async getFromServerProtectedByApiKey() {
return this.apiKeyHttp.get('https://order.api.example.com');
}
async getFromServerProtectedByAuth0() {
return this.auth0Http.get('https://payment.api.example.com');
}
async getFromServerProtectedBySigv4() {
return this.siv4Http.get('https://credit.api.example.com');
}
}
In this example, the order service requires API Key while the payment service requires AWS STS.
Variations
I share the variations of the sample code
Notes
In the sample code, I did not implement some important feature such as
Retry to the backend servers
Cache the credentials (access token of Auth0 / credential identity of STS)2
References
Because of how dependency injection works, if the NestJS app has only one HttpService
instance, the change to the HttpService
instance will be applied to all the other HTTP requests to backend services. So separated HttpService
for different authorizations are necessary.
These tokens have throttling limits, so you cannot make infinite requests for them. You should cache them until they expire.