Skip to content
LogoLogo

@portable/dns

Functions

Dns.isNotFound

Whether a lookup failed because there are no records to return: the name doesn't exist (ENOTFOUND), has no records of the type (ENODATA), or can't be a DNS name (EBADNAME). It reads code, so it works for a QueryError from any copy of this package and for errors from node:dns/promises. Any other failure is temporary, and a caller can retry it.

function isNotFound(error: unknown): boolean;
import * as Dns from '@portable/dns';
 
const resolver = Dns.create();
const records = await resolver
  .resolveTxt('_dmarc.example.com')
  .catch((error) => {
    if (Dns.isNotFound(error)) {
      return [];
    }
    throw error;
  });

Dns.create

Creates a resolver that sends DNS queries over HTTPS (RFC 8484) with fetch, so lookups work in browsers, Workers and other runtimes without a system resolver. Queries are GET requests with a DNS ID of 0, which HTTP caches can store and which need no CORS preflight. Results and error codes match node:dns/promises, and CNAME chains in the answer are followed.

function create(options: create.Options = {}): DohResolver;
OptionTypeDefaultDescription
endpointsreadonly (string | URL)[]['https://cloudflare-dns.com/dns-query', 'https://dns.google/dns-query']DNS-over-HTTPS endpoints (RFC 8484), tried in order. The next one is tried when a request fails, times out, or gets SERVFAIL, REFUSED, NOTIMP, FORMERR or a truncated response.
fetch((request: Request) => Promise<Response>)globalThis.fetchSends each request. Pass one to add headers, route through a proxy, or mock in tests.
timeoutnumber5000Milliseconds to wait for each endpoint, including reading the response body.
import * as Dns from '@portable/dns';
 
const resolver = Dns.create({ timeout: 2000 });
const records = await resolver.resolveTxt('example.com');
const spf = records
  .map((chunks) => chunks.join(''))
  .find((text) => text.startsWith('v=spf1'));

Errors

Dns.QueryError

Thrown when a lookup fails. code, syscall, hostname and message have the values node:dns/promises gives them, such as queryTxt ENOTFOUND example.com. When the failure came from fetch or the response, cause holds the original error.

class QueryError extends Errors.BaseError {
  constructor(options: QueryError.Options);
  readonly kind: string;
  readonly code: QueryError.Code;
  readonly syscall: string;
  readonly hostname: string;
}
OptionTypeDefaultDescription
causeunknown
codeCode
syscallstringThe node:dns name of the operation.
hostnamestringThe name or address the caller passed.
import * as Dns from '@portable/dns';
 
const resolver = Dns.create();
try {
  await resolver.resolveTxt('_dmarc.example.com');
} catch (error) {
  if (error instanceof Dns.QueryError && error.code === 'ENODATA') {
    console.log('no DMARC record');
  }
}

Types

Dns.CaaRecord

A certification authority authorization, as resolveCaa returns it. critical is the whole flags byte (128 when the issuer-critical bit is set), and the tag is the key of the value. A tag not listed here is still set on the object.

type CaaRecord = {
  critical: number;
  issue?: string | undefined;
  issuewild?: string | undefined;
  iodef?: string | undefined;
  contactemail?: string | undefined;
  contactphone?: string | undefined;
};

Dns.DohResolver

The resolver that create returns: every Resolver lookup, and query for any record type.

type DohResolver = Resolver & {
  query(
    name: string,
    type: DnsPacket.RecordType,
    options?: Resolver.CallOptions,
  ): Promise<DnsPacket.DecodedPacket>;
};

Dns.MxRecord

A mail exchange, as resolveMx returns it.

type MxRecord = {
  exchange: string;
  priority: number;
};

Dns.NaptrRecord

A naming authority pointer, as resolveNaptr returns it.

type NaptrRecord = {
  flags: string;
  service: string;
  regexp: string;
  replacement: string;
  order: number;
  preference: number;
};

Dns.Resolver

The lookups that SPF, DMARC and DKIM need, with the method names and result shapes of node:dns/promises. create returns one, and node:dns/promises, a new Resolver() from it and a plain object of functions in a test also match the type.

type Resolver = {
  resolve4(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<string[]>;
  resolve6(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<string[]>;
  resolveCname(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<string[]>;
  resolveMx(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<MxRecord[]>;
  resolveNs(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<string[]>;
  resolvePtr(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<string[]>;
  resolveTxt(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<string[][]>;
  resolveSrv(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<SrvRecord[]>;
  resolveSoa(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<SoaRecord>;
  resolveCaa(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<CaaRecord[]>;
  resolveNaptr(
    name: string,
    options?: Resolver.CallOptions,
  ): Promise<NaptrRecord[]>;
  reverse(
    ip: string,
    options?: Resolver.CallOptions,
  ): Promise<string[]>;
};

Dns.SoaRecord

A start of authority, as resolveSoa returns it.

type SoaRecord = {
  nsname: string;
  hostmaster: string;
  serial: number;
  refresh: number;
  retry: number;
  expire: number;
  minttl: number;
};

Dns.SrvRecord

A service location, as resolveSrv returns it. name is the target host.

type SrvRecord = {
  name: string;
  port: number;
  priority: number;
  weight: number;
};