Type Generation

Litestar Vite includes a type generation system that keeps your frontend in sync with your Python backend.

At a Glance

  • Run litestar assets generate-types to export OpenAPI + routes and generate TS types.

  • Generated artifacts live under src/generated by default.

  • .litestar.json is refreshed during generation so the Vite plugin stays in sync.

Overview

The type generation pipeline:

  1. Exports your Litestar OpenAPI schema to JSON

  2. Generates TypeScript interfaces using @hey-api/openapi-ts

  3. Extracts route metadata from your application

  4. Generates a typed route() helper for type-safe URL generation

Configuration

Enable type generation in your ViteConfig:

from litestar_vite.config import TypeGenConfig

VitePlugin(
    config=ViteConfig(
        types=TypeGenConfig(
            output=Path("src/generated"),  # default
            generate_sdk=True,             # generate API client
            generate_routes=True,          # generate routes.ts
        )
    )
)

Or use the shortcut for defaults:

VitePlugin(config=ViteConfig(types=True))

TypeGenConfig Reference

Parameter

Default

Description

output

src/generated

Output directory for all generated files

openapi_path

{output}/openapi.json

Path for exported OpenAPI schema

routes_path

{output}/routes.json

Path for routes metadata JSON

routes_ts_path

{output}/routes.ts

Path for typed route helper (generated by Litestar type export)

schemas_ts_path

{output}/schemas.ts

Path for schemas.ts helper types (generated by litestar-vite typegen)

generate_zod

False

Generate Zod schemas for runtime validation

generate_sdk

True

Generate API client SDK via hey-api (fetch-based)

generate_routes

True

Generate typed routes.ts file

generate_page_props

True

Generate Inertia page props types (effective only when Inertia is enabled)

generate_schemas

True

Generate schemas.ts with ergonomic form/response type helpers

page_props_path

{output}/inertia-pages.json

Path for page props metadata

fallback_type

unknown

Fallback value type for untyped dict/list in page props (unknown or any)

type_import_paths

{}

Map schema/type names to TypeScript import paths for props types excluded from OpenAPI

watch_patterns

See below

File patterns to watch for regeneration

Default Inertia shared-props types (User, AuthData, FlashMessages) are controlled by InertiaTypeGenConfig under InertiaConfig.type_gen.

Default watch patterns:

watch_patterns=["**/routes.py", "**/handlers.py", "**/controllers/**/*.py"]

hey-api Configuration

Litestar Vite uses hey-api/openapi-ts to generate TypeScript types from your OpenAPI schema. Create an openapi-ts.config.ts in your project root:

openapi-ts.config.ts
import { defineConfig } from "@hey-api/openapi-ts"

export default defineConfig({
  input: "./src/generated/openapi.json",
  output: "./src/generated/api",
  plugins: ["@hey-api/typescript", "@hey-api/schemas", "@hey-api/sdk", "@hey-api/client-axios"],
})

Available hey-api plugins:

  • @hey-api/typescript - Core TypeScript types (always included)

  • @hey-api/schemas - JSON Schema exports

  • @hey-api/sdk - Type-safe API client

  • @hey-api/client-axios - Axios-based HTTP client

  • @hey-api/client-fetch - Fetch-based HTTP client

  • @hey-api/zod - Zod runtime validators

Generating Types

Generate all types with a single command:

litestar assets generate-types

This runs the full pipeline:

  1. Exports OpenAPI schema to src/generated/openapi.json

  2. Exports route metadata to src/generated/routes.json and routes.ts (if enabled)

  3. Runs litestar-vite-typegen (invokes @hey-api/openapi-ts and generates schemas.ts / page props types)

The command also writes/updates .litestar.json and reports whether it was updated or unchanged, matching the output style for other generated files.

You can also export routes separately:

litestar assets export-routes

Generated Files

After running generate-types, your output directory contains:

src/generated/
├── openapi.json       # OpenAPI schema from Litestar
├── routes.json        # Route metadata
├── routes.ts          # Typed route() helper
├── schemas.ts         # Form/response type helpers (if generate_schemas=True)
└── api/               # hey-api output (if generate_sdk=True)
    ├── index.ts       # Barrel export
    ├── types.gen.ts   # TypeScript interfaces
    ├── schemas.gen.ts # JSON schemas (if @hey-api/schemas)
    └── sdk.gen.ts     # API client (if @hey-api/sdk)

Using Generated Types

Import types and the route helper in your frontend:

import { route } from './generated/routes';
import type { Book, Summary } from './generated/api';

// Type-safe URL generation
const bookUrl = route('api:books.detail', { book_id: 123 });
// => "/api/books/123"

// Type-safe API calls
const response = await fetch(route('api:summary'));
const summary: Summary = await response.json();

Using the Generated SDK

If generate_sdk=True, hey-api generates a fully typed API client:

import { client, getApiSummary, getApiBooks } from './generated/api';

