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-typesto export OpenAPI + routes and generate TS types.Generated artifacts live under
src/generatedby default..litestar.jsonis refreshed during generation so the Vite plugin stays in sync.
Overview¶
The type generation pipeline:
Exports your Litestar OpenAPI schema to JSON
Generates TypeScript interfaces using
@hey-api/openapi-tsExtracts route metadata from your application
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 directory for all generated files |
|
|
Path for exported OpenAPI schema |
|
|
Path for routes metadata JSON |
|
|
Path for typed route helper (generated by Litestar type export) |
|
|
Path for |
|
|
Generate Zod schemas for runtime validation |
|
|
Generate API client SDK via hey-api (fetch-based) |
|
|
Generate typed routes.ts file |
|
|
Generate Inertia page props types (effective only when Inertia is enabled) |
|
|
Generate schemas.ts with ergonomic form/response type helpers |
|
|
Path for page props metadata |
|
|
Fallback value type for untyped dict/list in page props ( |
|
|
Map schema/type names to TypeScript import paths for props types excluded from OpenAPI |
|
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:
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:
Exports OpenAPI schema to
src/generated/openapi.jsonExports route metadata to
src/generated/routes.jsonandroutes.ts(if enabled)Runs
litestar-vite-typegen(invokes@hey-api/openapi-tsand generatesschemas.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 |
|---|---|
|
Request body type for operation T (e.g., form fields) |
|
Response type for operation T with status code S |
|
Response type for 200/201/204 status |
|
Error response type for operation T |
|
Path parameter type for operation T |
|
Query parameter type for operation 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¶
Vite Integration - Vite integration overview
Operation Modes - Operation modes and aliases
Type Generation - Inertia-specific type generation