Files
skills/docs/api-stack/api/api-openapi/src/schema-registry/common-schemas.md
2026-05-29 08:20:56 +01:00

3.6 KiB

common-schemas

Purpose

Library of common Zod schemas decorated with OpenAPI example metadata. Covers the universal HTTP query / path / pagination primitives that every API plugin reaches for — UUID, email, URL, ISO datetime, signed and unsigned integers, page/limit/sort order. Pulling these into a single module ensures every plugin documents the same constraints, prevents drift, and makes the OpenAPI spec consistent across the entire multi-plugin server.

The schemas are exported as a single CommonSchemas const object so consumers can spread, destructure, or .openapi(...)-extend any subset. They are deliberately not pre-registered against any SchemaRegistry — each consumer chooses whether (and under which name) to register them.

Features

  • uuid, email, url, datetime — string formats with examples.
  • positiveInt, nonNegativeInt — integer constraints.
  • page / limit — coercing pagination inputs with sane defaults.
  • sortOrder — "asc" | "desc" enum, default "desc".
  • Every entry carries an OpenAPI example so the generated spec is rich.

Usage: Basic

import z from "zod";
import { CommonSchemas } from "@suaveplan/api-openapi/schema-registry";

const ListQuery = z.object({
    page: CommonSchemas.page,
    limit: CommonSchemas.limit,
    sort: CommonSchemas.sortOrder,
});

Usage: Advanced

import z from "zod";
import {
    CommonSchemas,
    createSchemaRegistry,
} from "@suaveplan/api-openapi/schema-registry";

const registry = createSchemaRegistry();

// Register pagination primitives once under shared names so OpenAPI emits
// `#/components/schemas/pagination.page` references rather than inlining
// the same constraint in every operation.
registry.register("pagination.page", CommonSchemas.page);
registry.register("pagination.limit", CommonSchemas.limit);
registry.register("pagination.sortOrder", CommonSchemas.sortOrder);

const ListUsersQuery = z.object({
    page: registry.get("pagination.page")!,
    limit: registry.get("pagination.limit")!,
    sort: registry.get("pagination.sortOrder")!,
    q: z.string().optional(),
});

API Reference

  • CommonSchemas.uuid: ZodString — UUID string with example.
  • CommonSchemas.email: ZodString — email string.
  • CommonSchemas.url: ZodString — URL string.
  • CommonSchemas.datetime: ZodString — ISO-8601 datetime.
  • CommonSchemas.positiveInt: ZodNumber — > 0, integer.
  • CommonSchemas.nonNegativeInt: ZodNumber — ≥ 0, integer.
  • CommonSchemas.page: ZodCoerce<number> — min 1, default 1.
  • CommonSchemas.limit: ZodCoerce<number> — min 1, max 100, default 20.
  • CommonSchemas.sortOrder: ZodEnum<"asc" | "desc"> — default "desc".

Implementation Notes

page and limit use z.coerce.number() rather than z.number() so they accept query-string inputs (always strings on the wire). Without coercion every plugin would have to wrap them in .transform(Number) manually, defeating the point of a shared catalogue.

The .openapi({ example: ... }) annotation surfaces in the generated spec without affecting validation. Consumers that want richer descriptions ({ description: "..." }) call .openapi(...) on a copy rather than mutating the shared schema.

sortOrder defaults to "desc" because the most common pagination pattern is "latest first". Plugins that want a different default override locally with CommonSchemas.sortOrder.default("asc").

CommonSchemas is as const — TypeScript surfaces every field with its narrowest literal type. Consumers can typeof CommonSchemas to derive per-field types without importing each schema individually.