Integraties

SDK's en plugins

We geven geen pakket uit dat je moet installeren en bijhouden. De API is één GET-verzoek met één header; daar heb je geen laag omheen voor nodig. Wat je hier vindt is werkende code die je rechtstreeks in je project plakt, met caching en foutafhandeling er al in.

In alle voorbeelden staat de publieke sandboxsleutel al ingevuld, dus ze doen het meteen. Vervang hem door je eigen sleutel als je live gaat.

De basis

Een adres opzoeken

Hetzelfde verzoek in negen talen, compleet met foutafhandeling.

curl -sS -i \
  -H "X-Api-Key: lat_test_demo_publiek_locatieapi_sandbox" \
  -H "Accept: application/json" \
  "https://api.locatieapi.nl/v3/lookup/1021JT/19"

WordPress

Een kleine plugin

Server-side aanroep achter een AJAX-actie, met een nonce en een transient als cache. Zet je sleutel in wp-config.php als constante LOCATIEAPI_KEY.

wp-content/plugins/locatieapi/locatieapi.php
<?php
/**
 * Plugin Name: LocatieAPI adresaanvulling
 * Description: Vult straat en plaats aan op basis van postcode en huisnummer.
 * Version: 1.0.0
 */

add_action('wp_ajax_locatieapi_lookup', 'locatieapi_lookup');
add_action('wp_ajax_nopriv_locatieapi_lookup', 'locatieapi_lookup');

function locatieapi_lookup()
{
    check_ajax_referer('locatieapi', 'nonce');

    $postcode = strtoupper(preg_replace('/\s+/', '', sanitize_text_field($_GET['postcode'] ?? '')));
    $number = (int) ($_GET['number'] ?? 0);

    $cacheKey = 'locatieapi_'.$postcode.'_'.$number;
    $cached = get_transient($cacheKey);

    if ($cached !== false) {
        wp_send_json($cached);
    }

    $response = wp_remote_get("https://api.locatieapi.nl/v3/lookup/{$postcode}/{$number}", [
        'timeout' => 5,
        'headers' => ['X-Api-Key' => LOCATIEAPI_KEY, 'Accept' => 'application/json'],
    ]);

    if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
        wp_send_json_error(['message' => 'Adres niet gevonden'], 404);
    }

    $address = json_decode(wp_remote_retrieve_body($response), true);

    set_transient($cacheKey, $address, DAY_IN_SECONDS);
    wp_send_json($address);
}

Zet je sleutel niet in een optie

Een optie die je in een template uitleest belandt zo in de broncode van je pagina. Gebruik een constante in wp-config.php, buiten de webroot als dat kan, en houd de aanroep server-side.

WooCommerce

Adresaanvulling in de checkout

Vult straat en plaats aan zodra postcode en huisnummer ingevuld zijn, via de AJAX-actie uit de plugin hierboven.

functions.php of je eigen plugin
<?php
/**
 * Vult in de WooCommerce-checkout straat en plaats aan zodra postcode en
 * huisnummer ingevuld zijn. De sleutel blijft server-side.
 */

add_action('woocommerce_after_checkout_form', function () {
    $nonce = wp_create_nonce('locatieapi');
    $endpoint = admin_url('admin-ajax.php');
    ?>
    <script>
    (function () {
        const postcode = document.getElementById('billing_postcode');
        const number = document.getElementById('billing_address_1');
        if (!postcode || !number) return;

        async function fill() {
            const pc = postcode.value.replace(/\s+/g, '').toUpperCase();
            const nr = parseInt(number.value, 10);
            if (!/^[0-9]{4}[A-Z]{2}$/.test(pc) || !nr) return;

            const url = new URL('<?php echo esc_js($endpoint); ?>', window.location.origin);
            url.searchParams.set('action', 'locatieapi_lookup');
            url.searchParams.set('nonce', '<?php echo esc_js($nonce); ?>');
            url.searchParams.set('postcode', pc);
            url.searchParams.set('number', String(nr));

            const response = await fetch(url);
            if (!response.ok) return;

            const address = await response.json();
            document.getElementById('billing_address_2').value = address.street;
            document.getElementById('billing_city').value = address.city;
        }

        postcode.addEventListener('blur', fill);
        number.addEventListener('blur', fill);
    })();
    </script>
    <?php
});
  • Laat de velden bewerkbaar. Een klant die op een adres woont dat administratief net anders heet moet gewoon kunnen bestellen.
  • Vul alleen aan als het veld nog leeg is of als de postcode is gewijzigd, zodat je een handmatige correctie niet overschrijft.
  • Werk je met een blocks-checkout, hang de aanroep dan aan de store-API in plaats van aan de classic-veldnamen.

