# events-routes _(no submodules)_ ## Purpose Builds the nine-route OpenAPI Hono module mounted by `createEventsPlugin`. Each route is wrapped by `@hono/zod-openapi`'s `createRoute` so the package's local Zod schemas drive both input-validation and generated OpenAPI doc shape. The route layer is the single point at which auth (`c.get("user")`), authorisation (`eventRepository.isCreator`), `maxInvitesPerRequest` enforcement, OTel emission, and EventBus fan-out converge. The handlers themselves are deliberately thin: every meaningful side-effect (CREATE row, UPDATE counter, DELETE row, EMIT event) funnels through the two repositories and the optional `emitEvent` callback. The callback is the integration point the plugin's `onReady` uses to forward route events onto the shared `ctx.eventBus` — that indirection is what fixes the upstream "local emitter" smell flagged in `enterprise/api-plugin-events.md`. ## Features - 9 routes: list / get / create / update / delete / rsvp / cancel-rsvp / my-rsvp / attendees / invite. - Zod validation on every body / param / query. - Auth guard on every route via `c.get("user")`. - Authorisation guard on every mutating route via `eventRepository.isCreator`. - `maxInvitesPerRequest` enforcement inside the `/invite` handler. - OTel counter emission on every successful mutation. - Typed `emitEvent` callback for cross-plugin fan-out. ## Usage: Basic Example ```ts import { createEventRoutes } from "@suaveplan/api-plugin-events"; import { OpenAPIHono } from "@hono/zod-openapi"; const routes = createEventRoutes({ eventRepository, rsvpRepository, maxInvitesPerRequest: 50, }); const parent = new OpenAPIHono(); parent.use(async (c, next) => { c.set("user", await resolveUser(c)); await next(); }); parent.route("/events", routes); ``` ## Usage: Advanced Example ```ts import { createEventRoutes } from "@suaveplan/api-plugin-events"; const routes = createEventRoutes({ eventRepository, rsvpRepository, maxInvitesPerRequest: 25, emitEvent: (event) => { // Forward onto the host's typed bus host.eventBus.emit(event.type, event.payload); // Side-channel: persistent audit log auditLog.write({ kind: event.type, payload: event.payload, at: new Date(), }); }, }); // `routes` is an OpenAPIHono instance — feed it into the OpenAPI // document generator: host.openAPIRegistry.attach(routes); ``` ## API Reference ### `createEventRoutes(config: EventRoutesConfig): OpenAPIHono` Returns an `OpenAPIHono<{ Variables: EventsRoutesVariables }>` with the nine routes registered. ### `interface EventRoutesConfig` - `eventRepository: IEventRepository` - `rsvpRepository: IRsvpRepository` - `maxInvitesPerRequest: number` - `emitEvent?: (event: EventsRouteEvent) => void` ### `interface EventsRoutesVariables` `{ user: { id: string } & Record | null | undefined }` — the route handler reads `c.get("user")` from this slot; the host supplies the auth middleware that populates it. ## Implementation Notes The handlers manually `try { getUser(c) } catch { return c.json(...) }` rather than relying on Hono's `onError` because the OpenAPI route spec requires every documented response status to be reachable from the handler body. Catching `EventsPluginError(UNAUTHENTICATED)` and returning a 401 inline keeps the spec honest. The pattern repeats on every route — verbose, but each block is a single 3-line `try`/`catch`/`json` triple. The `app.route("/", routes)` line in test fixtures and in the plugin mount mirrors the parent/child Hono pattern: the parent app owns the auth middleware, the child app owns the routes. Hono middleware registered on the parent BEFORE the `.route()` call fires before the child's handlers, so `c.get("user")` resolves correctly during the child's `c.req.valid(...)` parsing phase.