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 |
Default Inertia shared-props types (User, AuthData, FlashMessages) are controlled by
InertiaTypeGenConfig under InertiaConfig.type_gen.
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.
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¶
Install and Configure - Vite integration overview
Operation Modes - Operation modes and aliases
Type Generation - Inertia-specific type generation