These docs are a work in progress and may not be fully up to date. Some pages may contain internal notes for our team.
Skip to Content

Data flow — end user

A visitor landing on a host page that embeds a DropAFinder widget goes through several distinct fetch phases before the widget renders. This document traces the full path from script load to interactive widget, plus the subsequent interactions (search, analytics).

For the broader architecture context, see Architecture overview. For the embedding setup from the customer’s perspective, see Embedding guide.


Phase 1 — Script bootstrap

The host page includes two elements:

<div id="finder-app" finder-key="YOUR_FINDER_KEY"></div> <script src="https://cdn.locationfinders.com/ext/v2/snippet.js" defer></script>

When the browser fetches snippet.js (the root loader), the loader checks whether a pointer.variant is set on the finder’s config. If the variant is "default" or absent, it imports the standard ext/2 bundle from the CDN. If a non-default variant is set, it attempts to dynamically import a variant-specific bundle from ${CDN_BASE}/ext/v2/variants/${variant}.js; if that fails, it falls through to the default bundle.

Sources: ext/2/source/src/main.js, ext/2/source/src/lib/embed.js


Phase 2 — Finder key resolution

getFinderKey() queries the DOM for the first element with a finder-key attribute and returns its value:

const appElement = document.querySelector('[finder-key]'); return appElement?.getAttribute('finder-key');

This is the short token that identifies the finder. All subsequent fetches are keyed by it.

Source: ext/2/source/src/lib/embed.js


Phase 3 — Pointer fetch

fetchFinderPointer(finderKey) fetches the short-TTL pointer file from the CDN:

GET https://cdn.locationfinders.com/finders/{finderKey}/config.json

The pointer is a small JSON object with (at minimum) a hash field and a variant field:

{ "hash": "a1b2c3d4", "variant": "default", "published_at": "2026-04-27T12:00:00Z" }
  • CDN TTL: 60 seconds (max-age=60). This is the intentionally short-lived layer — it tells the widget which version of the payload to load.
  • If the pointer fetch fails (network error, 404), the function returns null and the widget falls through to the origin API fallback.

Source: ext/2/source/src/lib/api.js:39-50


Phase 4 — Payload fetch (CDN → localStorage → origin fallback)

fetchFinderPayload(finderKey, knownHash) implements a three-level read path:

Level 1 — localStorage cache

The widget checks localStorage for the key lf-cdn-v2:{finderKey}. If the cached entry’s hash matches the pointer’s hash, the cached payload is returned immediately. No network request.

Level 2 — CDN hash-versioned payload

If the cache misses (or hash changed), the widget fetches the immutable versioned file:

GET https://cdn.locationfinders.com/finders/{finderKey}/v/{hash}.json

This URL is immutable — the same hash always returns the same JSON. It is served with a long CDN cache TTL. On success, the payload is stored in localStorage (keyed by lf-cdn-v2:{finderKey}, value includes { hash, data }) for next time.

Level 3 — Origin API fallback

If the CDN fetch fails (or no hash was available from the pointer), the widget falls back to the origin backend:

GET https://api.locationfinders.com/api/ext/finders/details/{finderKey}

This route is unauthenticated and workspace-scoped by the finder key. It returns the same payload shape as the CDN version. The origin fallback is always available (it’s the dev path too, before any CDN infrastructure is live).

Source: ext/2/source/src/lib/api.js:52-95


Phase 5 — Geolocation enrichment

If the finder has geolocation enabled (e.g., “sort by distance” or “show results near me”), the widget calls:

GET https://ipinfo.io/json

The raw ipinfo.io response includes a loc string ("lat,lng"); fetchIpLocation() parses this into the normalized shape { lat, lng, country, region, city } used by the widget. The result is used to pre-center the map and sort results by proximity. If the request fails, geolocation features degrade gracefully — the map uses a default center and no distance sorting is applied.

Source: ext/2/source/src/lib/api.js:9-37


Phase 6 — Mount and render

With the payload resolved and geolocation complete, the Svelte App is mounted:

mount(App, { target, props: { finderKey, pointer } });

App.svelte receives finderKey and the pointer object (which includes the CDN hash). The payload fetch runs inside App once mounted. Reset.svelte applies CSS custom properties derived from the finder’s design config and injects the Google Fonts link for DM Sans.


Phase 7 — Subsequent interactions

Search / autocomplete

When a visitor types in the search bar:

GET /api/mapping/autocomplete?input=…&provider=… POST /api/mapping/places (place details, on selection)

The autocomplete provider is configured per-finder (Google Places, Mapbox, HERE, Radar, Geoapify, or DropASearch). All requests are proxied through the backend to hide third-party API keys.

Analytics

Every finder interaction (view, click, search) emits an analytics event:

POST https://api.locationfinders.com/api/analytics

This endpoint is unauthenticated and keyed by finderKey. The payload includes the event type, timestamp, and optional metadata (clicked location ID, search query, etc.).


Full sequence diagram

Visitor browser CDN Backend ipinfo.io | | | | |-- GET snippet.js -------->| | | |<-- 200 snippet.js --------| | | | | | | |-- GET config.json (60s) ->| | | |<-- { hash, variant } -----| | | | | | | | check localStorage | | | | (hash match?) ----hit--> return cached payload | | | (hash miss) | | | |-- GET /v/{hash}.json ---->| | | |<-- payload (immutable) ---| | | | store in localStorage | | | | | | | | (fallback if CDN fetch fails) | | |-- GET /api/ext/finders/details/{key} ------------->| | |<-- payload ----------------------------------------| | | | | | |-- GET ipinfo.io/json ----------------------------------------------------------->| |<-- { loc, country, ... } --------------------------------------------------------------| | parse loc → { lat, lng } | | | | | | | | mount App.svelte | | | | apply CSS vars + fonts | | | | render widget | | | | | | | | (visitor interactions) | | |-- POST /api/analytics ----------------------------->| | |-- GET /api/mapping/autocomplete ------------------>| |

Key performance characteristics

FetchCacheNotes
snippet.jsCDN (long TTL)Versioned URL; invalidated on deploy
config.json pointerCDN ~5 minShort TTL so new publishes propagate quickly
/v/{hash}.json payloadCDN long TTL + localStorageImmutable; hash changes only on publish
Origin fallbackNoneAlways real-time; used in dev and on CDN miss
ipinfo.ioNonePer-visitor; not cached by the widget