@molecule/api-geolocation-photon

Provider bond · geolocation · API (Node) · v1.1.1 · Apache-2.0

Photon (OpenStreetMap) geolocation provider for molecule.dev — point it at your own instance

npm install @molecule/api-geolocation-photon

npm · Source on GitHub · Implements @molecule/api-geolocation

How it works

@molecule/api-geolocation-photon is a provider bond on the API (Node) side: it implements the geolocation core interface (@molecule/api-geolocation) with a concrete vendor or library behind it.

Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.

import { setProvider } from '@molecule/api-geolocation'
import { provider } from '@molecule/api-geolocation-photon'

setProvider(provider)

Works with: @molecule/api-geolocation

Reference

Photon (OpenStreetMap) geolocation provider for molecule.dev.

Implements the GeolocationProvider interface using a Photon instance for geocoding, reverse geocoding, and search-as-you-type (autocomplete). Distance calculations use the Haversine formula. Timezone lookups are not supported.

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Quick Start

import { setProvider } from '@molecule/api-geolocation'
import { provider } from '@molecule/api-geolocation-photon'

setProvider(provider)

Type

provider

Installation

npm install @molecule/api-geolocation-photon @molecule/api-geolocation

API

Interfaces

PhotonGeolocationConfig

Configuration options for the Photon geolocation provider.

interface PhotonGeolocationConfig {
  /**
   * Base URL of the Photon instance.
   * Defaults to the public instance `'https://photon.komoot.io'`.
   * Point this at a self-hosted Photon for production volume — the public
   * instance serves reasonable low-volume use only and throttles or bans
   * heavy callers.
   */
  baseUrl?: string

  /**
   * Photon result language (`en`, `de`, `fr`, `nl`, `it`, …). Languages the
   * instance has no data for fall back to the default. Defaults to the
   * instance default (`en`) when omitted.
   */
  language?: string

  /** Request timeout in milliseconds. Defaults to `10000`. */
  timeout?: number

  /** Maximum number of results to return from search queries. Defaults to `10`. */
  limit?: number

  /**
   * ISO 3166-1 alpha-2 country codes to keep (e.g. `['LI', 'CH']`). Photon's
   * API has no country-restriction parameter, so results are filtered AFTER
   * the query — a restriction combined with a small `limit` can return fewer
   * (even zero) results that the upstream actually had.
   */
  countryCodes?: string[]
}

Functions

createProvider(config)

Creates a Photon geolocation provider.

function createProvider(config?: PhotonGeolocationConfig): GeolocationProvider
  • config — Provider configuration (base URL, language, bounds).

Returns: A GeolocationProvider backed by the Photon API.

Constants

provider

The provider implementation, lazily initialized from env: PHOTON_BASE_URL (self-hosted or alternate instance) and PHOTON_LANG (result language).

const provider: GeolocationProvider

Core Interface

Implements @molecule/api-geolocation interface.

Bond Wiring

Setup function to register this provider with the core interface:

import { setProvider } from '@molecule/api-geolocation'
import { provider } from '@molecule/api-geolocation-photon'

export function setupGeolocationPhoton(): void {
  setProvider(provider)
}

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-geolocation ^1.0.2

Runtime Dependencies

  • @molecule/api-geolocation

  • Defaults to the public instance https://photon.komoot.io, which serves reasonable low-volume use only — heavy usage is throttled or banned, with no availability guarantees. For production volume, run your own Photon (Apache-2.0, an OpenSearch-embedded Java 21 app with an OSM extract) and point PHOTON_BASE_URL (or config.baseUrl) at it.

  • Attribution is required by the data, not optional: Photon serves OpenStreetMap data under ODbL — display "© OpenStreetMap contributors" wherever results appear.

  • Coverage is the loaded extract: a self-hosted Photon answers only for the region its index was built from; queries outside it return [], not an error. Build the index from the region your app actually geocodes.

  • No server-side country filter: countryCodes filters results AFTER the query, so a restriction combined with a small limit can return fewer results than matched.

  • Results are as-typed-as-Photon-parsed them: OSM data is community edited — treat a geocode result as a suggestion to confirm, never as a verified postal address (the same caution applies to every OSM geocoder).

  • getTimezone is not implemented (optional core capability) — feature- detect per the core's remarks, or use @molecule/api-geolocation-google.

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:

  • A known input resolves to plausibly-correct results: a real address passed to geocode() returns coordinates in roughly the right place (a famous landmark lands inside its own city, not the middle of the ocean), and a known lat/lng passed to reverseGeocode() names the right city — never an empty array, null, 0,0, or a hardcoded placeholder.
  • The app actually CONSUMES the result downstream, verified on screen: the map recenters on the geocoded point, a "near me" list is sorted or filtered by distance() (closest first), or the address form marks a real address valid and a bogus one invalid — a coordinate that comes back but changes nothing in the UI is a broken integration, not a pass.
  • If the app relies on the BROWSER geolocation permission, denying it (or letting it time out) falls back gracefully to manual entry — type or autocomplete an address — never a blank map, a spinner that never resolves, or a crash.
  • An unresolvable input (gibberish address, empty geocode()/ reverseGeocode() result) surfaces a clear "location not found" message, not a crash, a silent blank screen, or a default location shown as if real.
  • PRIVACY: a user's precise coordinates are not exposed to other users or written to logs beyond what the feature needs (persist/show only the granularity required — e.g. city, not raw lat/lng), and the geocoding provider key stays SERVER-SIDE (app screens call YOUR API, never the geocoding service directly).