@rijwind/sdk
v0.2.0
Published
Maps, geocoding, and routing SDK for the Rijwind API.
Maintainers
Readme
@rijwind/sdk
Maps, geocoding, and routing for the open web — one key, fully typed.
npm install @rijwind/sdkShow a map
import { Map, MapStyle, config } from '@rijwind/sdk';
import '@rijwind/sdk/style.css';
config.apiKey = 'rw_live_…';
const map = new Map({
container: 'map',
style: MapStyle.LIGHT, // LIGHT · DARK · GRAYSCALE · WHITE · BLACK
center: [4.9041, 52.3676],
zoom: 10,
});Map extends MapLibre GL JS, so every MapLibre
method, event, and option works unchanged — markers, popups, layers, the
lot. On top, it opens a basemap tile session and keeps it fresh in the
background, so a long-open map keeps working and you're billed once per map
view, not per tile. You never handle signed URLs or tokens.
Tear-down is just MapLibre's remove() — the tile session is cleaned up
with it:
map.remove();Custom styles
Pass your own MapLibre style URL or object instead of a MapStyle. Anywhere
the tile source should point at the basemap, use the {{TILE_URL}}
placeholder — the SDK substitutes the live session for you:
const map = new Map({ container: 'map', style: 'https://example.com/my-style.json' });Geocoding & routing
The same package exposes a typed REST client. In Node or any backend, import
it from the light entry point (@rijwind/sdk/client) so no map library is
pulled in:
import { createClient } from '@rijwind/sdk/client';
const rijwind = createClient({ apiKey: process.env.RIJWIND_API_KEY! });
// Forward geocode
const { data, error } = await rijwind.geocode.search({ q: 'Damrak 1, Amsterdam', limit: 5 });
if (!error) {
for (const f of data.features) console.log(f.properties.name, f.geometry.coordinates);
}In the browser you can use the same methods straight off the main entry:
import { createClient, config } from '@rijwind/sdk';
config.apiKey = 'rw_live_…';
const rijwind = createClient(); // picks up config.apiKeyEvery method returns { data, error, response } — the same shape as
openapi-fetch, which this wraps.
data is typed against the success response; error against the error
envelope.
Route
const { data } = await rijwind.route({
locations: [
{ lat: 52.3676, lon: 4.9041 },
{ lat: 52.0907, lon: 5.1214 },
],
costing: 'bicycle',
});
console.log(data?.trip.summary); // { length, time, ... }
console.log(data?.trip.legs[0].shape); // encoded polyline (1e-6 precision)Isochrone
const { data } = await rijwind.isochrone({
locations: [{ lat: 52.3676, lon: 4.9041 }],
costing: 'bicycle',
contours: [{ time: 5 }, { time: 10 }, { time: 15 }],
polygons: true,
});Static maps
staticMapUrl(params) builds a URL for a server-rendered map image — drop it
straight into an <img>. Pure string building (no network, no map library), so
it works in Node and the browser, and is exported from both @rijwind/sdk and
@rijwind/sdk/client.
import { staticMapUrl, config } from '@rijwind/sdk';
config.apiKey = 'rw_live_…';
const url = staticMapUrl({
center: [4.9041, 52.3676],
zoom: 12,
size: [800, 500],
style: 'light', // light | dark | grayscale | white | black
hidpi: true, // @2x (High-DPI) — costs 2 units
markers: [{ lon: 4.9041, lat: 52.3676, color: '#ff0000', size: 'l', label: 'A' }],
circle: { radiusMeters: 1000, fillColor: '#0066ff', fillOpacity: 0.15 },
});
// <img src={url} width={800} height={500} alt="map" />Instead of center + zoom, give a bbox: [minLon, minLat, maxLon, maxLat], or
set auto: true to frame the overlays — the same three viewport modes as Mapbox
and MapTiler. Add paths for polylines, format: 'jpg' | 'webp', padding, and
attribution (a corner, or false). Billed 1 unit per image (2 for @2x), from
the same pool as every other endpoint.
Configuration
config holds the defaults every Map and createClient falls back to:
import { config } from '@rijwind/sdk';
config.apiKey = 'rw_live_…';
config.baseUrl = 'https://api.rijwind.com'; // default; point at a local instance in dev
config.fetch = customFetch; // optional — inject retries, telemetryPer-call options override the globals: new Map({ apiKey }),
createClient({ apiKey, baseUrl }).
Entry points
| Import | Contains | Pulls in MapLibre? |
| --- | --- | --- |
| @rijwind/sdk | Map, MapStyle, config, REST client + types | yes (browser) |
| @rijwind/sdk/client | REST client + types only | no (Node-safe) |
| @rijwind/sdk/style.css | map stylesheet | — |
Errors
Every endpoint returns the same envelope on failure:
{
error: 'quota_exhausted', // stable, machine-readable
message: 'Monthly request quota exceeded.', // for humans
}Common codes: missing_key, invalid_key, revoked_key,
origin_blocked, quota_exhausted, matrix_too_large,
isochrone_too_large. See the error reference.
Raw client
Need an endpoint that isn't wrapped? Drop down to the underlying openapi-fetch client — it speaks the full OpenAPI surface:
const { data } = await rijwind.raw.GET('/tiles/v1/token');Versioning
@rijwind/sdk follows the API: 0.x.y while in preview, 1.x.y once the
API has been stable for a quarter. Breaking changes ship in a major;
additive endpoints and optional parameters in minors.
License
MIT.
