feat(api-client): typed fetch-based client for the mrrmlab API #15
No reviewers
Labels
No labels
app/archiv
app/einkaufslisten
app/imap-client
app/wissensbasis
arch-answered
arch-question
area/api
area/auth
area/infra
area/mobile
area/shared
area/ui
area/web
portfolio-status
prio/high
prio/low
prio/medium
roadmap/public
size/l
size/m
size/s
size/xl
size/xs
status/blocked
status/needs-info
type/bug
type/chore
type/docs
type/feature
type/idea
type/refactor
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
admin-mrrm/mrrmlabapp!15
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/api-client"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
@mrrmlab/api-clientfetch— works in Node 22+, browsers, React Native@mrrmlab/shared-typesschemas (catches API drift)ApiErrorfor non-2xx,ApiResponseValidationErrorfor schema mismatchesgetTokencallback so the upcoming auth package can wire OIDC inUsage
Test plan
pnpm --filter @mrrmlab/api-client buildsucceedsApiResponseValidationErroron 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