3.3 KiB
routes
Purpose
Defines the Zod request/response schemas the five management routes
mount when the plugin is configured with a TenantStore. Every body,
query, and response shape flows through these schemas so the OpenAPI
3.1 document published at /openapi.json is in lock-step with the
runtime behaviour — operators and SDKs generated from the spec see the
same surface the runtime enforces.
Features
TenantResponseSchema— wire-shape projection of the domainTenant(Dates serialised as ISO strings).TenantListResponseSchema— paginated list response (items+total).CreateTenantBodySchema/UpdateTenantBodySchema— strict body schemas; unknown keys are rejected.TenantIdParamSchema/ListTenantsQuerySchema— path-param and query schemas with coercion where appropriate (limit,offsetare coerced from query string numerals).- Slug validation regex: lowercase alphanumeric + hyphen, starting with an alphanumeric. Keeps URLs predictable and prevents subtle case-folding bugs in path-prefix resolution.
Usage: Basic Example
import { CreateTenantBodySchema } from "@suaveplan/api-plugin-tenant";
const result = CreateTenantBodySchema.safeParse({
name: "Acme",
slug: "acme",
});
if (!result.success) throw new Error(result.error.message);
Usage: Advanced Example
import {
CreateTenantBodySchema,
ListTenantsQuerySchema,
TenantResponseSchema,
} from "@suaveplan/api-plugin-tenant";
// Query coercion — limit/offset arrive as strings from the URL.
const query = ListTenantsQuerySchema.parse({
status: "active",
limit: "50",
offset: "0",
});
console.log(query.limit); // number, not string
// Round-trip a Tenant through the response schema.
const wire = TenantResponseSchema.parse({
id: "tenant_1",
name: "Acme",
slug: "acme",
status: "active",
settings: {},
enabledPlugins: ["billing"],
domains: [],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
// Reject invalid slug at the boundary.
const bad = CreateTenantBodySchema.safeParse({
name: "X",
slug: "ACME!", // uppercase + punctuation
});
console.assert(!bad.success);
API Reference
TenantResponseSchema— single-tenant wire shape.TenantListResponseSchema—{ items: TenantResponse[]; total: number }.CreateTenantBodySchema— strict object;name,slugrequired.UpdateTenantBodySchema— strict object; all fields optional.TenantIdParamSchema—{ id: string }.ListTenantsQuerySchema—{ status?, limit?, offset? }with numeric coercion onlimitandoffset.DeleteTenantResponseSchema—{ success: true }.- Type aliases (
CreateTenantBody,UpdateTenantBody, etc.) are exported for handler signatures.
Implementation Notes
strict() is applied to CreateTenantBodySchema, UpdateTenantBodySchema,
and the nested settings shape so unknown keys are rejected instead of
silently dropped. This matters because the same shape round-trips
through OpenAPI codegen on the client side — unknown keys on a strict
schema imply the client has drifted, and the right behaviour is to fail
loudly. The slug regex is intentionally conservative so URLs survive
percent-encoding, CDN normalisation, and case-insensitive DNS lookups
without ambiguity.