@thinwrap/location
@thinwrap/location for TypeScript — unified facade for 21 connectors across 6 providers, spanning routing, matrix, geocoding, and isochrone. Built-in polyline utilities. Stateless, zero deps, BYO fetch.
TypeScript constraints
- Runtime dependencies
- Zero runtime dependencies
- BYO HTTP client
- injectable fetch parameter (Node 18+ native fetch)
- Provenance
- npm provenance + Sigstore
- Minimum runtime
- Node 18.20+
- Node 18 and 20 emit a one-time ExperimentalWarning on the first fetch() invocation: "ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time."
- The warning is informational only — fetch() in Node 18.20+ and Node 20.x is functionally identical to the stable fetch in Node 21+. Suppress with the NODE_NO_WARNINGS=1 env var or the --no-warnings flag if it clutters CI logs.
- Thinwrap does not patch around the warning, does not silence it on the consumer's behalf, and does not require a polyfill. It is surfaced exactly as Node's runtime surfaces it.
30-second install
npm install @thinwrap/location2-minute end-to-end example
Compute a route between two waypoints through any supported provider. The example below uses Mapbox; switching providers means changing the provider ID and config — input and output shape stay identical.
import { Routing } from '@thinwrap/location';
const routing = new Routing('mapbox', { accessToken: process.env.MAPBOX_TOKEN! });
const result = await routing.route({
waypoints: [
{ lat: 37.7749, lng: -122.4194 },
{ lat: 37.3382, lng: -121.8863 },
],
});
console.log(`Distance: ${result.totalDistanceMeters}m, Duration: ${result.totalDurationSeconds}s`);
console.log(result.polyline); // Google precision-5 encodedSame code works for all supported routing providers — swap to 'google', 'here', 'esri', 'tomtom', or 'osrm' and pass the matching config. Output normalizes to a singleIRoutingResult shape across every provider.
See the universal route snippet for the full copy-paste pattern.
Supported providers
| Provider | Operations | Auth | README |
|---|---|---|---|
google Google Maps Platform | routing, matrix, geocoding | query | docs |
mapbox Mapbox | routing, matrix, geocoding, isochrone | query | docs |
here HERE | routing, matrix, geocoding, isochrone | header | docs |
esri ESRI ArcGIS | routing, matrix, geocoding, isochrone | bearer | docs |
tomtom TomTom | routing, matrix, geocoding, isochrone | query | docs |
osrm OSRM (self-hosted) | routing, matrix | header | docs |
Migration paths
Built-in polyline utilities
Every routing connector emits a Google precision-5 encoded polyline onresult.polyline. Decode, re-encode, or convert HERE's flex-polyline / ESRI's path geometry without a third-party polyline library:
import { decodePolyline, encodePolyline, decodeFlexPolyline, encodeEsriPaths } from '@thinwrap/location';
const points = decodePolyline(result.polyline); // [{ lat, lng }, …]
const reEncoded = encodePolyline(points); // back to precision-5
const here = decodeFlexPolyline('BFoz5...'); // HERE flex-polyline
const esri = encodeEsriPaths([[[ -74, 40 ], [ -73.5, 40.5 ]]]);The four polyline primitives are the only encode/decode exports — locked at v1.0. No additional package required.
Operation coverage
Provider × operation gaps below are architectural, not bugs:
- OSRM ships only
routing+matrix— geocoding and isochrone are out of scope by design for OSRM. - Google does not expose a public isochrone API at v1.0; if Google adds one, it lands in a minor release.
Migration: from @googlemaps/google-maps-services-js
- import { Client } from '@googlemaps/google-maps-services-js';
- const gm = new Client();
- await gm.directions({ params: { origin, destination, key } });
+ import { Routing } from '@thinwrap/location';
+ const routing = new Routing('google', { apiKey: process.env.GOOGLE_KEY! });
+ await routing.route({ waypoints: [origin, destination] });Migration: from @mapbox/mapbox-sdk
Drop the SDK; the wrapper hits the same HTTP endpoints and normalizes the response.
Migration: from hand-rolled HTTP
Keep your fetch wrapper and retry layer; replace the per-provider request shape with one uniform call. The wrapper holds no state — every operation is a single function call from input to output (HERE Matrix v8's submit-poll-retrieve cycle stays hidden behind a singleawait).
Migration: from a cross-language service
The PHP variant at /packages/location-phpships the same facade shape and provider set — cross-language parity is tracked as a design goal, not a hard release gate.