Can NestJS HttpService be configured with a global custom DNS resolver?
0 reputation · 21 Feb 2022, 02:51 UTC
0 reputation · 21 Feb 2022, 02:51 UTC
When building a NestJS application that relies on the injected HttpService for outbound HTTPS calls, I need to ensure that all requests use a specific DNS resolver (e.g., an internal DNS server) and optionally relax certificate validation for internal services with self‑signed certificates. The HttpService is a thin wrapper around Axios, which ultimately uses Node’s built‑in http/https modules; NestJS does not expose a global option to override the DNS lookup function or to toggle rejectUnauthorized centrally. Configuration must be supplied per‑instance via a custom https.Agent, either when registering HttpModule or by overriding the HttpService’s underlying axios instance.
Is there a supported way to register a custom DNS lookup function (or a custom https.Agent) that applies to every HttpService instance without manually providing it each time? Can Node.js environment variables such as NODE_OPTIONS=--dns-result-order be relied upon within a NestJS process to affect DNS resolution globally, and if so, how should they be integrated safely? What patterns or provider overrides are recommended to achieve a centralized DNS/TLS configuration while preserving type safety and testability?
34775 reputation · 21 Feb 2022, 09:54 UTC
Yes – you can make every injected HttpService use a custom DNS resolver by supplying a pre‑configured Axios instance that contains an https.Agent (and optionally an http.Agent) with a custom lookup function. NestJS does not expose a global flag; the resolver must be baked into the agent that the HttpService’s underlying Axios instance uses.
httpAgent and httpsAgent.http.Agent and https.Agent accept a lookup option (available since Node 0.11.14) that replaces the default DNS resolution.HttpModule with a custom Axios instance via HttpModule.register({ axios: customInstance }), every subsequent HttpService injection receives that instance, so the custom resolver applies globally.const dns = require('dns');
function customLookup(hostname, options, callback) {
// Use your internal DNS servers (e.g., 10.0.0.53)
const servers = ['10.0.0.53'];
dns.setServers(servers); // optional, affects fallback
dns.resolve4(hostname, { ...options, servers }, (err, addresses) => {
if (err) return callback(err);
callback(null, addresses, 4); // family 4 for IPv6 use resolve6 similarly
});
}
https.Agent (and http.Agent if you need plain HTTP) that uses this lookup:const https = require('https');
const http = require('http');
const httpsAgent = new https.Agent({ lookup: customLookup, keepAlive: true });
const httpAgent = new http.Agent({ lookup: customLookup, keepAlive: true });
const axios = require('axios');
const customAxios = axios.create({
httpAgent,
httpsAgent,
// optional: relax TLS for internal services
httpsAgent: { ...httpsAgent, rejectUnauthorized: false },
});
AppModule):@Module({
imports: [
HttpModule.register({
axios: customAxios,
}),
// other modules…
],
})
export class AppModule {}
HttpService injected throughout the app will use the custom DNS resolver.The approach above relies on the lookup option being honoured by the Node version you run. If you are on an older Node (< 0.11.14) the agent will ignore the custom function and fall back to the system resolver. Verify your Node version; if it is insufficient, you would need to upgrade or use a global dns.setServers call (which only affects the dns module, not the underlying c‑ares resolver used by http/https in some builds).
HttpService in a test controller, make a request to a known host, and temporarily replace dns.resolve4 with a mock that returns a specific IP; assert the mock was called.httpService.axiosRef.defaults.httpsAgent.lookup should reference your customLookup function.tcpdump -n port 53 to see DNS queries directed to your internal DNS server IP.If you are unsure about the Node version in your deployment, please confirm the exact version so we can advise whether the lookup option is guaranteed to work.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 21 Feb 2022, 03:05 UTC
You can make the DNS resolver global by overriding Node’s dns.lookup (or dns.promises.lookup) during application bootstrap. In a NestJS module, implement OnApplicationBootstrap and call dns.setDefaultResultOrder('ipv4first') or assign a custom function to dns.lookup. This affects every outgoing request from HttpService because Axios ultimately relies on Node’s built‑in DNS. For TLS, pair this with a custom https.Agent provided when registering HttpModule so the same agent is reused across all injected HttpService instances.