Files
backend/docs/typescript-frontend-integration.md
Andriy Oblivantsev a5a97a0ad9
CI / test (push) Successful in 5s
Add register-by-signature, web fixes, bin scripts, docs
- Register by signing service key: GET /v1/service-key, POST /v1/auth/register-by-signature
- Login auto-attempts register first for new users
- Web: default API URL momswap.produktor.duckdns.org, /libs/ static handler
- Docker: webbuild stage for geo-api-client, copy web+libs to runtime
- Bin scripts: test.sh, run.sh, up.sh, down.sh
- docs/ed25519-security-use-cases.md: use cases, message formats, examples
- SERVICE_PUBLIC_KEY env (defaults to ADMIN_PUBLIC_KEY)

Made-with: Cursor
2026-03-01 12:59:02 +00:00

3.9 KiB

TypeScript Frontend Integration Guide

This document explains how frontend developers should integrate with the backend through the reusable TypeScript client at libs/geo-api-client.

Primary backend URL for integration:

  • https://momswap.produktor.duckdns.org/

Deployment: API is proxied via reverse proxy from https://momswap.produktor.duckdns.org to backend at 172.17.0.1:8122. Docker Compose maps port 8122 for the reverse proxy.

Goals

  • Keep cryptographic signing logic in one place.
  • Avoid duplicating API request code in frontend apps.
  • Use a consistent local key storage format across projects.

Client package location

  • Source: libs/geo-api-client/src
  • Entry point: libs/geo-api-client/src/index.ts
  • Build output (browser ESM): libs/geo-api-client/dist/index.js

Build and test the client

cd libs/geo-api-client
bun install
bun test
bun run build

Public API (current)

Class: GeoApiClient

Constructor:

  • new GeoApiClient(baseUrl, storage, storageKey?)

Key methods:

  • ensureKeysInStorage()
  • getStoredKeys()
  • importKeys(keys)
  • exportKeys()
  • setAccessToken(token)
  • getServicePublicKey()
  • createChallenge(publicKey)
  • loginWithSignature(publicKey, privateKey)
  • registerBySigningServiceKey(publicKey, privateKey) — register by signing the API service public key (no invitation required)
  • createInvitation(payload, inviterPrivateKey)
  • registerWithInvitation(...)
  • listCollections()
  • createCollection(name)
  • listFeatures(collectionId)
  • createPointFeature(collectionId, lon, lat, properties)
  1. Create one GeoApiClient instance per backend base URL.
  2. Call ensureKeysInStorage() when app initializes.
  3. If not yet registered: call registerBySigningServiceKey(publicKey, privateKey) (signs the API service key and publishes your public key).
  4. Use loginWithSignature() to obtain and set a bearer token.
  5. Call collection/feature methods after authentication.
  6. Use importKeys/exportKeys in profile settings UX.

Registration by signing service key

When SERVICE_PUBLIC_KEY (or ADMIN_PUBLIC_KEY) is set, users can register without an invitation:

  1. GET /v1/service-key — fetch the API public key to sign.
  2. Sign that key with your private key.
  3. POST /v1/auth/register-by-signature with { publicKey, signature }.

Example (TypeScript app)

import { GeoApiClient } from "../libs/geo-api-client/dist/index.js";

const storage = window.localStorage;

const storageLike = {
  getItem: (key: string) => storage.getItem(key),
  setItem: (key: string, value: string) => storage.setItem(key, value),
  removeItem: (key: string) => storage.removeItem(key),
};

const client = new GeoApiClient("https://momswap.produktor.duckdns.org", storageLike);

const keys = await client.ensureKeysInStorage();
await client.loginWithSignature(keys.publicKey, keys.privateKey);

const created = await client.createCollection("My Places");
await client.createPointFeature(created.id, -16.6291, 28.4636, { name: "Santa Cruz" });
const features = await client.listFeatures(created.id);
console.log(features);

Security notes

  • Private keys are currently stored in browser storage via the selected storage adapter.
  • If your frontend has stronger security requirements, wrap the storage adapter with your own encryption/decryption layer before calling setItem/getItem.
  • Never send private keys to the backend.

No-build frontend compatibility

For no-bundler apps, import the built ESM file:

<script type="module">
  import { GeoApiClient } from "../libs/geo-api-client/dist/index.js";
  // use client...
</script>

The backend itself serves static UI at /web/, but this library can be consumed by any frontend runtime that supports fetch, TextEncoder, and ES modules.

For local development you can switch the client base URL to http://localhost:8122.