Spring til indhold

Guide

Adressefelt med autocomplete i din checkout

Et adressefelt, der foreslår rigtige danske adresser, fjerner de tastefejl, der senere bliver til returpakker og manuelle rettelser. Her er hele vejen: et debounced felt i frontend, et proxy-endpoint der holder nøglen på serveren, og ét kald der giver dig både visningsteksten og de felter, formularen skal udfyldes med.

Ét kald — ikke to

De fleste adresse-felter slår forslaget op to gange: først for at vise listen, og så igen for at hente de strukturerede felter, når brugeren har valgt. /v1/adresse/search returnerer begge dele på én gang, så valget kan udfylde formularen med det samme:

GET /v1/adresse/search?q=rentemestervej 10json
{
  "results": [
    {
      "betegnelse": "Rentemestervej 10, 2400 København NV",
      "adgangsadresse_id": "0a3f507a-e17e-32b8-e044-0003ba298018",
      "bfe_nummer": 6022755,
      "vejnavn": "Rentemestervej",
      "husnr": "10",
      "postnr": "2400",
      "postnrnavn": "København NV",
      "kommunekode": "0101",
      "koordinat": { "lon": 12.5347335, "lat": 55.7050242 }
    }
  ]
}

Bemærk adgangsadresse_id og bfe_nummer. Gem dem sammen med ordren i stedet for kun den fritekst, kunden så: id'et er stabilt, og bfe_nummer er nøglen til resten af ejendommen, hvis du senere får brug for den.

Frontend: felt med forslag

De to detaljer, der afgør, om feltet føles rigtigt: debounce, så hvert tastetryk ikke bliver et kald — og afbrydelse af det forrige kald, så et langsomt svar ikke overskriver et nyere.

adressefelt.jstypescript
// Adressefelt med forslag. Debounce, afbryd det forrige kald, og
// udfyld de øvrige felter ud fra det valgte forslag.
const input = document.querySelector('#adresse');
const liste = document.querySelector('#forslag');
let inflight;
let timer;

input.addEventListener('input', () => {
  clearTimeout(timer);
  timer = setTimeout(async () => {
    const q = input.value.trim();
    if (q.length < 2) return (liste.innerHTML = '');

    // Afbryd det forrige kald, så et langsomt svar ikke overskriver et nyere.
    inflight?.abort();
    inflight = new AbortController();

    // Kald dit eget backend-endpoint — nøglen må aldrig ligge i browseren.
    const res = await fetch('/api/adresse?q=' + encodeURIComponent(q), {
      signal: inflight.signal,
    });
    const { results } = await res.json();

    liste.innerHTML = '';
    for (const r of results) {
      const li = document.createElement('li');
      li.textContent = r.betegnelse;
      li.addEventListener('click', () => {
        input.value = r.betegnelse;
        // Felterne kommer med i samme svar — ingen ekstra opslag.
        form.vejnavn.value = r.vejnavn ?? '';
        form.husnr.value = r.husnr ?? '';
        form.postnr.value = r.postnr ?? '';
        form.by.value = r.postnrnavn ?? '';
        // Gem nøglerne: adgangsadresse_id er stabil, bfe_nummer peger på ejendommen.
        form.adresse_id.value = r.adgangsadresse_id;
        liste.innerHTML = '';
      });
      liste.append(li);
    }
  }, 150);
});

Backend: hold nøglen på serveren

En API-nøgle i browseren kan læses af enhver besøgende og bruges på din regning. Læg et lille proxy-endpoint i din egen backend — så bliver nøglen, hvor den hører til, og du kan cache og rate-limite, før kaldene rammer os.

server.jstypescript
// Proxy i din backend. Nøglen bliver på serveren, og du kan
// cache + rate-limite, før kaldene rammer os.
app.get('/api/adresse', async (req, res) => {
  const q = String(req.query.q ?? '');
  if (q.length < 2) return res.json({ results: [] });

  const upstream = await fetch(
    'https://api.grundfast.dk/v1/adresse/search?per_side=8&q=' +
      encodeURIComponent(q),
    { headers: { Authorization: 'Bearer ' + process.env.GRUNDFAST_KEY } },
  );
  if (!upstream.ok) return res.status(502).json({ results: [] });

  res.set('Cache-Control', 'private, max-age=60');
  res.json(await upstream.json());
});

De adresser, du allerede har

Et nyt felt retter kun fremtidige ordrer. Kartoteket bagud kan vaskes mod DAR i stedet for at blive tastet om — du får rangerede kandidater med A/B/C-konfidensbånd og bfe_nummer, så det sikre kan rettes automatisk og resten sendes til et menneske.

datavaskbash
# Har du allerede en liste med adresser — fx et gammelt kundekartotek —
# så vask dem i stedet for at få dem tastet igen.
curl -sS "https://api.grundfast.dk/v1/datavask/adgangsadresser?betegnelse=rentemestervej%2010%2C%202400%20kbh%20nv" \
  -H "Authorization: Bearer $GRUNDFAST_KEY"

Byg det på en test-nøgle

En gf_test_-nøgle giver rigtige svar, faktureres aldrig og har 25.000 kald/md. Byg feltet færdigt, og skift nøgle når du går i produktion.

Se også

Ofte stillede spørgsmål

Skal jeg bruge /v1/autocomplete eller /v1/adresse/search?

Til et checkout-felt: /v1/adresse/search. Den returnerer de strukturerede felter (vejnavn, husnr, postnr, postnrnavn, kommunekode, koordinat og bfe_nummer) direkte i svaret, så du kan udfylde formularen uden et ekstra opslag. /v1/autocomplete giver tekst + vaerdi og er tænkt som DAWA-kompatibel drop-in, hvor du selv slår værdien op bagefter.

Må API-nøglen ligge i frontend-koden?

Nej. En nøgle i browseren kan læses af enhver besøgende og bruges på din regning. Læg et lille proxy-endpoint i din egen backend, som sætter Authorization-headeren — så kan du samtidig cache og rate-limite, før kaldene rammer os.

Hvor mange kald bliver et checkout-felt til?

Med 150 ms debounce og afbrydelse af det forrige kald lander en typisk adresse-indtastning på nogle få kald. Free-planen er 5.000 kald/md, hvilket rækker til et mindre checkout-flow; derover fortsætter betalte planer automatisk til 1 kr/1.000 ekstra kald.

Kan jeg teste uden at betale?

Ja. En gf_test_-nøgle giver rigtige svar, faktureres aldrig og har 25.000 kald/md. Byg feltet færdigt på test-nøglen, og skift til en gf_live_-nøgle, når du går i produktion.

Hvad gør jeg med de adresser, jeg allerede har liggende?

Vask dem mod DAR med /v1/datavask/adgangsadresser. Du får rangerede kandidater med A/B/C-konfidensbånd og bfe_nummer, så du kan rette de sikre automatisk og lade et menneske se på resten. Har du mange, tager :batch-varianten dem i ét kald.