valrs

JSON Schema Generation

Export valrs schemas to JSON Schema format

JSON Schema Generation

valrs schemas can generate JSON Schema definitions for use with other tools, API documentation, and validation systems.

Generating JSON Schema

Use the .toJsonSchema() method to generate JSON Schema from any valrs schema:

import { v } from 'valrs';
 
// Generate JSON Schema for a string
const stringSchema = v.string().toJsonSchema();
// { type: 'string' }
 
// Generate JSON Schema for an integer
const intSchema = v.int32().toJsonSchema();
// { type: 'integer', minimum: -2147483648, maximum: 2147483647 }
 
// Generate JSON Schema for an object
const userSchema = v.object({
  name: v.string(),
  age: v.number(),
  email: v.string().optional(),
});
const jsonSchema = userSchema.toJsonSchema();
// {
//   type: 'object',
//   properties: {
//     name: { type: 'string' },
//     age: { type: 'number' },
//     email: { oneOf: [{ type: 'string' }, { type: 'null' }] }
//   },
//   required: ['name', 'age']
// }

Supported Targets

The .toJsonSchema() method accepts an optional target parameter to specify the JSON Schema version:

Draft 2020-12 (Default)

The latest JSON Schema specification. This is the default when no target is specified:

const schema = v.string().toJsonSchema(); // Uses draft-2020-12
const schema = v.string().toJsonSchema('draft-2020-12'); // Explicit

Draft 07

Widely supported older version:

const schema = v.string().toJsonSchema('draft-07');

OpenAPI 3.0

For API documentation with OpenAPI/Swagger:

const schema = v.string().toJsonSchema('openapi-3.0');

Type Mappings

The following table shows how valrs types map to JSON Schema:

valrs TypeJSON Schema
v.string(){ type: 'string' }
v.number(){ type: 'number' }
v.boolean(){ type: 'boolean' }
v.int32(){ type: 'integer', minimum: -2147483648, maximum: 2147483647 }
v.int64(){ type: 'integer' }
v.uint32(){ type: 'integer', minimum: 0, maximum: 4294967295 }
v.uint64(){ type: 'integer', minimum: 0 }
v.float32(){ type: 'number' }
v.float64(){ type: 'number' }
v.literal(value){ const: value }
v.literal(null){ type: 'null' }
v.enum([...]){ type: 'string', enum: [...] }
v.array(T){ type: 'array', items: T }
v.object({...}){ type: 'object', properties: {...}, required: [...] }
v.record(T){ type: 'object', additionalProperties: T }
v.tuple([A, B]){ type: 'array', prefixItems: [A, B], minItems: 2, maxItems: 2 }
v.union([A, B]){ oneOf: [A, B] }
v.intersection(A, B){ allOf: [A, B] }
v.map(K, V)Array of [key, value] tuples
v.set(T){ type: 'array', items: T, uniqueItems: true }
T.optional(){ oneOf: [T, { type: 'null' }] }
T.nullable(){ oneOf: [T, { type: 'null' }] }

Object Schemas

Object schemas include property definitions and required fields:

const schema = v.object({
  name: v.string(),
  age: v.number(),
  active: v.boolean().optional(),
});
 
const jsonSchema = schema.toJsonSchema();
// {
//   type: 'object',
//   properties: {
//     name: { type: 'string' },
//     age: { type: 'number' },
//     active: { oneOf: [{ type: 'boolean' }, { type: 'null' }] }
//   },
//   required: ['name', 'age']
// }

Strict Mode

Object schemas with .strict() set additionalProperties: false:

const schema = v.object({
  name: v.string(),
}).strict();
 
const jsonSchema = schema.toJsonSchema();
// {
//   type: 'object',
//   properties: { name: { type: 'string' } },
//   required: ['name'],
//   additionalProperties: false
// }

Catchall Properties

Object schemas with .catchall() define additionalProperties:

const schema = v.object({
  name: v.string(),
}).catchall(v.number());
 
const jsonSchema = schema.toJsonSchema();
// {
//   type: 'object',
//   properties: { name: { type: 'string' } },
//   required: ['name'],
//   additionalProperties: { type: 'number' }
// }

Composite Types

Arrays

const schema = v.array(v.string()).toJsonSchema();
// { type: 'array', items: { type: 'string' } }

Tuples

const schema = v.tuple([v.string(), v.number()]).toJsonSchema();
// {
//   type: 'array',
//   prefixItems: [{ type: 'string' }, { type: 'number' }],
//   minItems: 2,
//   maxItems: 2
// }

Unions

const schema = v.union([v.string(), v.number()]).toJsonSchema();
// { oneOf: [{ type: 'string' }, { type: 'number' }] }

Discriminated Unions

const schema = v.discriminatedUnion('type', [
  v.object({ type: v.literal('a'), value: v.string() }),
  v.object({ type: v.literal('b'), count: v.number() }),
]).toJsonSchema();
// {
//   oneOf: [...],
//   discriminator: { propertyName: 'type' }
// }

Use Cases

OpenAPI Documentation

Generate schemas for your API documentation:

import { v } from 'valrs';
 
const UserSchema = v.object({
  id: v.int32(),
  name: v.string(),
  email: v.string(),
  createdAt: v.string(),
});
 
const openApiSpec = {
  openapi: '3.0.0',
  paths: {
    '/users': {
      post: {
        requestBody: {
          content: {
            'application/json': {
              schema: UserSchema.toJsonSchema('openapi-3.0'),
            },
          },
        },
      },
    },
  },
};

Form Validation Libraries

Some form libraries accept JSON Schema for validation:

import { v } from 'valrs';
 
const ContactSchema = v.object({
  name: v.string(),
  email: v.string(),
  message: v.string(),
});
 
// Use with react-jsonschema-form or similar
const formSchema = ContactSchema.toJsonSchema('draft-07');

Database Schema Generation

Generate schemas for database column definitions:

const columnSchemas = {
  name: v.string().toJsonSchema(),
  age: v.int32().toJsonSchema(),
  active: v.boolean().toJsonSchema(),
};

Advanced: Input vs Output Schemas

For schemas that transform values (where input and output types differ), you can access both input and output JSON Schemas through the Standard Schema interface:

import { v } from 'valrs';
 
// A schema that transforms string input to number output
const schema = v.string().transform((s) => parseInt(s, 10));
 
// Input schema - what your API accepts
const inputSchema = schema['~standard'].jsonSchema.input({ target: 'openapi-3.0' });
// { type: 'string' }
 
// Output schema - what the validator produces
const outputSchema = schema['~standard'].jsonSchema.output({ target: 'openapi-3.0' });
// {} (transforms don't have predictable output schemas)

Note: The .toJsonSchema() method returns the input schema. For output schemas, use the ['~standard'].jsonSchema.output() method directly.

Next Steps