Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hey-api/openapi-ts/llms.txt

Use this file to discover all available pages before exploring further.

The Valibot plugin generates Valibot schemas from your OpenAPI specification. Valibot is a modular and lightweight schema library with excellent bundle size, making it ideal for client-side applications.

Installation

Install the required dependencies:
npm install valibot @hey-api/openapi-ts

Configuration

Add the Valibot plugin to your openapi-ts.config.ts:
openapi-ts.config.ts
import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
  input: 'path/to/openapi.json',
  output: {
    path: './src/client',
  },
  plugins: [
    '@hey-api/typescript',
    {
      name: '@hey-api/valibot',
      // Configuration options
    },
  ],
});

Schema Generation

Definitions

Generate schemas for reusable component definitions:
{
  name: '@hey-api/valibot',
  definitions: true,  // Enable definitions (default)
}
Customize definition schema names and casing:
{
  name: '@hey-api/valibot',
  definitions: {
    enabled: true,
    name: 'v{{name}}',  // Naming pattern (default)
    case: 'camelCase',  // Identifier casing
  },
}
Generated output:
valibot.gen.ts
import * as v from 'valibot';

/**
 * A pet in the pet store
 */
export const vPet = v.object({
  id: v.pipe(v.number(), v.integer()),
  name: v.string(),
  status: v.optional(v.picklist(['available', 'pending', 'sold'])),
});

Requests

Generate validators for API request data (body, query, path, headers):
{
  name: '@hey-api/valibot',
  requests: {
    enabled: true,
    name: 'v{{name}}Data',  // Naming pattern
    case: 'camelCase',
  },
}
Generated output:
valibot.gen.ts
export const vGetPetByIdData = v.object({
  path: v.object({
    petId: v.pipe(v.number(), v.integer()),
  }),
  query: v.optional(v.object({
    includeDetails: v.optional(v.boolean()),
  })),
});

Responses

Generate validators for API responses:
{
  name: '@hey-api/valibot',
  responses: {
    enabled: true,
    name: 'v{{name}}Response',  // Naming pattern
    case: 'camelCase',
  },
}
Generated output:
valibot.gen.ts
export const vGetPetByIdResponse = v.object({
  id: v.pipe(v.number(), v.integer()),
  name: v.string(),
  status: v.optional(v.picklist(['available', 'pending', 'sold'])),
});

Webhooks

Generate validators for webhook payloads:
{
  name: '@hey-api/valibot',
  webhooks: {
    enabled: true,
    name: 'v{{name}}WebhookRequest',
    case: 'camelCase',
  },
}

Valibot Pipes

Valibot uses a pipe-based architecture for transformations and validations. The plugin generates schemas using Valibot’s pipe syntax:
// String with length constraints
v.pipe(
  v.string(),
  v.minLength(3),
  v.maxLength(50)
)

// Number with range constraints
v.pipe(
  v.number(),
  v.integer(),
  v.minValue(1),
  v.maxValue(100)
)

// String with pattern validation
v.pipe(
  v.string(),
  v.regex(/^[a-zA-Z0-9_]*$/)
)

Metadata Support

Enable Valibot metadata for additional schema information:
{
  name: '@hey-api/valibot',
  metadata: true,  // Enable metadata (default: false)
}
Metadata is useful for documentation, code generation, AI structured outputs, and form validation.

Usage Examples

Validating API Requests

import * as v from 'valibot';
import { vCreatePetData } from './client/valibot.gen';

// Validate request data
const result = v.safeParse(vCreatePetData, {
  body: {
    name: 'Fluffy',
    status: 'available',
  },
});

if (result.success) {
  // Data is valid and typed
  await fetch('/api/pets', {
    method: 'POST',
    body: JSON.stringify(result.output.body),
  });
} else {
  // Handle validation errors
  console.error(result.issues);
}

Validating API Responses

import * as v from 'valibot';
import { vGetPetByIdResponse } from './client/valibot.gen';

const response = await fetch('/api/pets/123');
const data = await response.json();

// Validate and parse response
const pet = v.parse(vGetPetByIdResponse, data);
// pet is now fully typed

Async Validation

import * as v from 'valibot';
import { vCreatePetData } from './client/valibot.gen';

// Use parseAsync for async validation
const data = await v.parseAsync(vCreatePetData, requestData);

Type Inference

import * as v from 'valibot';
import { vPet } from './client/valibot.gen';

// Infer TypeScript type from Valibot schema
type Pet = v.InferOutput<typeof vPet>;

const pet: Pet = {
  id: 1,
  name: 'Fluffy',
};

Form Validation with React

