@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/location

2-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 encoded

Same 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

6 providers ship with @thinwrap/location@1.0.0.
ProviderOperationsAuthREADME
google Google Maps Platformrouting, matrix, geocodingquerydocs
mapbox Mapboxrouting, matrix, geocoding, isochronequerydocs
here HERErouting, matrix, geocoding, isochroneheaderdocs
esri ESRI ArcGISrouting, matrix, geocoding, isochronebearerdocs
tomtom TomTomrouting, matrix, geocoding, isochronequerydocs
osrm OSRM (self-hosted)routing, matrixheaderdocs

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.

Read more