Géocoder une adresse en JavaScript et Node.js

Ce tutoriel n’utilise que le fetch intégré de Node 18 ou plus récent (ou d’un navigateur). Il géocode une adresse, gère poliment la limite de débit, puis géocode une liste avec une petite file. Un client typé existe aussi : voir le SDK TypeScript.

L’URL de base https://api.addrova.eu n’est pas encore en ligne. Tout ce qui suit suit la spécification de l’API. [À VÉRIFIER : service ouvert, URL de base définitive.]

1. Une adresse

const resp = await fetch("https://api.addrova.eu/v1/geocode", {
  method: "POST",
  headers: { "X-API-Key": process.env.ADDROVA_API_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({ q: "4 place d'Armes, Belfort", country: "FR", limit: 1 }),
});
if (!resp.ok) throw new Error((await resp.json()).code);
const { results, attribution } = await resp.json();
const best = results[0];
console.log(best.label, best.location, best.precision, best.confidence_level);
console.log(attribution.map((a) => a.text));

L’adresse voyage dans le corps d’un POST, pas dans l’URL, donc elle n’apparaît jamais dans une URL ni une chaîne de requête (l’API ne journalise que le gabarit de route, ZL-01). Conservez l’attribution avec le résultat si vous le stockez.

2. L’encapsuler et relancer sur limite de débit

Quand vous dépassez votre débit, l’API répond 429 avec un en-tête Retry-After. Respectez-le au lieu de marteler.

async function geocode(q, country = "FR", attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    const resp = await fetch("https://api.addrova.eu/v1/geocode", {
      method: "POST",
      headers: { "X-API-Key": process.env.ADDROVA_API_KEY, "Content-Type": "application/json" },
      body: JSON.stringify({ q, country, limit: 1 }),
    });
    if (resp.status === 429 && i < attempts - 1) {
      const wait = Number(resp.headers.get("retry-after") ?? 1);
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    if (!resp.ok) throw new Error(`${resp.status} ${(await resp.json()).code}`);
    return (await resp.json()).results[0] ?? null;
  }
}

const hit = await geocode("4 place d'Armes, Belfort");
console.log(hit?.label ?? "introuvable");

3. Une liste, avec une petite file

Une courte liste n’a pas besoin d’un job par lot. Lancez quelques requêtes à la fois plutôt que toutes d’un coup.

async function geocode(q) {
  const resp = await fetch("https://api.addrova.eu/v1/geocode", {
    method: "POST",
    headers: { "X-API-Key": process.env.ADDROVA_API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ q, country: "FR", limit: 1 }),
  });
  if (!resp.ok) throw new Error(`${resp.status}`);
  return (await resp.json()).results[0] ?? null;
}

async function geocodeAll(addresses, concurrency = 4) {
  const out = new Array(addresses.length);
  let next = 0;
  const worker = async () => {
    while (next < addresses.length) {
      const i = next++;
      out[i] = await geocode(addresses[i]);
    }
  };
  await Promise.all(Array.from({ length: Math.min(concurrency, addresses.length) }, worker));
  return out;
}

const rows = await geocodeAll(["4 place d'Armes, Belfort", "10 rue de Rivoli, Paris"]);
console.log(rows.map((r) => r?.label ?? null));

Pour des milliers de lignes, utilisez le point d’entrée par lot ou le géocodeur CSV plutôt qu’une boucle.

Bonnes habitudes

  • Ne mettez pas de clé live dans une page publique. Dans un navigateur, utilisez une clé restreinte à l’origine ou relayez l’appel depuis votre serveur.
  • Vérifiez confidence_level et precision avant d’utiliser un point : voir les niveaux de précision et les seuils de confiance.
  • Ne journalisez jamais l’adresse envoyée. Le service n’écrit aucune adresse dans ses journaux (ZL-10) ; votre propre code ne devrait pas non plus.