SvelteKit Example

A SvelteKit 2 application with Svelte 5 that renders a MapLibre vector tile map and adds places search. Uses a SvelteKit server endpoint as the token proxy.

Prerequisites

Quick Start

git clone https://github.com/geogdev/examples.git
cd examples/frameworks/sveltekit-maplibre
npm install
cp .env.example .env

Add your API key to .env:

GEOG_API_KEY=your-api-key-here

Start the dev server:

npm run dev

Open http://localhost:5173.

Project Structure

FileDescription
src/routes/api/geog-token/+server.tsServer endpoint — token exchange with caching at 80% TTL
src/lib/geog.tsTyped API client with token exchange and places search functions
src/routes/+page.svelteMain page — map rendering and search UI in a single component
.env.exampleEnvironment variable template

How It Works

Server Endpoint

The API key stays server-side. A SvelteKit server endpoint handles token exchange:

// src/routes/api/geog-token/+server.ts
import { env } from "$env/static/private";

let cachedToken: TokenResponse | null = null;
let tokenExpiresAt = 0;

export const GET: RequestHandler = async () => {
  const now = Date.now();
  if (cachedToken && now < tokenExpiresAt) {
    return json({
      access_token: cachedToken.access_token,
      expires_in: cachedToken.expires_in,
    });
  }
  cachedToken = await exchangeToken(env.GEOG_API_KEY);
  tokenExpiresAt = now + cachedToken.expires_in * 1000 * 0.8;
  return json({
    access_token: cachedToken.access_token,
    expires_in: cachedToken.expires_in,
  });
};

SSR-Safe MapLibre Import

MapLibre requires the DOM, so it's imported dynamically inside onMount to avoid SSR errors:

<script lang="ts">
  import { onMount } from "svelte";

  let mapContainer: HTMLDivElement;

  onMount(async () => {
    const maplibregl = await import("maplibre-gl");

    // Fetch token from server endpoint
    const res = await fetch("/api/geog-token");
    const { access_token } = await res.json();

    const map = new maplibregl.Map({
      container: mapContainer,
      transformRequest: (url) => {
        if (url.startsWith("https://api.geog.dev")) {
          return {
            url,
            headers: { Authorization: `Bearer ${access_token}` },
          };
        }
      },
      // ...
    });
  });
</script>

Svelte 5 Runes

The example uses Svelte 5 runes for reactive state:

<script lang="ts">
  let query = $state("");
  let results: Place[] = $state([]);
  let isSearching = $state(false);
</script>

Full Source

github.com/geogdev/examples/tree/main/frameworks/sveltekit-maplibre

See Also