npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@grundfast/sdk

v0.17.2

Published

TypeScript SDK for the Grundfast API — Danish property & geodata (BBR, Matriklen).

Readme

@grundfast/sdk

Official TypeScript SDK for the Grundfast API — the convenient old services.datafordeler.dk shape (translated kodelister, re-joined names, current-only data, WGS84 geometry, auto-pagination) over Denmark's new entity-based Datafordeler backend. Register #1 is BBR, #2 is Matriklen.

Full API reference: grundfast.dk/docs/sdk.

Installation

npm install @grundfast/sdk
# or
bun add @grundfast/sdk

Ships dual ESM + CommonJS with bundled type declarations — works in Node, Bun, and the browser.

Quick Start

import { GrundfastClient } from '@grundfast/sdk';

const gf = new GrundfastClient({ apiKey: 'gf_live_…' });

// Resolve one property by its BFE (Bestemt Fast Ejendom) number.
const ejendom = await gf.ejendom(5651067);
console.log(ejendom.jordstykke.ejerlav_navn, ejendom.bygninger.length);

The client defaults to https://api.grundfast.dk. Override baseUrl (or inject a custom fetch) via the constructor options:

const gf = new GrundfastClient({
	apiKey: 'gf_live_…',
	baseUrl: 'https://api.grundfast.dk',
	timeoutMs: 15000, // per-attempt abort timeout (default 15s; 0 disables)
	maxRetries: 2, // extra attempts on a transient failure (default 2; 0 disables)
	retryBaseMs: 250, // first backoff step, doubled per attempt + half-jittered
	retryCapMs: 5000, // ceiling on one backoff sleep (also caps Retry-After)
});

Every attempt is aborted after timeoutMs so a hung upstream never hangs your await — a timeout throws GrundfastError with status: 408.

Retries

Transient failures are retried with jittered exponential backoff, honouring a capped Retry-After. The policy is deliberately narrow:

  • Retried: 500, 502, 503 and the per-minute burst 429 — on GET requests and the idempotent :batch reads. A GET additionally retries 408, a network failure and its own timeout.
  • Never retried: the monthly-quota 429 (it means upgrade, not wait — its Retry-After can be weeks), any 4xx you can fix, a burst 429 asking to wait longer than retryCapMs, non-idempotent POSTs like createExport(), and a :batch POST whose request failed in transit — replaying one could charge your quota twice.

Cancellation

Every method takes a trailing { signal }, merged with the client's own timeout. Abort it to cancel the in-flight request and any pending retry backoff — what a type-ahead needs so a slow keystroke can't overwrite a newer result:

let inFlight: AbortController | undefined;

async function onKeystroke(q: string) {
	inFlight?.abort(); // drop the superseded lookup
	inFlight = new AbortController();
	try {
		const { results } = await gf.adresseSearch(q, 8, { signal: inFlight.signal });
		render(results);
	} catch (err) {
		if ((err as Error).name === 'AbortError') return; // superseded — not an error
		throw err;
	}
}

An abort rejects with the signal's own reason (an AbortError), never with GrundfastError(408) — that status is reserved for the client's own timeout.

Try it without a key

The demo endpoints run offline from a bundled fixture — no API key required:

const gf = new GrundfastClient();
const demo = await gf.demoEjendom();
const fc = await gf.demoEjendomGeoJson(); // WFS-compatible GeoJSON FeatureCollection
const koder = await gf.kodeliste('varmeinstallation'); // translated code list

Keyed endpoints

const gf = new GrundfastClient({ apiKey: 'gf_live_…' });

// Clean, re-joined property rollup.
const ejendom = await gf.ejendom(5651067);

// Same property as a WFS-compatible GeoJSON FeatureCollection (one Feature per building).
const geojson = await gf.ejendomGeoJson(5651067);

// Matriklen (cadastre) rollup.
const matrikel = await gf.matrikel(5651067);

// Building(s) for a BFE — same rollup shape as ejendom() (resolved from the same join).
const bygning = await gf.bygning(5651067);

// Per-building GeoDanmark footprint polygons (the real outlines BBR can't give).
const footprints = await gf.geodanmarkBygninger(5651067);
console.log(footprints.buildings[0]?.rings, footprints.attribution); // CC BY 4.0 credit

// The two arealudpegninger, on a WGS84 coordinate. Both answer a MEASURED yes/no:
// a `false` never comes from an upstream fault — the call fails instead — because a
// "no" is the answer somebody digs or builds on.
const jord = await gf.jordOmraadeklassificering(10.1763672, 56.1327761);
jord.omraadeklassificeret; // § 50 a covers the point
jord.analysefri; // true | false | null — null means "not covered", never "unknown"

