Type Generation

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

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 Vite plugin)

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)

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. Runs npx openapi-ts to generate TypeScript types

  3. Generates routes.ts with typed route helper

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
└── 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
    ],
)

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