Wire frontend into Docker Compose with type-safe API client
- Add frontend service to docker-compose.yml (port 3000, BACKEND_URL env var) - Add frontend/Dockerfile using adapter-node for plain Node/Docker runtime - Switch svelte.config.js from adapter-auto to adapter-node - Generate OpenAPI types from backend spec (openapi-typescript + openapi-fetch) - Add src/lib/server/api.ts as server-only typed API client factory - Add generate:api script to regenerate types when backend spec changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,5 +32,17 @@ services:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
container_name: mealprep-frontend
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
BACKEND_URL: http://app:8080
|
||||
depends_on:
|
||||
- app
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
|
||||
15
frontend/Dockerfile
Normal file
15
frontend/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM node:22-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/build build/
|
||||
COPY --from=build /app/node_modules node_modules/
|
||||
COPY package.json .
|
||||
EXPOSE 3000
|
||||
ENV NODE_ENV=production
|
||||
CMD ["node", "build"]
|
||||
4031
frontend/package-lock.json
generated
Normal file
4031
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
frontend/package.json
Normal file
41
frontend/package.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"prepare": "svelte-kit sync || echo ''",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:e2e": "playwright test",
|
||||
"generate:api": "curl -s http://localhost:8080/v3/api-docs -o src/lib/api/openapi.json && openapi-typescript src/lib/api/openapi.json -o src/lib/api/schema.d.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.59.1",
|
||||
"@sveltejs/adapter-auto": "^7.0.0",
|
||||
"@sveltejs/adapter-node": "^5.5.4",
|
||||
"@sveltejs/kit": "^2.50.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
||||
"@tailwindcss/vite": "^4.2.2",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/svelte": "^5.3.1",
|
||||
"@vitest/ui": "^4.1.2",
|
||||
"jsdom": "^29.0.1",
|
||||
"openapi-typescript": "^7.13.0",
|
||||
"svelte": "^5.54.0",
|
||||
"svelte-check": "^4.4.2",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"vitest": "^4.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"openapi-fetch": "^0.17.0"
|
||||
}
|
||||
}
|
||||
1
frontend/src/lib/api/openapi.json
Normal file
1
frontend/src/lib/api/openapi.json
Normal file
File diff suppressed because one or more lines are too long
1992
frontend/src/lib/api/schema.d.ts
vendored
Normal file
1992
frontend/src/lib/api/schema.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
19
frontend/src/lib/server/api.ts
Normal file
19
frontend/src/lib/server/api.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import createClient from 'openapi-fetch';
|
||||
import type { paths } from '$lib/api/schema.d.ts';
|
||||
import { env } from '$env/dynamic/private';
|
||||
|
||||
// Usage in +page.server.ts load functions and form actions:
|
||||
//
|
||||
// export const load = async ({ fetch }) => {
|
||||
// const api = apiClient(fetch);
|
||||
// const { data, error } = await api.GET('/v1/recipes', { ... });
|
||||
// };
|
||||
//
|
||||
// Always pass SvelteKit's `fetch` so session cookies are forwarded correctly.
|
||||
|
||||
export function apiClient(fetch?: typeof globalThis.fetch) {
|
||||
return createClient<paths>({
|
||||
baseUrl: env.BACKEND_URL ?? 'http://localhost:8080',
|
||||
fetch: fetch ?? globalThis.fetch
|
||||
});
|
||||
}
|
||||
24
frontend/svelte.config.js
Normal file
24
frontend/svelte.config.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import adapter from '@sveltejs/adapter-node';
|
||||
import { relative, sep } from 'node:path';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
compilerOptions: {
|
||||
// defaults to rune mode for the project, except for `node_modules`. Can be removed in svelte 6.
|
||||
runes: ({ filename }) => {
|
||||
const relativePath = relative(import.meta.dirname, filename);
|
||||
const pathSegments = relativePath.toLowerCase().split(sep);
|
||||
const isExternalLibrary = pathSegments.includes('node_modules');
|
||||
|
||||
return isExternalLibrary ? undefined : true;
|
||||
}
|
||||
},
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
Reference in New Issue
Block a user