Angular Example

An Angular 19 application with standalone components that renders a MapLibre vector tile map and adds places search. Uses a companion Node.js server for token exchange.

Prerequisites

Quick Start

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

Add your API key to .env:

GEOG_API_KEY=your-api-key-here

Start both processes — the token proxy server and the Angular dev server:

# Terminal 1: Token proxy server (port 3001)
node server.js

# Terminal 2: Angular dev server (port 4200)
npm start

Open http://localhost:4200.

Project Structure

FileDescription
server.jsCompanion Node.js server (port 3001) — token exchange and places search proxy
proxy.conf.jsonDev server proxy config — routes /api/* from port 4200 to port 3001
src/app/services/geog.service.tsInjectable service — manages token cache and places search
src/app/components/map/map.component.tsStandalone component — MapLibre map with auth
src/app/components/places-search/places-search.component.tsStandalone component — search UI and markers
.env.exampleEnvironment variable template

How It Works

Two-Process Dev Setup

Angular's dev server runs on port 4200. A companion Node.js server on port 3001 handles the token proxy. The proxy.conf.json file routes /api/* requests from the Angular dev server to the companion server:

{
  "/api": {
    "target": "http://localhost:3001",
    "secure": false,
    "changeOrigin": true
  }
}

Injectable Service

GeogService is an Angular injectable that manages the token lifecycle and exposes methods for the map and search components:

@Injectable({ providedIn: "root" })
export class GeogService {
  private cachedToken: string | null = null;
  private tokenExpiresAt = 0;

  async getToken(): Promise<string> {
    const now = Date.now();
    if (this.cachedToken && now < this.tokenExpiresAt) {
      return this.cachedToken;
    }
    const response = await fetch("/api/geog-token");
    const data = await response.json();
    this.cachedToken = data.access_token;
    this.tokenExpiresAt = now + data.expires_in * 1000 * 0.8;
    return this.cachedToken;
  }

  async searchPlaces(query: string, lat: number, lon: number) {
    // ...
  }
}

Standalone Components

All components use standalone: true (no NgModules). The map component injects GeogService and initializes MapLibre in ngAfterViewInit with transformRequest for auth.

Full Source

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

See Also