Magento 2

Een service in je eigen module

Registreer hem in di.xml en roep hem aan vanuit een controller onder frontend, nooit rechtstreeks vanuit de browser met je livesleutel.

Model/AddressLookup.php
<?php
declare(strict_types=1);

namespace Score\LocatieApi\Model;

use Magento\Framework\App\CacheInterface;
use Magento\Framework\HTTP\Client\Curl;
use Magento\Framework\Serialize\SerializerInterface;

/**
 * Adresopzoeker voor Magento 2. Registreer hem als service en roep hem aan
 * vanuit een controller onder frontend/, nooit rechtstreeks vanuit de browser.
 */
class AddressLookup
{
    private const BASE = 'https://api.locatieapi.nl';

    public function __construct(
        private readonly Curl $curl,
        private readonly CacheInterface $cache,
        private readonly SerializerInterface $serializer,
        private readonly string $apiKey,
    ) {
    }

    public function find(string $postcode, int $number): ?array
    {
        $postcode = strtoupper(preg_replace('/\s+/', '', $postcode));
        $cacheKey = 'locatieapi_'.$postcode.'_'.$number;

        $cached = $this->cache->load($cacheKey);
        if ($cached) {
            return $this->serializer->unserialize($cached);
        }

        $this->curl->setTimeout(5);
        $this->curl->addHeader('X-Api-Key', $this->apiKey);
        $this->curl->addHeader('Accept', 'application/json');
        $this->curl->get(self::BASE."/v3/lookup/{$postcode}/{$number}");

        if ($this->curl->getStatus() !== 200) {
            return null;
        }

        $address = $this->serializer->unserialize($this->curl->getBody());
        $this->cache->save($this->serializer->serialize($address), $cacheKey, [], 86400);

        return $address;
    }
}

JavaScript

Autocomplete rechtstreeks vanuit de browser

Dit mag bij ons, mits je de toegestane origins op de sleutel zet en er een sleutel met alleen leesrechten voor gebruikt.

autocomplete.js
/**
 * Autocomplete rechtstreeks vanuit de browser. Dat mag met LocatieAPI,
 * mits je de origin op de sleutel zet en een sleutel met alleen
 * leesrechten gebruikt.
 */
const KEY = 'lat_test_demo_publiek_locatieapi_sandbox';

async function suggest(term) {
  if (term.length < 2) return [];

  const url = new URL('https://api.locatieapi.nl/v1/autocomplete');
  url.searchParams.set('q', term);
  url.searchParams.set('type', 'address');
  url.searchParams.set('limit', '8');

  const response = await fetch(url, { headers: { 'X-Api-Key': KEY } });

  if (!response.ok) return [];

  const { data } = await response.json();

  return data.map((row) => row.label);
}

const input = document.querySelector('#adres');
let timer;

input.addEventListener('input', () => {
  clearTimeout(timer);
  timer = setTimeout(async () => {
    const options = await suggest(input.value);
    document.querySelector('#suggesties').replaceChildren(
      ...options.map((label) => Object.assign(document.createElement('option'), { value: label })),
    );
  }, 150);
});

Python

Een kleine client met cache

Met een sessie die de header vasthoudt, een cache en een terugval die niets stukmaakt als de API even niet bereikbaar is.

locatieapi.py
"""Kleine client met caching en een nette terugval."""

from functools import lru_cache

import requests

BASE = "https://api.locatieapi.nl"
KEY = "lat_test_demo_publiek_locatieapi_sandbox"

session = requests.Session()
session.headers.update({"X-Api-Key": KEY, "Accept": "application/json"})


@lru_cache(maxsize=4096)
def lookup(postcode: str, number: int) -> dict | None:
    postcode = postcode.replace(" ", "").upper()

    try:
        response = session.get(f"{BASE}/v3/lookup/{postcode}/{number}", timeout=5)
    except requests.RequestException:
        return None

    if response.status_code == 404:
        return None

    response.raise_for_status()

    return response.json()


if __name__ == "__main__":
    print(lookup("1021 JT", 19))

Staat jouw stack er niet bij?

Gebruik het instructieblok voor je codeerassistent: dat zet de hele specificatie plus de opdracht om er in jouw project een adresopzoeker mee te bouwen op je klembord, met varianten voor Laravel, Next.js, WordPress en Python.