first commit
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# 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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user