@tilery/client
v0.1.0
Published
Client library for Tilery.eu tile API with service worker caching
Maintainers
Readme
@tilery/client
Client library for Tilery.eu tile API with service worker caching and automatic token refresh.
Features
- Token Management - Automatic token refresh before expiry
- Service Worker Caching - Long-lived tile cache that survives token rotation
- No CORS Preflight - Token passed as query parameter, not header
- MapLibre Integration - Get ready-to-use style objects
The SDK implements only the JWT/token-exchange flow — that's the safe path for browsers (15-minute lifetime, origin-locked, plays well with the service-worker cache). The Tilery API also accepts an app-mode API key sent directly as
Authorization: Bearer …for trusted backends, but that path skips the SDK entirely (and skips the cache). If you reach for it from a browser, you've left the safe envelope this SDK provides — see the security model before doing so.
Installation
npm install @tilery/clientThe package has zero runtime dependencies. MapLibre style JSON (including all layer definitions) is fetched directly from the tile server.
Quick Start
import { TileryClient } from "@tilery/client";
import maplibregl from "maplibre-gl";
// Create client with your token fetcher
const client = new TileryClient({
getToken: async () => {
// Your backend exchanges API key for short-lived JWT
const res = await fetch("/api/tile-token", { credentials: "include" });
if (!res.ok) throw new Error("Failed to get token");
return res.json(); // { token: string, exp: number }
},
});
// Register service worker for tile caching
await client.registerServiceWorker();
// Create map with auto-generated style
const map = new maplibregl.Map({
container: "map",
style: await client.getMapStyle({ flavor: "dark" }),
center: [10.75, 59.91],
zoom: 12,
});Service Worker Setup
The service worker code is bundled in the library. Create a file in your public directory:
Option 1: Copy from built library
# The library exports the compiled SW code
node -e "import('@tilery/client').then(m => console.log(m.SERVICE_WORKER_CODE))" > public/tilery-sw.jsOption 2: Build script
// scripts/build-sw.ts
import { SERVICE_WORKER_CODE } from "@tilery/client";
import { writeFileSync } from "fs";
writeFileSync("public/tilery-sw.js", SERVICE_WORKER_CODE);Then register it:
await client.registerServiceWorker();The service worker intercepts tile requests and:
- Strips the
?token=query parameter to create a normalized cache key - Checks the cache using the normalized key
- On cache miss, fetches with the original URL (including token)
- Stores the response under the normalized key
This means tiles stay cached even when your token rotates!
API Reference
TileryClient
Constructor Options
interface TileryClientOptions {
// Required: async function that returns { token, exp }
getToken: () => Promise<{ token: string; exp: number }>;
// Optional: tile server URL (default: 'https://api.tilery.eu').
// Use 'https://api.test.tilery.eu' to point at the staging environment.
tileServer?: string;
// Optional: seconds before expiry to refresh (default: 60)
refreshBuffer?: number;
// Optional: path to service worker (default: '/tilery-sw.js')
serviceWorkerPath?: string;
}Methods
// Get current token (fetches if needed, cached otherwise)
const token = await client.getToken();
// Register service worker
await client.registerServiceWorker();
// Get MapLibre-compatible style (fetched from the tile server, token injected)
const style = await client.getMapStyle({
flavor: "light", // see GET {tileServer}/map/styles for the full list
});
// Get URL templates
const tileUrl = await client.getTileUrlTemplate();
const glyphsUrl = client.getGlyphsUrl();
const spriteUrl = client.getSpriteUrl("dark");
// Clean up
client.destroy();Events
client.on("token:refresh", (token) => {
console.log("Token refreshed, expires at:", token.exp);
});
client.on("token:error", (error) => {
console.error("Token refresh failed:", error);
});
client.on("sw:registered", (registration) => {
console.log("Service worker registered");
});
client.on("sw:error", (error) => {
console.error("Service worker failed:", error);
});Backend Token Endpoint
Your backend should exchange an API key for a short-lived JWT. Example with the Tilery API:
// /api/tile-token.ts (your backend)
export async function GET(request: Request) {
const res = await fetch("https://tilery.eu/api/tokens/exchange", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TILERY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ ttl: 900 }), // 15 minutes
});
return res.json(); // { token: string, exp: number }
}Manual Usage (without MapLibre helper)
const client = new TileryClient({
getToken: async () => fetch("/api/tile-token").then((r) => r.json()),
});
// Get token and build URLs manually
const token = await client.getToken();
const tileUrl = `https://api.tilery.eu/tiles/{z}/{x}/{y}?token=${token.token}`;
// Use with any mapping library
const map = new maplibregl.Map({
container: "map",
style: {
version: 8,
sources: {
tiles: {
type: "vector",
tiles: [tileUrl],
maxzoom: 14,
},
},
layers: [
/* your layers */
],
},
});Service Worker Messages
You can communicate with the service worker for cache management:
// Clear all cached tiles
navigator.serviceWorker.controller?.postMessage({ type: "CLEAR_CACHE" });
// Get cache size
const channel = new MessageChannel();
channel.port1.onmessage = (e) => console.log("Cache size:", e.data.size);
navigator.serviceWorker.controller?.postMessage({ type: "GET_CACHE_SIZE" }, [channel.port2]);License
MIT
