@nifrajs/schema
v3.1.0
Published
Built-in schema builder `t` - TypeBox-backed (free JSON Schema + compiled validators), implements Standard Schema so nifra routes validate it natively.
Downloads
3,821
Maintainers
Readme
@nifrajs/schema
The optional, batteries-included schema builder for nifra: t is
TypeBox-backed, so it validates at the boundary and gives you OpenAPI for
free (a TypeBox schema is a JSON Schema). Plus toOpenAPI(contract | app).
bun add @nifrajs/schemaimport { server } from "@nifrajs/core/server"
import { t, toOpenAPI } from "@nifrajs/schema"
const app = server().post("/users", { body: t.object({ name: t.string(), age: t.integer() }) }, (c) => ({
id: "u1",
name: c.body.name, // typed + validated
}))
const openapi = toOpenAPI(app) // OpenAPI 3.1tbuilder -string/number/integer/boolean/null/literal/object/array/optional/union/record, with options (min/max,pattern,format, …) that become JSON Schema constraints. Validators are compiled (fast).- Validating string formats -
email/uuid/date-time/date/time/uri/ipv4validate and annotate; register more withregisterFormat. toOpenAPI- richest from a contract (it carriesresponseschemas + op names →operationIds); also works on a live app. Routes using a BYO Standard Schema are emitted without a detailed schema (Standard Schema exposes no JSON Schema).
t vs bring-your-own (bundle size)
t is the batteries-included default because a TypeBox schema is a JSON Schema: it gives you
OpenAPI + MCP tools/list for free and compiles to a fast validator. That completeness has a
cost - TypeBox carries the whole JSON Schema type system, so t adds meaningful bytes to a bundle.
nifra validates through Standard Schema, so any Standard-Schema validator works on a route with
no adapter - { body: v.object({ ... }) } with valibot, for instance. A validated app built on a
lean validator like valibot is a fraction of the size (measured ~16 KB gzipped), which matters for
edge/Workers cold-starts. The trade is the one noted above: a bring-your-own validator exposes no
JSON Schema, so those routes carry no OpenAPI/MCP detail.
Rule of thumb: reach for t when you want the contract (OpenAPI/MCP) for free; bring valibot (or any
Standard-Schema validator) when bundle size on the edge is the priority. Both are first-class - the
choice is per route, and you can mix them in one app.
toOpenAPI coverage
It emits paths, parameters (path + object query), requestBody, and responses, plus:
servers, top-leveltags, and an infodescription(document options).securitySchemes→components.securitySchemes, a document-widesecurity, and per-operationsecurity([]marks an operation explicitly public).- Non-200 responses and non-JSON content - a contract op's
responsesmap declares extra status codes;requestContentType/responseContentTypeset media types other thanapplication/json. $refreuse - a schema with a$id(t.object({…}, { $id: "User" })) is hoisted intocomponents.schemasonce and referenced by$refeverywhere it's used.- Per-operation
summary,description,tags,deprecated- declared on the contract op.
A contract is richest (its ops carry all of the above); in app mode a route's response is a
generic 200 with no schema - declare a defineContract with response schemas, or pass
options.operations (keyed by "METHOD /path") to enrich app routes. Routes using a BYO Standard Schema
are still emitted without body/response detail (Standard Schema exposes no JSON Schema).
@nifrajs/core is a peer dependency. ESM-only. MIT.
For AI agents
Start with LLM.md - this package's contract card (the exports you call + its footguns),
one cheap read instead of the whole corpus. For the wider framework: the repo's
AGENTS.md is the copy-paste quick reference, and
llms-full.txt is the full machine-readable corpus. Run nifra check as the
done-gate, or nifra mcp to give the agent live project tools.
