Files
skills/docs/api-stack/api/api-plugin-import/src/routes/build-routes.md
2026-05-29 08:20:56 +01:00

2.7 KiB

routes/build-routes — auth-gated import routes

Purpose

Constructs the OpenAPIHono router mounted by createImportPlugin at the configured routePrefix. Five routes are registered — POST /preview, POST /, GET /, GET /{id}, DELETE /{id} — each gated behind requireAuth() from @suaveplan/api-plugin-auth so anonymous requests return 401 problem+json before they ever reach a handler. Owner-scoped routes read c.get("user").id and pass it through to the canonical ImportService, which raises IMPORT.NOT_FOUND (mapped to 404 problem+json) on foreign-owner lookups so the surface is anti-enumeration-safe.

Features

  • Five OpenAPI routes with full Zod request / response schemas.
  • Per-route app.use("/*", requireAuth()) guard.
  • progressToResponse() shape mapper so the response body always satisfies ImportJobResponseSchema.
  • Every handler funnels error paths through respondProblemJson so the response shape is uniform across success and failure.

Usage: Basic Example

import { createImportRoutes, ImportService } from "@suaveplan/api-plugin-import";
import { OpenAPIHono } from "@hono/zod-openapi";

const service = new ImportService();
const app = new OpenAPIHono();
app.route("/imports", createImportRoutes({ service }));

Usage: Advanced Example

import { createImportRoutes, ImportService, MemoryImportStore } from "@suaveplan/api-plugin-import";
import { OpenAPIHono } from "@hono/zod-openapi";

const store = new MemoryImportStore();
const service = new ImportService({ store });
service.bus.on("import.progress", (snap) => sse.broadcast(snap));

const app = new OpenAPIHono();
app.use("/imports/*", customRateLimit({ window: "1m", max: 10 }));
app.route("/imports", createImportRoutes({ service }));

API Reference

  • createImportRoutes(deps: ImportRoutesDeps): OpenAPIHono
  • interface ImportRoutesDeps { service: ImportService }

Implementation Notes

  • The requireAuth() middleware is mounted via app.use("/*", ...) so every route inherits the guard. Mounting per-route would duplicate the wiring and miss the OpenAPI catch-all.
  • ownerOf(c) returns the empty string when c.get("user") is null; the auth middleware throws BEFORE the handler runs, so the empty string is unreachable from production traffic — but it remains a safe default if a caller somehow bypasses the middleware in a test harness.
  • The list endpoint returns { items, total } rather than a raw array so clients can paginate; the schema enforces total >= 0.
  • The delete endpoint surfaces a { deleted: boolean } payload — a successful delete returns { deleted: true }; a missing job throws IMPORT.NOT_FOUND from the service layer and is rendered as 404.