import * as v from 'valibot';
import { useState } from 'react';
import { vCreatePetData } from './client/valibot.gen';

// Extract the body schema from the request data schema
const CreatePetSchema = vCreatePetData.entries.body;
type CreatePetForm = v.InferOutput<typeof CreatePetSchema>;

function CreatePetForm() {
  const [formData, setFormData] = useState<Partial<CreatePetForm>>({});
  const [errors, setErrors] = useState<string[]>([]);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    // Validate form data
    const result = v.safeParse(CreatePetSchema, formData);

    if (result.success) {
      await fetch('/api/pets', {
        method: 'POST',
        body: JSON.stringify(result.output),
      });
      setErrors([]);
    } else {
      setErrors(result.issues.map(issue => issue.message));
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={formData.name || ''}
        onChange={(e) => setFormData({ ...formData, name: e.target.value })}
      />

      <select
        value={formData.status || ''}
        onChange={(e) => setFormData({ ...formData, status: e.target.value })}
      >
        <option value="available">Available</option>
        <option value="pending">Pending</option>
        <option value="sold">Sold</option>
      </select>

      {errors.length > 0 && (
        <ul>
          {errors.map((error, i) => <li key={i}>{error}</li>)}
        </ul>
      )}

      <button type="submit">Create Pet</button>
    </form>
  );
}

String Formats

Valibot automatically generates appropriate validators for OpenAPI string formats:
OpenAPI FormatValibot Validator
datev.isoDate()
date-timev.isoTimestamp()
emailv.email()
ipv4, ipv6v.ip()
timev.isoTimeSecond()
uriv.url()
uuidv.uuid()
Example:
# OpenAPI specification
User:
  type: object
  properties:
    email:
      type: string
      format: email
    website:
      type: string
      format: uri
    createdAt:
      type: string
      format: date-time
Generated schema:
export const vUser = v.object({
  email: v.pipe(v.string(), v.email()),
  website: v.pipe(v.string(), v.url()),
  createdAt: v.pipe(v.string(), v.isoTimestamp()),
});

Advanced Configuration

Custom Naming Patterns

Use functions for dynamic naming:
{
  name: '@hey-api/valibot',
  definitions: {
    name: (name: string) => `valibotSchema${name}`,
  },
  requests: {
    name: (name: string) => `${name}RequestSchema`,
  },
}

Selective Schema Generation

Disable specific schema categories:
{
  name: '@hey-api/valibot',
  definitions: true,
  requests: false,    // Don't generate request schemas
  responses: true,
  webhooks: false,    // Don't generate webhook schemas
}

Global Configuration

import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
  input: 'path/to/openapi.json',
  output: {
    path: './src/client',
  },
  plugins: [
    '@hey-api/typescript',
    {
      name: '@hey-api/valibot',
      case: 'camelCase',
      comments: true,
      metadata: false,
      definitions: {
        name: 'v{{name}}',
      },
      requests: {
        name: 'v{{name}}Data',
      },
      responses: {
        name: 'v{{name}}Response',
      },
    },
  ],
});

Bundle Size Benefits

Valibot is designed to be tree-shakable and has a smaller bundle size compared to other validation libraries:
  • Modular architecture: Only import what you need
  • Tree-shakable: Unused validators are removed during bundling
  • Minimal runtime: Core functionality is lightweight
  • Pipe-based composition: Efficient schema composition
Example bundle comparison:
// Only the validators you use are included in your bundle
import * as v from 'valibot';

// This only bundles: object, string, number, integer, pipe
const schema = v.object({
  name: v.string(),
  age: v.pipe(v.number(), v.integer()),
});

Type Reference

The Valibot plugin exports these configuration types:
import type { ValibotPlugin } from '@hey-api/valibot';
import { defineConfig } from '@hey-api/valibot';

// Use the type helper for better type safety
const valibotConfig = defineConfig({
  name: '@hey-api/valibot',
  // ... your configuration
});

Migration from Zod

If you’re migrating from Zod to Valibot:
import { z } from 'zod';

const schema = z.object({
  name: z.string().min(3).max(50),
  age: z.number().int().min(0),
});

type User = z.infer<typeof schema>;

const result = schema.safeParse(data);
if (result.success) {
  console.log(result.data);
}
Key differences:
  • Valibot uses v.pipe() for chaining validators instead of method chaining
  • Valibot uses InferOutput instead of infer
  • Valibot’s parse result uses output instead of data
  • Valibot’s validation issues use a different structure

Validators Overview

Learn about validator plugins and their benefits

Zod Plugin

Alternative validator with method chaining syntax

Valibot Documentation

Official Valibot library documentation