const natur = await gf.naturSkovbyggelinje(8.5286062, 55.8988908);
natur.skovbyggelinje; // a line that STANDS covers the point (§ 17, 300 m from forest)
natur.ophaevet; // a separate question — a point can sit in both, and then the line applies
natur.ophaevede; // the lifted polygons: why, and the kommune's own afgørelse link
natur.usikker; // true = no ADOPTED registration behind the yes — confirm with the kommune

Scope

This SDK is a typed data-access client for the read endpoints — every live register (BBR, Matriklen, DAGI, DHM, Stednavne, DAR/adresse, CVR, jordforurening/områdeklassificering, naturbeskyttelse/skovbyggelinjer), the DAWA-compatible drop-in layer (postnumre, vejnavne, ejerlav, autocomplete, datavask, reverse-geocoding), GeoDanmark building footprints, kodelister, point-in-time lookups (?asOf/historik), and the batch + bulk-export helpers. Account, API-key, and billing management are done in the dashboard (you mint keys there), so the SDK intentionally exposes no auth/keys/me/billing methods.

Batch & streaming

// Up to 50 BFEs per call. Partial-failure aware: each BFE lands in results or errors,
// and the call itself succeeds even if some BFEs fail. One billable unit per deduped BFE.
const { results, errors } = await gf.ejendomBatch([5651067, 12345678]);

// Auto-paginate an arbitrarily large BFE array: chunks into <=50-sized batch calls
// and yields each successfully-resolved property (failures are skipped — use
// ejendomBatch directly if you need to inspect them).
for await (const item of gf.ejendomStream(thousandsOfBfes)) {
	console.log(item.bfe, item.ejendom.antal_bygninger, item.stale);
}

Error handling

Every non-2xx response (and a request timeout, as status: 408) throws a GrundfastError carrying the HTTP status — after the retry policy above has given up. On a 429 the retryAfter field holds the Retry-After seconds, so you can back off over the waits the client deliberately won't sleep on itself:

import { GrundfastClient, GrundfastError } from '@grundfast/sdk';

try {
	await gf.ejendom(999999999);
} catch (err) {
	if (err instanceof GrundfastError) {
		console.error(err.status, err.message); // e.g. 404 "property not found"
		if (err.status === 429 && err.retryAfter) {
			await new Promise((r) => setTimeout(r, err.retryAfter * 1000));
		}
	}
}

Migrating from DAWA

DAWA (Danmarks Adressers Web API) closes 1 October 2026 at 10:00. The date moved twice — it was announced for 1 July, then 17 August — so older write-ups still quote the earlier ones; the current date is confirmed in Dataforsyningen's announcement (updated 12 June 2026).

Address identifiers come from DAR in both cases, so stored address UUIDs keep resolving — there is no re-keying step. Point the base URL at https://api.grundfast.dk/v1 and send a bearer key:

| DAWA | Grundfast | SDK | | ------------------------------------- | ------------------------------- | ------------------------------------ | | /autocomplete | /v1/autocomplete | gf.autocomplete(q) | | /adgangsadresser/{id} | /v1/adresse/{uuid} | gf.adresse(uuid) | | /adgangsadresser/reverse | /v1/adgangsadresser/reverse | gf.adgangsadresseReverse(lon, lat) | | /datavask/adgangsadresser | /v1/datavask/adgangsadresser | gf.datavask(text) | | /jordstykker/reverse | /v1/jordstykker/reverse | gf.jordstykkeReverse(lon, lat) | | /postnumre | /v1/postnumre | gf.postnumre() | | /vejnavne | /v1/vejnavne | gf.vejnavne() | | /ejerlav | /v1/ejerlav | gf.ejerlav() | | /kommuner, /regioner, /sogne, … | /v1/dagi/{tema} | gf.dagiList(tema) | | — (not in DAWA) | /v1/adgangsadresser/bfe/{bfe} | gf.adgangsadresserByBfe(bfe) |

DAWA was keyless; Grundfast requires a key and meters usage. Beyond the like-for-like swap, every address carries the BFE number of its property, so the same lookup continues into BBR buildings and units and Matriklen parcels — see gf.ejendom(bfe).

Full endpoint-by-endpoint detail: grundfast.dk/en/dawa-replacement.

AI agents (MCP)

Grundfast also runs a Model Context Protocol server, published in the official MCP registry as dk.grundfast/danish-address-property-data. It is a streamable-HTTP remote at https://api.grundfast.dk/mcp with the same bearer key, exposing one read-only tool per register. See grundfast.dk/en/mcp-server.

Links