refactor: migrate all page.server.ts files to typed API client

All server-side fetch calls now go through createApiClient() from
$lib/api.server.ts, which wraps openapi-fetch with the generated OpenAPI
types. This means backend changes are reflected in the frontend after
running npm run generate:api.

- Add stub src/lib/generated/api.ts (replaced by generate:api output)
- Fix GroupController: missing /api prefix and ResponseStatusException
- Root, conversations, persons, documents pages all use typed client
- Error handling uses apiError.code directly (no parseBackendError needed)
- Edit page load uses typed client; PUT action keeps raw fetch (multipart)
- Login keeps raw fetch (explicit Authorization header, not cookie auth)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-15 13:39:15 +01:00
parent 5d356cd694
commit d76248cffd
11 changed files with 220 additions and 259 deletions

View File

@@ -1,24 +1,21 @@
import { error, redirect } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
import { parseBackendError, getErrorMessage } from '$lib/errors';
import { createApiClient } from '$lib/api.server';
import { getErrorMessage } from '$lib/errors';
export async function load({ params, fetch }) {
const { id } = params;
const baseUrl = env.API_INTERNAL_URL || 'http://localhost:8080';
const api = createApiClient(fetch);
try {
const res = await fetch(`${baseUrl}/api/documents/${id}`);
const { data, error: apiError, response } = await api.GET('/api/documents/{id}', {
params: { path: { id } }
});
if (res.status === 401) throw redirect(302, '/login');
if (response.status === 401) throw redirect(302, '/login');
if (!res.ok) {
const backendError = await parseBackendError(res);
throw error(res.status, getErrorMessage(backendError?.code));
}
return { document: await res.json() };
} catch (e) {
if (e.status) throw e;
throw error(500, getErrorMessage('INTERNAL_ERROR'));
if (apiError) {
const code = (apiError as { code?: string })?.code;
throw error(response.status, getErrorMessage(code));
}
return { document: data };
}

View File

@@ -1,37 +1,35 @@
import { error, fail, redirect } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
import { createApiClient } from '$lib/api.server';
import { parseBackendError, getErrorMessage } from '$lib/errors';
export async function load({ params, fetch }) {
const { id } = params;
const baseUrl = env.API_INTERNAL_URL || 'http://localhost:8080';
const api = createApiClient(fetch);
try {
const [docRes, personsRes] = await Promise.all([
fetch(`${baseUrl}/api/documents/${id}`),
fetch(`${baseUrl}/api/persons`)
]);
const [docResult, personsResult] = await Promise.all([
api.GET('/api/documents/{id}', { params: { path: { id } } }),
api.GET('/api/persons')
]);
if (!docRes.ok) {
const backendError = await parseBackendError(docRes);
throw error(docRes.status, getErrorMessage(backendError?.code));
}
if (!personsRes.ok) {
throw error(personsRes.status, getErrorMessage('INTERNAL_ERROR'));
}
return {
document: await docRes.json(),
persons: await personsRes.json()
};
} catch (e) {
if (e.status) throw e;
throw error(500, getErrorMessage('INTERNAL_ERROR'));
if (docResult.error) {
const code = (docResult.error as { code?: string })?.code;
throw error(docResult.response.status, getErrorMessage(code));
}
if (personsResult.error) {
throw error(personsResult.response.status, getErrorMessage('INTERNAL_ERROR'));
}
return {
document: docResult.data,
persons: personsResult.data
};
}
export const actions = {
default: async ({ request, params, fetch }) => {
// Raw fetch is used here because FormData multipart bodies are passed through
// directly from the browser without transformation.
const baseUrl = env.API_INTERNAL_URL || 'http://localhost:8080';
const formData = await request.formData();