Skip to content

Commit 3d7d722

Browse files
committed
feat: implement per-request caching for DNS resolution
1 parent 668f171 commit 3d7d722

File tree

3 files changed

+37
-6
lines changed

3 files changed

+37
-6
lines changed

server/services/dns.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { eq } from "drizzle-orm";
22
import { after } from "next/server";
3+
import { cache } from "react";
34
import { isCloudflareIp } from "@/lib/cloudflare";
45
import { USER_AGENT } from "@/lib/constants/app";
56
import { db } from "@/lib/db/client";
@@ -70,7 +71,16 @@ function buildDohUrl(
7071
return url;
7172
}
7273

73-
export async function resolveAll(domain: string): Promise<DnsResolveResult> {
74+
/**
75+
* Resolve all DNS record types for a domain with Postgres caching.
76+
*
77+
* Wrapped in React's cache() for per-request deduplication during SSR,
78+
* ensuring multiple services can query DNS without triggering duplicate
79+
* lookups to DoH providers.
80+
*/
81+
export const resolveAll = cache(async function resolveAll(
82+
domain: string,
83+
): Promise<DnsResolveResult> {
7484
console.debug(`[dns] start ${domain}`);
7585

7686
const providers = providerOrderForLookup(domain);
@@ -396,7 +406,7 @@ export async function resolveAll(domain: string): Promise<DnsResolveResult> {
396406
throw new Error(
397407
`All DoH providers failed for ${registrable}: ${String(lastError)}`,
398408
);
399-
}
409+
});
400410

401411
async function resolveTypeWithProvider(
402412
domain: string,

server/services/hosting.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { eq } from "drizzle-orm";
22
import { alias } from "drizzle-orm/pg-core";
33
import { after } from "next/server";
4+
import { cache } from "react";
45
import { db } from "@/lib/db/client";
56
import { findDomainByName } from "@/lib/db/repos/domains";
67
import { upsertHosting } from "@/lib/db/repos/hosting";
@@ -25,7 +26,16 @@ import { resolveAll } from "@/server/services/dns";
2526
import { probeHeaders } from "@/server/services/headers";
2627
import { lookupIpMeta } from "@/server/services/ip";
2728

28-
export async function detectHosting(domain: string): Promise<Hosting> {
29+
/**
30+
* Detect hosting, email, and DNS providers for a domain with Postgres caching.
31+
*
32+
* Wrapped in React's cache() for per-request deduplication during SSR,
33+
* ensuring multiple components can query hosting without triggering duplicate
34+
* fetches of DNS and headers data.
35+
*/
36+
export const detectHosting = cache(async function detectHosting(
37+
domain: string,
38+
): Promise<Hosting> {
2939
console.debug(`[hosting] start ${domain}`);
3040

3141
// Only support registrable domains (no subdomains, IPs, or invalid TLDs)
@@ -275,4 +285,4 @@ export async function detectHosting(domain: string): Promise<Hosting> {
275285
`[hosting] ok ${registrable} hosting=${hostingName} email=${emailName} dns=${dnsName}`,
276286
);
277287
return info;
278-
}
288+
});

server/services/ip.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
export async function lookupIpMeta(ip: string): Promise<{
1+
import { cache } from "react";
2+
3+
/**
4+
* Lookup IP metadata including geolocation and ownership information.
5+
*
6+
* Wrapped in React's cache() for per-request deduplication during SSR,
7+
* ensuring multiple services can query the same IP without triggering
8+
* duplicate API calls to the upstream provider.
9+
*/
10+
export const lookupIpMeta = cache(async function lookupIpMeta(
11+
ip: string,
12+
): Promise<{
213
geo: {
314
city: string;
415
region: string;
@@ -94,4 +105,4 @@ export async function lookupIpMeta(ip: string): Promise<{
94105
},
95106
};
96107
}
97-
}
108+
});

0 commit comments

Comments
 (0)