mapbase
v1.0.2
Published
Typed HTTP client for the Mapbase Engine API.
Downloads
1,194
Maintainers
Readme
mapbase
Typed HTTP client for the Mapbase Engine API.
Mapbase · API reference · SDK guide · Dashboard
Table of contents
- Install
- Quickstart
- Get an API key
- Documentation
- API surface
- React hooks (
mapbase/react) - Place sources
- Geometry helpers
- Responses
- Retry
- Cursor pagination (
listAll) - Errors
- Versioning
- License
Install
npm install mapbase
# or
bun add mapbaseTypes are generated from the engine's OpenAPI 3.1 spec — they stay in sync with the published API contract.
Quickstart
Every method returns { data, error, rateLimit }. Check error before
using data.
import { Mapbase } from "mapbase"
const mapbase = new Mapbase(process.env.MAPBASE_API_KEY!)
const { data, error } = await mapbase.autocomplete.search({
q: "lis",
layers: ["locations"],
country: "PT",
limit: 5,
})
if (error) {
console.error(error.code, error.message)
} else {
for (const suggestion of data.results) {
console.log(suggestion.id, suggestion.name)
}
}Try the live playground before wiring the SDK into your app.
Get an API key
All /v1 routes require an API key. The SDK sends it as x-api-key on
every request.
- Create an account — sign up at dashboard.mapbase.dev/login (free tier available).
- Mint a key — open
dashboard.mapbase.dev/api-keys
and create a key. Live keys use the
mb_live_prefix. - Pass it to the client — the first constructor argument:
const mapbase = new Mapbase(process.env.MAPBASE_API_KEY!)Notes
- You only see the full secret once at creation; keys are SHA-256 hashed at rest.
- Optionally restrict each key to a list of allowed domains in the dashboard.
- Do not ship live keys in browser bundles. Call Mapbase from your server, or inject keys server-side (for example via a Next.js server action or API route).
mapbase.layers.list()does not require auth; every other/v1route does.
Documentation
| Resource | URL |
| -------- | --- |
| Website & playground | mapbase.dev |
| SDK guide (walkthroughs) | mapbase.dev/sdk |
| Interactive API reference (Scalar) | mapbase.dev/api-reference |
| OpenAPI 3.1 spec | mapbase.dev/openapi.json |
| LLM docs index (llms.txt) | mapbase.dev/llms.txt |
| API reference (Markdown) | mapbase.dev/api-reference.md |
| Engine base URL | api.mapbase.dev |
The SDK is codegen'd from the same OpenAPI contract that powers Scalar, so request/response types cannot drift from the live API. For step-by-step examples (autocomplete, point resolve, rate limits), see the SDK guide.
Use /locations to list and filter locations. Use /locations/{identifier} to
fetch one location. Use /locations/{identifier}/relations to navigate the
hierarchy. Use /locations/{identifier}/boundary when you need polygons. Use
/locations/{identifier}/zones when you need custom zones attached to that
location. Use /autocomplete for search-as-you-type across all layers.
API surface
Each namespace maps to a tag in the engine's OpenAPI spec. Override the
default base URL with { baseUrl: "https://api.mapbase.dev" } if needed.
| Namespace | Methods | Notes |
| --------- | ------- | ----- |
| locations | list, listAll, get, relations, boundary, zones | Administrative locations |
| postcodes | list, listAll, get, byPoint, byCode | Postal codes |
| lau | list, listAll, get, boundary, postcodes, postcodesAll | EU LAU regions |
| zones | list, listAll, get, boundary | Read-only; writes via zoneSubmissions |
| zoneSubmissions | create, list, get, update, submit, delete | User-scoped via API key; two keys for the same user share submissions |
| boundaries | list | Bulk boundary fetch with spatial and registry filters |
| points | resolve | Point-in-polygon resolve |
| autocomplete | search | Unified search across every layer |
| seo | page, tree, sitemap | SEO page bundles, directory trees, sitemap entries |
| layers | list | Registry layers with live row counts; no API key required |
| providers.google | autocomplete, place, geocode, reverseGeocode | Live passthroughs; pass your Google key via googleApiKey in request options; responses typed as unknown |
| providers.geoapify | autocomplete, boundaries | Live passthroughs; pass your Geoapify key via geoapifyApiKey in request options |
React hooks (mapbase/react)
Hooks live under the mapbase/react subpath. Peer dependencies:
@tanstack/react-query (required, >=5) and react (>=18, optional).
Exports: MapbaseProvider, useMapbase, useMapbaseClient,
useOptionalMapbase, usePlaceSource, useAutocomplete, useBoundary,
usePointResolve, useLayerCatalog.
import { useAutocomplete } from "mapbase/react"
function Search() {
const { query, setQuery, hits, loading } = useAutocomplete({ country: "PT" })
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
{loading ? (
<span>…</span>
) : (
hits.map((h) => <div key={h.id}>{h.label}</div>)
)}
</div>
)
}Wrap your tree in MapbaseProvider once (server-side key injection recommended):
import { MapbaseProvider } from "mapbase/react"
function App() {
return (
<MapbaseProvider apiKey={process.env.MAPBASE_API_KEY!}>
<Search />
</MapbaseProvider>
)
}| Hook | Purpose |
| ---- | ------- |
| useAutocomplete | Debounced typeahead via react-query; returns { query, setQuery, hits, loading, error, ... } |
| useBoundary | Fetch a boundary polygon by place id |
| usePointResolve | Resolve a coordinate or suggestion to taxonomy |
| useLayerCatalog | Public layer catalog with availability and count helpers |
Place sources
Use createPlaceSource(client, name) to build a PlaceSource for
"mapbase" | "google" | "geoapify". Named factories:
createMapbaseSource, createGoogleSource, createGeoapifySource.
Google and Geoapify sources require the customer's provider key
(googleApiKey / geoapifyApiKey).
import { Mapbase, createPlaceSource } from "mapbase"
const client = new Mapbase("mb_live_...")
const source = createPlaceSource(client, "mapbase")
const { hits } = await source.autocomplete("Lisb", { country: "PT" })Geometry helpers
Polyline and bbox helpers (coordinates are [lng, lat], GeoJSON order):
decodeGooglePolyline(encoded)— decode a precision-5 Google polyline to[lng, lat]pairs.polygonFeaturesFromEncodedPolylines(encoded[])— assemble GeoJSONFeaturepolygons from encoded rings.polylinesToGeoJson(encoded[])— first feature geometry, ornull.bboxToPolygonCoordinates([minLng, minLat, maxLng, maxLat])— closed ring.
Responses
Every method returns a Result:
type Result<T> =
| { data: T; error: null; rateLimit: RateLimitInfo }
| { data: null; error: MapbaseError; rateLimit: RateLimitInfo }The SDK never throws on HTTP errors. Network failures and timeouts are
surfaced as MapbaseError with code: "network_error".
Retry
Retry is off by default (attempts: 0). Opt in at client construction
or per request:
const mapbase = new Mapbase("mb_live_...", {
retry: { attempts: 3, respectRetryAfter: true },
})
// Per-request override — disable despite client default
await mapbase.locations.get("0-EU-ES", {}, { retry: false })
await mapbase.autocomplete.search(
{ q: "lis", layers: ["locations"] },
{ retry: { attempts: 2 } },
)By default only GET requests are retried (429, 502, 503, 504, and
transport failures). POST, PATCH, and DELETE require
retry: { idempotent: true } — do not set that on
zoneSubmissions writes unless the engine guarantees idempotency.
timeoutMs applies to each attempt. Optional retry.budgetMs caps
total wall time across attempts and backoff sleeps.
Cursor pagination (listAll)
List namespaces expose async iterators that walk every page via
meta.next_cursor. Top-level collections use listAll; id-scoped related
lists use {relation}All (for example lau.postcodesAll).
for await (const postcode of mapbase.postcodes.listAll({ country: "PT" })) {
console.log(postcode.code)
}Unlike single-page list() methods, iterators throw MapbaseError when
a page request fails. Pass signal to cancel between pages; an aborted
signal ends the generator without throwing.
*All param types omit cursor and offset (cursor-only iteration). Use
the existing list() methods for offset-based random access.
Optional caps on ListAllOptions:
maxPages— stop after this many HTTP page fetches (first page counts as 1).maxItems— stop after yielding this many rows (may end mid-page).
Per-request retry passes through to each page fetch. For bulk exports,
consider retry: { attempts: 2–5, respectRetryAfter: true }.
zoneSubmissions has no listAll in v1.
Errors
Handle failures by switching on error.code:
import { MapbaseError, type MapbaseErrorCode } from "mapbase"
const { error } = await mapbase.locations.get("0-EU-ES")
if (error) {
switch (error.code) {
case "rate_limited":
console.log("Try again in", error.rateLimit?.retryAfter, "s")
break
case "unauthorized":
console.log("Check your API key")
break
case "invalid_request":
console.log("Fix the request parameters")
break
case "not_found":
console.log("No such location")
break
default:
console.error(error)
}
}Versioning
[email protected] targets the engine's stable /v1 contract today. Additive
engine changes ship as SDK minor versions. When a breaking engine API ships
under /v2, the SDK major version will track that prefix. Backwards-
incompatible changes within /v1 would violate the engine contract.
License
MIT
