batchCooking/apps/api/src/app.ts
Nicolas dfeb6fd1ac API: GET /planning/current (home planning + sidebar, step 1/5)
- packages/shared: PlanningView/PlanningItemView, exported.
- apps/api: planning module (service + route), mounted at /planning.
  GET /planning/current returns the authenticated user's household's
  planning covering today, or null (no error) when there isn't one yet —
  the expected state until planning creation exists.
- Tests: Mocha (apps/api/test/planning.test.ts) + Cucumber
  (features/planning.feature), same conventions as auth.
- packages/express-tools: fixed AsyncRequestHandler/wrapAsyncHandler's
  Locals generic constraint (Record<string, unknown> -> Record<string,
  any>, matching Express's own Response<ResBody, LocalsObj>) — the first
  endpoint combining requireAuth/AuthLocals with an async handler exposed
  that the stricter constraint rejected plain interfaces Response itself
  accepts fine.
- Docs: README.md ("Planning" section) + specs/backend-architecture.md.

First commit of the home-page-after-login feature (see plan discussed in
chat) — frontend layout/routing/HomePage follow in subsequent commits on
this same branch/PR.
2026-08-16 20:46:44 +02:00

51 lines
2.1 KiB
TypeScript

import { errorHandlerService } from "@batch-cooking/error-tools";
import { ExpressServer, createErrorMiddleware } from "@batch-cooking/express-tools";
import { ErrorCode } from "@batch-cooking/shared";
import type { Express, Request, Response } from "express";
import { env } from "./config/env.js";
import { authRouter } from "./modules/auth/auth.routes.js";
import { planningRouter } from "./modules/planning/planning.routes.js";
/**
* Builds the API's `ExpressServer`: standard middleware, routes, and the
* final error handler, in that order. Returns the `ExpressServer` wrapper
* (not just the raw Express app) so `server.ts` can call `.listen()` on
* it — {@link createApp} below is the thinner entry point that exposes
* just the raw `Express` instance, for test tooling (supertest) that
* expects one.
*/
export function createServer(): ExpressServer {
const server = new ExpressServer();
server.setupCore({ corsOrigin: env.CORS_ORIGIN });
server.addRoute("get", "/health", (_req: Request, res: Response) => {
res.status(200).json({ status: "ok" });
});
server.mountRouter("/auth", authRouter);
server.mountRouter("/planning", planningRouter);
// No route matched — same shape as every other error response, via the
// shared ErrorCode contract, so clients never special-case 404s.
server.addMiddleware((_req: Request, res: Response) => {
res.status(404).json({ code: ErrorCode.NOT_FOUND, message: "Not found" });
});
// Final error-handling middleware: every thrown/`next(err)`-ed error in
// the app ends up here. All the "what status/body does this error map
// to" logic lives in ErrorHandlerService, from @batch-cooking/error-tools
// — this stays a thin adapter.
server.setErrorHandler(createErrorMiddleware(errorHandlerService));
return server;
}
/**
* Builds a fresh Express application instance (no shared mutable state
* between calls — used both indirectly by the real server entrypoint
* (`server.ts`, via {@link createServer}) and directly by tests, which
* each get their own app via supertest).
*/
export function createApp(): Express {
return createServer().instance;
}