From 37e5539e5ae46f1b0ddd048d42c402caea83af2d Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 22 Apr 2026 16:04:25 -0400 Subject: [PATCH] feat(web): add api.{get,post,del} typed facade over apiFetch --- web/src/lib/api/client.test.ts | 26 +++++++++++++++++++++++++- web/src/lib/api/client.ts | 8 ++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/web/src/lib/api/client.test.ts b/web/src/lib/api/client.test.ts index 891bbc2a..002cb595 100644 --- a/web/src/lib/api/client.test.ts +++ b/web/src/lib/api/client.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; -import { apiFetch, type ApiError } from './client'; +import { api, apiFetch, type ApiError } from './client'; afterEach(() => { vi.unstubAllGlobals(); @@ -55,3 +55,27 @@ describe('apiFetch', () => { }); }); }); + +describe('api.get/post/del', () => { + test('api.get returns typed JSON on 200', async () => { + stubFetch(200, { id: 'abc', name: 'A' }); + type T = { id: string; name: string }; + const result = await api.get('/api/artists/abc'); + expect(result).toEqual({ id: 'abc', name: 'A' }); + }); + + test('api.post serializes body and sets method', async () => { + const spy = stubFetch(200, { ok: true }); + await api.post('/api/auth/login', { username: 'u', password: 'p' }); + const init = spy.mock.calls[0][1] as RequestInit; + expect(init.method).toBe('POST'); + expect(init.body).toBe(JSON.stringify({ username: 'u', password: 'p' })); + }); + + test('api.del sends DELETE and resolves to null on 204', async () => { + const spy = stubFetch(204, null); + const result = await api.del('/api/sessions/xyz'); + expect(spy.mock.calls[0][1]).toMatchObject({ method: 'DELETE' }); + expect(result).toBeNull(); + }); +}); diff --git a/web/src/lib/api/client.ts b/web/src/lib/api/client.ts index c7707d60..87c0fbf7 100644 --- a/web/src/lib/api/client.ts +++ b/web/src/lib/api/client.ts @@ -33,3 +33,11 @@ export async function apiFetch(path: string, init?: RequestInit): Promise(path: string): Promise => apiFetch(path) as Promise, + post: (path: string, body: unknown): Promise => + apiFetch(path, { method: 'POST', body: JSON.stringify(body) }) as Promise, + del: (path: string): Promise => + apiFetch(path, { method: 'DELETE' }) as Promise +};