@gittrends-app/geocoder is a Node.js library and CLI for turning free-form
place names into structured administrative results. It targets city, state, and
country-level places rather than street addresses or points of interest. The CLI
and core default to OpenStreetMap Nominatim, Photon, and LocationIQ, with
caching, fallback providers, and request queues.
The default OSM provider is the public
Nominatim service. Its public-service
limit is at most one request per second. The official policy identifies
clients with a User-Agent or Referer; this implementation requires an
identifying User-Agent and contact email for that server. It sends the
former as the User-Agent header and the latter as the email query
parameter; it has no Referer option:
OSM_EMAIL=ops@example.com \
OSM_USER_AGENT='my-app/1.0 (https://example.com/contact)' \
gittrends-geocoderFor regular or long-running bulk work, Nominatim's policy also requires a single-threaded, cached client and a maximum of four requests per minute. The library and CLI retain safe defaults: one request per second and one concurrent request for regular use, and four requests per minute for the CLI's public bulk profile. They do not reject explicit concurrency or rate overrides, and the CLI bulk command honors the requested worker count. Choosing overrides that comply with provider terms is the caller's responsibility; use one worker and keep caching enabled for public Nominatim bulk work.
Do not use the public service for autocomplete or systematic harvesting, scraping place details, reselling geocoding data, or building a competing database. Give visible OpenStreetMap/Nominatim attribution wherever results are shown. Queries are sent to third-party providers, which may process or log them: disclose those providers in your privacy notice and do not send personal or confidential data unless applicable privacy law and provider terms permit it.
The public endpoint and a self-hosted Nominatim instance are different
services. A custom OSM_SERVER is not automatically self-hosted or covered by
the public endpoint's capacity: use an instance you operate or a provider
whose terms permit your workload, and follow its limits. Switching providers
does not transfer the public Nominatim policy to another provider.
npm install github:gittrends-app/geocoder
# or
yarn add github:gittrends-app/geocoderThe public API method is search, not geocode:
import { Cache, OpenStreetMap } from '@gittrends-app/geocoder';
const geocoder = new Cache(
new OpenStreetMap({
osmServer: 'https://nominatim.openstreetmap.org',
email: process.env.OSM_EMAIL,
userAgent: 'my-app/1.0 (https://example.com/contact)',
concurrency: 1
}),
{ size: 1000, positiveTtl: 3_600_000, negativeTtl: 300_000 }
);
const address = await geocoder.search('Brazil');Cache TTLs are milliseconds; 0 means no expiry. It caches successful
results and not-found results separately, normalizes cache keys, and can use a
secondary Keyv store options. The CLI's persistent cache is a file named
geocoder-cache.sqlite under CACHE_DIR; treat retained queries and results as
data that may need an expiry or deletion policy.
Results contain source, name, type, confidence, provider, and may
contain coordinates, a bounding box, and city, state, country, and
country_code fields. source is the trimmed query with repeated whitespace
collapsed. name trims, removes empty or repeated administrative components,
and joins them with commas; type is the provider's administrative place type.
score is the provider's score when available; it is not a probability. OSM
and LocationIQ use provider importance for confidence and score, while
Photon reports confidence: 0 without a score. minConfidence filters OSM and
LocationIQ results.
LocationIQ is configured in library code with a required constructor
apiKey; its options are baseUrl, minConfidence, language,
concurrency, rate, and retries. Library code does not read
LOCATIONIQ_KEY, LOCATIONIQ_API_KEY, CACHE_DIR, or cache TTL environment
variables. Those environment variables are CLI configuration only.
The executable is gittrends-geocoder. Running it without a subcommand starts
the HTTP server:
OSM_EMAIL=ops@example.com \
OSM_USER_AGENT='my-app/1.0 (https://example.com/contact)' \
gittrends-geocoder --host 0.0.0.0 --port 8080
curl 'http://localhost:8080/search?q=Brazil'GET /search?q=<place> returns an address, or 404 when none is found. q
is normalized, must be non-empty, and is limited to 500 characters. /docs
serves Swagger UI and / redirects there. /health, /health/ready, and
/health/live are local process checks and never contact providers or consume
cache/provider capacity. They normally return 200; /health/live also
bypasses the optional inbound rate limiter, while the other two can be
rate-limited. /health returns status, timestamp, and uptime; the other two
return { "ready": true } and { "alive": true }.
Successful searches also include X-Geocoder-Provider and, when available,
X-Geocoder-Attribution response headers. These headers do not replace
visible attribution in the consuming application.
The server accepts these options:
--osm-server <SERVER> --osm-email <EMAIL> --osm-agent <AGENT>
--providers <LIST> --rate-profile <public|public-bulk|self-hosted>
--provider-language <LANG> --provider-timeout-ms <MS>
--provider-retries <COUNT> --locationiq-key <KEY>
--cache-dir <DIR> --cache-size <SIZE>
--cache-positive-ttl <DURATION> --cache-negative-ttl <DURATION>
--concurrency <COUNT> --rate-limit
--rate-limit-max <MAX> --rate-limit-window <DURATION>
--trust-proxy <BOOLEAN> -H, --host <HOST> -p, --port <PORT>
Providers are named osm, photon, and locationiq; --providers controls
their order. The first provider is tried first and later providers are used
for a null result or retryable provider failure. LocationIQ requires
--locationiq-key (or the CLI environment variable LOCATIONIQ_KEY or
LOCATIONIQ_API_KEY) when selected. There is no CLI option for a LocationIQ
base URL. Photon uses its hosted Komoot endpoint; there is intentionally no CLI
option for a Photon base URL.
--rate-profile accepts public, public-bulk, or self-hosted. The CLI
identifies the public Nominatim server from OSM_SERVER; server mode applies
one request per second and bulk mode applies four requests per minute.
These are safe defaults, not a public Nominatim concurrency/rate policy
enforcement mechanism: explicit --concurrency choices are passed through, and
following provider terms is the caller's responsibility. self-hosted should
only describe an instance you operate; a custom server still requires following
that server's policy.
The CLI environment equivalents include OSM_SERVER, OSM_EMAIL,
OSM_USER_AGENT, PROVIDERS, RATE_PROFILE, PROVIDER_LANGUAGE,
PROVIDER_TIMEOUT_MS, PROVIDER_RETRIES, LOCATIONIQ_KEY,
LOCATIONIQ_API_KEY, CACHE_DIR, CACHE_SIZE,
CACHE_POSITIVE_TTL_MS, CACHE_NEGATIVE_TTL_MS, CONCURRENCY, PORT,
HOST, LOG_LEVEL, RATE_LIMIT_ENABLED, RATE_LIMIT_MAX,
RATE_LIMIT_WINDOW, TRUST_PROXY, and
GRACEFUL_SHUTDOWN_TIMEOUT_MS, and NODE_ENV. Durations accept values such as 500ms,
5 seconds, or 1 hour.
Input is newline-delimited text from a positional file, --input <FILE>, or
stdin (the default; use - explicitly). Output is NDJSON on stdout and
progress is written to stderr:
OSM_EMAIL=ops@example.com \
OSM_USER_AGENT='my-app/1.0 (https://example.com/contact)' \
gittrends-geocoder bulk places.txt \
--providers osm --workers 1 --cache-dir .cache \
--cache-positive-ttl '24 hours' --resume previous.ndjson \
--continue-on-error > results.ndjsonBulk automatically uses the public-bulk profile for the public Nominatim
endpoint. Bulk options are -i, --input <FILE>, --resume <FILE>, --workers <COUNT>,
and --continue/--continue-on-error, plus the provider and cache options
listed above. Server-only options such as --rate-limit, --trust-proxy,
--host, and --port have no effect on bulk. Inputs are normalized and
deduplicated; successful queries in --resume are skipped. Bulk honors
--workers, including values greater than one; for public Nominatim, worker and
rate choices must follow provider terms and are the caller's responsibility.
Each output line
is a success record such as { "query": "Paris", "ok": true, "address": null }
(or an address object) or a failure record such as
{ "query": "Paris", "ok": false, "error": "Geocoding failed" }.
Build and run from the repository root:
docker build -t gittrends/geocoder .
docker run --rm -p 8080:8080 gittrends/geocoderThe image listens on 0.0.0.0 port 8080 (unprivileged), runs with NODE_ENV=production, uses
/app/.cache as a volume, keeps up to 10,000 in-memory cache entries, and
uses provider concurrency 1. Its image default OSM_SERVER is
https://nominatim.geocoding.ai, a hosted endpoint distinct from the official
public Nominatim service; verify that endpoint's terms or override it with
-e OSM_SERVER=.... If overriding it with the official public endpoint, also
provide OSM_EMAIL and OSM_USER_AGENT. No public Nominatim identity is
baked into the image. The CLI cache defaults are one hour for found results
and five minutes for not-found results.
The image runs as the unprivileged node user. Its health check uses
/health/live; this endpoint is independent of upstream provider health.
yarn install --frozen-lockfile
yarn verifyThe project is MIT-licensed and available at https://github.com/gittrends-app/geocoder.