- Add test/integration.test.ts: getServicePublicKey, full flow (register, login, createCollection, createPointFeature, listFeatures) - Update docs example: registerBySigningServiceKey then loginWithSignature - Document integration tests in typescript-frontend-integration.md Made-with: Cursor
4.4 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 # unit + integration tests (docs flow)
bun run build
Integration tests in test/integration.test.ts cover the recommended flow: register, login, create collection, create point feature, list features.
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)
Recommended integration flow
- Create one
GeoApiClientinstance per backend base URL. - Call
ensureKeysInStorage()when app initializes. - If not yet registered: call
registerBySigningServiceKey(publicKey, privateKey)(signs the API service key and publishes your public key). - Use
loginWithSignature()to obtain and set a bearer token. - Call collection/feature methods after authentication.
- Use
importKeys/exportKeysin profile settings UX.
Registration by signing service key
When SERVICE_PUBLIC_KEY (or ADMIN_PUBLIC_KEY) is set, users can register without an invitation:
GET /v1/service-key— fetch the server public key (clients use this for registration and further signed communication).- Sign that key with your private key.
POST /v1/auth/register-by-signaturewith{ publicKey, signature }.
Server keys are generated with ./bin/gen-server-keys.sh and stored in etc/.
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();
// Register (ignored if already registered); then login
try {
await client.registerBySigningServiceKey(keys.publicKey, keys.privateKey);
} catch (_) {
// Already registered or registration disabled
}
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.