// Configure base URL (optional - defaults to same origin)
client.setConfig({ baseUrl: '/api' });

// Type-safe API calls with full IntelliSense
const { data: summary } = await getApiSummary();
const { data: books } = await getApiBooks();

// Parameters are typed
const { data: book } = await getApiBooksBookId({ path: { book_id: 1 } });

Typed Routes

The generated routes.ts provides Ziggy-style type-safe routing:

// If OpenAPI schemas include a `format`, `routes.ts` also emits semantic aliases
// and uses them in route parameter types (aliases are plain primitives, no runtime parsing).
export type UUID = string;
export type DateTime = string;

// Generated types
export type RouteName = "home" | "api:summary" | "api:books" | "api:books.detail";

export interface RoutePathParams {
  "api:books.detail": { book_id: number };
  "home": Record<string, never>;
  // ... other routes
}

export interface RouteQueryParams {
  "api:books.detail": Record<string, never>;
  "home": Record<string, never>;
  // ... other routes
}

type EmptyParams = Record<string, never>
type MergeParams<A, B> =
  A extends EmptyParams ? (B extends EmptyParams ? EmptyParams : B) : B extends EmptyParams ? A : A & B

export type RouteParams<T extends RouteName> =
  MergeParams<RoutePathParams[T], RouteQueryParams[T]>;

// Type-safe route function (params required only when needed)
export function route<T extends RoutesWithoutRequiredParams>(name: T): string;
export function route<T extends RoutesWithoutRequiredParams>(name: T, params?: RouteParams<T>): string;
export function route<T extends RoutesWithRequiredParams>(name: T, params: RouteParams<T>): string;

// Usage
route("home");                                  // "/"
route("api:books.detail", { book_id: 123 });   // "/api/books/123"
route("api:books.detail");                     // TypeScript Error!

Note

For URL generation, route params never require null values. Optionality is represented by optional query parameters (?:) and omission, matching how route() serializes values.

Auto-Regeneration

During development, types are regenerated when watched files change. Configure patterns in TypeGenConfig:

TypeGenConfig(
    watch_patterns=[
        "**/routes.py",
        "**/handlers.py",
        "**/controllers/**/*.py",
        "**/models/**/*.py",  # Add custom patterns
    ],
)

Form and Schema Types

The generated schemas.ts provides ergonomic type helpers for working with request bodies and response types. These helpers use route names (like routes.ts) for a clean, consistent developer experience.

Generated Types:

// schemas.ts provides:
import type {
    OperationName,    // Union of all API operation names
    FormInput,        // Extract request body type
    FormResponse,     // Extract response type by status code
    SuccessResponse,  // Extract 200/201 response
    ErrorResponse,    // Extract error response type
    PathParams,       // Extract path parameters
    QueryParams,      // Extract query parameters
} from './generated/schemas'

Usage with Inertia.js Forms (React):

import { useForm } from '@inertiajs/react'
import type { FormInput, SuccessResponse } from '@/generated/schemas'
import { route } from '@/generated/routes'

function LoginForm() {
    // Type-safe form data using route names
    const form = useForm<FormInput<'api:login'>>({
        username: '',
        password: '',
    })

    const submit = () => {
        form.post(route('api:login'))
    }

    return (
        <form onSubmit={(e) => { e.preventDefault(); submit() }}>
            <input
                value={form.data.username}
                onChange={e => form.setData('username', e.target.value)}
            />
            {/* TypeScript knows 'username' is a valid field */}
        </form>
    )
}

Usage with REST APIs:

import type { FormInput, SuccessResponse, FormResponse } from '@/generated/schemas'
import { route } from '@/generated/routes'

async function login(credentials: FormInput<'api:login'>) {
    const response = await fetch(route('api:login'), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(credentials),
    })

    if (response.ok) {
        const data: SuccessResponse<'api:login'> = await response.json()
        return data.access_token  // TypeScript knows this exists
    } else {
        const error: FormResponse<'api:login', 400> = await response.json()
        throw new Error(error.detail)  // TypeScript knows this exists
    }
}

Available Helper Types:

Type

Description

FormInput<T>

Request body type for operation T (e.g., form fields)

FormResponse<T, S>

Response type for operation T with status code S

SuccessResponse<T>

Response type for 200/201/204 status

ErrorResponse<T>

Error response type for operation T

PathParams<T>

Path parameter type for operation T

QueryParams<T>

Query parameter type for operation T

PartialFormInput<T>

All form fields optional (for initialization)

Configuration:

Schema type generation is enabled by default. Disable with:

TypeGenConfig(generate_schemas=False)

Inertia Page Props

When using Inertia.js, enable page props generation:

VitePlugin(
    config=ViteConfig(
        types=TypeGenConfig(generate_page_props=True),
        inertia=True,
    )
)

This generates inertia-pages.json which the Vite plugin uses to create page-props.ts with typed props for each page component.

See Type Generation for Inertia-specific details.

See Also