Transport JSON Schema Export
json-rest-schema can also export a transport-facing JSON Schema document for adapters that want pre-handler validation.
javascript
const userSchema = createSchema({
id: { type: 'id', required: true },
email: { type: 'string', required: true },
age: { type: 'number', min: 18, defaultTo: 18 },
status: { type: 'string', enum: ['draft', 'published'] }
})
const createTransportSchema = userSchema.toJsonSchema()
const patchTransportSchema = userSchema.toJsonSchema({ operation: 'patch' })Key points:
- Draft: exports draft-07 JSON Schema.
- Operation-aware:
operation: '<name>'controls therequiredlist and whetherdefaultTois emitted. - Compatibility:
mode: 'create' | 'replace' | 'patch'still works as shorthand for the built-in operations. - Transport-facing: the export is intended for JSON/Ajv/Fastify-style request validation, not for reproducing every in-process coercion path.
- Strict field shape:
additionalPropertiesdefaults tofalsebecause runtime validation rejects unknown fields. Override it withtoJsonSchema({ additionalProperties: true })if needed. - Nested export: schema-backed nested object contracts are hoisted into draft-07
definitionsand referenced with$ref, so repeated and recursive graphs stay finite. Nested object fields inherit the active operation, while object schemas used as array items or object-map values are exported inreplacemode. - Recursive graph support: runtime validation and
toJsonSchema()both support self-recursive schema graphs. Direct self-recursive object fields point back to#, while recursive nested contracts are emitted throughdefinitions. - Opaque bags stay opaque:
type: 'object'plusadditionalProperties: trueexports as a permissive object field and does not invent child property rules. - Single source of truth: only rules owned by
json-rest-schemaare exported. External metadata keys from other layers are ignored. - Passive metadata preserved: schema-owned passive metadata such as
precision,scale,unsigned, andtemporalPrecisionis preserved underx-json-rest-schema.metadata. - Custom rules: if a custom type or validator needs transport export support, attach a
toJsonSchema()hook to the handler. If you register a custom validator without that hook, export fails loudly instead of silently drifting.
Worked nested export example
javascript
const schema = createSchema({
workspace: {
type: 'object',
required: true,
schema: workspaceSummarySchema
},
roles: {
type: 'array',
items: roleSchema
},
metadata: {
type: 'object',
additionalProperties: true
}
})Exporting schema.toJsonSchema() gives you:
workspaceas a$refto a definition whoserequiredfields still inherit the active operationroles.itemsas a$refto a nested object definition exported inreplacemodemetadataas{ type: 'object', additionalProperties: true }- object maps as
{ type: 'object', additionalProperties: <value schema> } - passthrough nested objects as validated
propertiesplusadditionalProperties: true
That means the transport export stays aligned with runtime semantics:
- nested objects behave like nested contracts even when the runtime schema graph is recursive
- array object items behave like complete replacements
- opaque bags stay opaque instead of pretending to be structured
Worked recursive export example
Using the same recursive nodeSchema from the Recursive Schemas chapter above:
javascript
const transportSchema = nodeSchema.toJsonSchema()Key recursive export behaviors:
- a direct self-recursive object field such as
parentbecomes a reference back to# - recursive nested contracts reached through array items or dynamic object values are hoisted into
definitions - the exporter stays finite because it tracks schema graph nodes, not just call depth
For the parent field above, the transport shape is:
javascript
{
allOf: [
{
$ref: '#'
}
],
'x-json-rest-schema': {
castType: 'object'
}
}For the children.items edge, the transport shape becomes a $ref into definitions, and that referenced definition points back to itself for deeper children.items recursion.