Next.js app¶
An app is one Next.js app in one Docker image: the pages, the REST API and the MCP endpoint in the same process, over a framework-free domain layer and SQLite. TypeScript strict, Tailwind for styling, Vitest for unit tests, Playwright for end-to-end tests.
Why¶
One process and one image is what self-hosting on one server needs: nothing else to run, nothing to keep in step between services. Next.js serves the pages and the route handlers for /api/v1, /mcp and the sign-in from the same code, so the one domain layer stays one.
How¶
- Layout:
app/(pages and route handlers),components/,backend/(domain, framework-free, imported throughbackend/index.ts),docs/,e2e/. - Pinned exactly:
next,eslint-config-nextand the MCP SDK carry exact versions, not ranges; they move together through one Dependabot group. Next.js has breaking changes between minors: checknode_modules/next/dist/docs/before relying on a remembered API. - Scripts:
dev,build,start,lint(ESLint with the boundary rule andeslint-config-prettier),formatandformat:check(Prettier, Formatting and linting),typecheck(next typegen && tsc --noEmit),test(Vitest),test:e2e(Playwright),generate(OpenAPI from the route table, committed). - End-to-end tests run against
next dev, with a test-only sign-in route that exists only under an env flag and is compiled out of production builds. - Styling: Tailwind through
@tailwindcss/postcss; icons fromlucide-react. - Validation:
zodat every boundary (route input, tool input, config). - Logs:
pino, structured, English, never a token, a form or a person's details. - Image: multi-stage Dockerfile,
output: "standalone", a distroless Node runtime, aHEALTHCHECKon/health, data inDATA_DIRon one volume. The Node major lives in.nvmrcand the Dockerfile and moves by hand, in both at once. - Config: env vars
<APP>_*exceptPUBLIC_URL,DATA_DIR,PORT,TZ;backend/config.ts,.env.example,compose.yamland the configuration docs change together. - Sign-in: Better Auth, see Sign-in.
Rules out¶
- A second service (a sidecar API, a separate MCP server) for what one app can serve.
- Version ranges on
nextor the MCP SDK. - Business logic in
app/orcomponents/. - A test-only route reachable in a production build.
Reference¶
freundebuch (Next.js, SQLite, Tailwind, Vitest, Playwright, distroless image); questlog follows it (ADR 0001).