feat(api-client): typed fetch-based client for the mrrmlab API #15

Merged
admin-mrrm merged 1 commit from feat/api-client into main 2026-04-15 09:37:22 +02:00
Owner

Summary

  • New workspace package @mrrmlab/api-client
  • Typed wrapper around every endpoint exposed by the NestJS API
  • Built on platform fetch — works in Node 22+, browsers, React Native
  • Response bodies validated against @mrrmlab/shared-types schemas (catches API drift)
  • ApiError for non-2xx, ApiResponseValidationError for schema mismatches
  • Pluggable getToken callback so the upcoming auth package can wire OIDC in

Usage

const client = createApiClient({
  baseUrl: 'https://api.mrrmlab.example',
  getToken: () => authStore.getAccessToken(),
});
const list = await client.shoppingLists.create({ title: 'Wocheneinkauf' });
await client.shoppingLists.items.add(list.id, { label: 'Milch' });

Test plan

  • pnpm --filter @mrrmlab/api-client build succeeds
  • End-to-end smoke against running API: health, list CRUD, item CRUD, ApiError on 404 / 400
  • Response schemas validate (would throw ApiResponseValidationError on drift)

Closes #7

## Summary - New workspace package `@mrrmlab/api-client` - Typed wrapper around every endpoint exposed by the NestJS API - Built on platform `fetch` — works in Node 22+, browsers, React Native - Response bodies validated against `@mrrmlab/shared-types` schemas (catches API drift) - `ApiError` for non-2xx, `ApiResponseValidationError` for schema mismatches - Pluggable `getToken` callback so the upcoming auth package can wire OIDC in ## Usage ```ts const client = createApiClient({ baseUrl: 'https://api.mrrmlab.example', getToken: () => authStore.getAccessToken(), }); const list = await client.shoppingLists.create({ title: 'Wocheneinkauf' }); await client.shoppingLists.items.add(list.id, { label: 'Milch' }); ``` ## Test plan - [x] `pnpm --filter @mrrmlab/api-client build` succeeds - [x] End-to-end smoke against running API: health, list CRUD, item CRUD, ApiError on 404 / 400 - [x] Response schemas validate (would throw `ApiResponseValidationError` on drift) Closes #7
New workspace package @mrrmlab/api-client. Wraps every endpoint exposed
by the NestJS backend in a typed, validated method tree consumed via:

    const client = createApiClient({ baseUrl, getToken });
    const list = await client.shoppingLists.create({ title: '...' });
    await client.shoppingLists.items.add(list.id, { label: '...' });

Design notes:
- Built on globalThis.fetch (Node 22+, browsers, RN) with an injectable
  `fetch` for tests / RN polyfills. No HTTP library dependency.
- `getToken` callback runs on every request — refresh logic, OIDC, and
  storage stay in the consumer (auth package will plug in here).
- Response bodies are parsed against the same Zod schemas the API
  validates inputs with (`@mrrmlab/shared-types`). Mismatches throw
  ApiResponseValidationError, so backend drift surfaces immediately
  instead of leaking `undefined` into the UI.
- Non-2xx responses throw ApiError with status + parsed body. 204s and
  explicit `expectEmpty` skip body parsing.
- Every method takes an optional AbortSignal.

Build setup mirrors @mrrmlab/shared-types: CommonJS dist/ with
tsBuildInfoFile inside dist/ so it stays consistent with deleteOutDir.

Smoke-tested end-to-end against the running API: health, full shopping
list + items CRUD round-trip, ApiError on 404 after delete, ApiError
on 400 for empty title.

Closes #7
admin-mrrm deleted branch feat/api-client 2026-04-15 09:37:22 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
admin-mrrm/mrrmlabapp!15
No description provided.