Telaio
CLI

Generate API Client

Generate a fully typed TypeScript API client from your OpenAPI spec using telaio gen-client.

Generate API Client

pnpx telaio gen-client

telaio gen-client generates a TypeScript API client from your app's OpenAPI spec. It spins up the app in ephemeral mode (no lifecycle hooks, no open ports), extracts the OpenAPI JSON, and passes it to @hey-api/openapi-ts for code generation.

Peer dependency

pnpm add -D @hey-api/openapi-ts

Configuration

Configure the client in telaio.config.ts:

import { defineConfig } from 'telaio';

export default defineConfig({
  app: 'src/app.ts',  // required: path to your app builder module

  client: {
    output: 'client',   // output directory, relative to project root
    enabled: true,      // set to false to disable generation
    plugins: [
      '@hey-api/sdk',
      '@hey-api/typescript',
      { name: '@hey-api/client-fetch', throwOnError: true },
    ],
  },
});

ClientConfig options

OptionTypeDefaultDescription
outputstring'client'Output directory for generated files
enabledbooleantrueSet to false to skip generation (useful in CI)
pluginsarrayDefault set@hey-api/openapi-ts plugin list

Default plugins

When plugins is not specified, the following are used:

  • @hey-api/typescript -- TypeScript type definitions
  • @hey-api/sdk -- typed SDK functions
  • @tanstack/react-query -- React Query integration (if installed)

Ephemeral mode

gen-client imports your app module and calls .asEphemeral() before .buildApi(). This skips all onStart and onStop lifecycle hooks, meaning the server never binds to a port and background processes never start. The app is assembled purely to extract the OpenAPI spec.

Your app module should export the builder result, not a started server:

// src/app.ts — export the built app, not app.start()
import { loadConfig, createApp } from 'telaio';

const config = loadConfig({ modules: { server: true, database: true } });

export const app = await createApp({ config })
  .withDatabase()
  .withSwagger({ info: { title: 'My API', version: '1.0.0' } })
  .withApiDocs()
  .withPlugins({ autoload: { dir: 'src/routes' } })
  .buildApi();

Output

The generated client is written to the configured output directory. After generation, gen-client runs biome lint --write followed by biome format --write over the output directory so the client matches your project's formatting and lint rules.

client/
  index.ts
  types.gen.ts
  sdk.gen.ts
  @tanstack/
    react-query.gen.ts

The post-processors pass --vcs-use-ignore-file=false so the output directory is processed even when it is listed in .gitignore (typical for generated code) and your biome.json has vcs.useIgnoreFile: true.

Using the generated client

import { client, getUsers, getUserById } from '../client/index.js';

// Configure the base URL once
client.setConfig({ baseUrl: 'https://api.example.com' });

// Use typed functions
const users = await getUsers({ query: { limit: 20, skip: 0 } });
const user = await getUserById({ path: { id: '123' } });

Watch mode

pnpx telaio gen-client --watch

When --watch is passed, gen-client watches src/ for file changes and regenerates the client automatically. This is useful during development to keep the client in sync as you add or modify routes.

Run telaio gen-client as part of your CI pipeline to keep the client in sync with the API spec. Commit the generated output so consumers of the client do not need to run generation themselves.

On this page