Skip to content

WHOIS & RDAP Lookup — JavaScript source

Look up registration data for any domain, IP address, or ASN — straight from your browser to the authoritative RDAP registry over HTTPS. No backend, no database of lookups, fully private.

This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.

/**
 * RDAP lookup — WHOIS registration data over HTTPS (RFCs 7480-7485).
 *
 * Language: JavaScript (ES module, Fetch API — Node 18+ or browser)
 * CosmoDev polyglot showcase port of the `whois` tool.
 * Ported from src/lib/whois.ts — display source, part of CosmoDev's
 * polyglot tool pages.
 *
 * RDAP is the JSON successor to port-43 WHOIS. The client first asks
 * IANA's bootstrap registry which server is authoritative for the TLD
 * (dns.json) or the ASN block (asn.json), then fetches the record
 * straight from that server. Two plain HTTPS GETs — no WHOIS port, no
 * backend. RDAP servers send permissive CORS headers, so this exact
 * code runs in a browser (which is how the CosmoDev tool itself works).
 */

// IANA's bootstrap registries: match keys -> authoritative RDAP servers.
const BOOTSTRAP_URLS = {
  domain: 'https://data.iana.org/RDAP/dns.json',
  autnum: 'https://data.iana.org/RDAP/asn.json',
};

/** Classify a query: an AS number ("as3356" / "3356") or a domain name. */
function classify(query) {
  const m = /^(?:as)?(\d{1,10})$/i.exec(query.trim());
  if (m && Number(m[1]) > 0) return { kind: 'autnum', value: String(Number(m[1])) };
  const value = query.trim().toLowerCase().replace(/\.+$/, '');
  return { kind: 'domain', value, tld: value.split('.').pop() };
}

/** True when `asn` falls inside an asn.json key ("lo-hi" or a bare number). */
function asnInRange(key, asn) {
  const [lo, hi] = key.split('-');
  return hi === undefined ? Number(lo) === asn : asn >= Number(lo) && asn <= Number(hi);
}

/** Ask IANA's bootstrap which RDAP server is authoritative for the query. */
async function resolveRdapBase(q) {
  const { services } = await (await fetch(BOOTSTRAP_URLS[q.kind])).json();
  for (const [keys, urls] of services) {
    const hit =
      q.kind === 'autnum'
        ? keys.some((k) => asnInRange(k, Number(q.value)))
        : keys.some((k) => k.toLowerCase() === q.tld);
    if (hit) return urls[0];
  }
  throw new Error(`No RDAP server for ${q.value}`);
}

/** The eventDate of one action ("registration", "expiration"), if present. */
function eventDate(events, action) {
  return events?.find((e) => e.eventAction?.toLowerCase() === action)?.eventDate;
}

/** Registrar name/email live in an entity with role "registrar", in a jCard. */
function registrarInfo(entities) {
  const props = entities?.find((e) => e.roles?.includes('registrar'))?.vcardArray?.[1] ?? [];
  const first = (kind) => props.find((p) => p[0] === kind)?.[3];
  return { name: first('fn'), email: first('email') };
}

/**
 * Look up a domain or ASN over RDAP and return the essentials.
 *
 * @returns {Promise<{registrar: {name?: string, email?: string},
 *                    registered?: string, expires?: string,
 *                    status: string[], nameservers: string[]}>}
 * @throws {Error} On network failure, unknown TLD/ASN, or a non-2xx response.
 */
export async function whoisLookup(query) {
  const q = classify(query);
  const base = await resolveRdapBase(q);
  const res = await fetch(`${base.replace(/\/+$/, '')}/${q.kind}/${q.value}`);
  if (!res.ok) throw new Error(`RDAP server returned HTTP ${res.status}`);
  const rdap = await res.json();
  return {
    registrar: registrarInfo(rdap.entities),
    registered: eventDate(rdap.events, 'registration'),
    expires: eventDate(rdap.events, 'expiration'),
    status: rdap.status ?? [],
    nameservers: (rdap.nameservers ?? [])
      .map((ns) => ns.ldhName)
      .filter(Boolean),
  };
}

// Usage:
//   const record = await whoisLookup('cosmolabs.org');
//   console.log(record.registrar, record.registered, record.expires);

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →