SDK · mapbase

Typed HTTP client for the Mapbase Engine API

Resend-inspired ergonomics. { data, error } on every call. Types are generated from the engine's OpenAPI 3.1 contract — they cannot drift. Tracks the engine major: [email protected]/v1.

1. Install

terminal
bun add mapbase
# or: npm i mapbase / pnpm add mapbase / yarn add mapbase

2. Authenticate

Mint an API key in the dashboard. Keys are prefixed mb_live_ and SHA-256 hashed at rest. The SDK sends them as x-api-key on every request. Never expose a key in browser code — proxy from your own server, or use the registry components which accept a key prop you sourced from a server-side env.

3. First request

The SDK exposes one namespace per OpenAPI tag. Live routes live under locations; boundaries, addresses, postcodes, and geocode are typed but currently return a not_implemented error until their handlers ship.

quickstart.ts
import { Mapbase } from "mapbase"

const mapbase = new Mapbase(process.env.MAPBASE_API_KEY!)

const { data, error } = await mapbase.locations.autocomplete({
  q: "lis",
  country: "PT",
  limit: 5,
})

if (error) {
  console.error(error.code, error.message)
} else {
  for (const suggestion of data.data) {
    console.log(suggestion.id, suggestion.name)
  }
}

4. Errors never throw

HTTP errors come back as a typed MapbaseError on the error field. Only network failures (DNS, TLS, timeout, abort) throw. Switch on error.code to handle each failure mode explicitly.

error-handling.ts
import { Mapbase, MapbaseError } from "mapbase"

const mapbase = new Mapbase(process.env.MAPBASE_API_KEY!)

const { data, error, rateLimit } = await mapbase.locations.byId("pt/lisboa")

if (error) {
  switch (error.code) {
    case "unauthorized":
      throw new Error("Mapbase: invalid or missing API key.")
    case "not_found":
      // Handle as a 404 in your app.
      return null
    case "rate_limited":
      // Backoff using the parsed Retry-After header.
      await sleep((rateLimit.retryAfter ?? 1) * 1000)
      return retry()
    case "not_implemented":
      // The endpoint is on the public surface but the engine handler
      // hasn't shipped yet. Treat as graceful degradation.
      return null
    case "network_error":
      // Real transport failure — surface a retryable banner.
      throw error
    default:
      // Unknown / 5xx — log with request id for support.
      console.error(error.code, error.requestId, error.message)
      throw error
  }
}

return data

declare function sleep(ms: number): Promise<void>
declare function retry(): Promise<unknown>

5. Rate limit headers

Every response — success or error — exposes the parsed RateLimit-* and Retry-After headers as rateLimit. Use it to pace concurrent calls or back off on 429s.

rate-limit.ts
import { Mapbase } from "mapbase"

const mapbase = new Mapbase(process.env.MAPBASE_API_KEY!)

const { rateLimit } = await mapbase.locations.autocomplete({ q: "lis" })

console.log({
  limit: rateLimit.limit, // bucket size
  remaining: rateLimit.remaining, // tokens left
  reset: rateLimit.reset, // seconds until refill
  retryAfter: rateLimit.retryAfter, // present only on 429
  policy: rateLimit.policy, // raw policy header for debugging
})

Versioning

  • [email protected] ↔ engine /v1 while the surface is shaking out.
  • Breaking changes to the runtime shape, the MapbaseError surface, or removed methods bump the major.
  • New namespaces, optional arguments, or new typed error codes previously surfaced as unknown are minor.
  • The engine and SDK share a CI drift check — engine PRs that change the schema must regenerate packages/sdk/src/_generated/schema.ts.