Fetch
Create Freestyle Fetch

Generated Code

Understanding the structure and usage of generated API clients.

Overview

The OpenAPI Generator creates three TypeScript files that work together to provide a complete, type-safe API client.

File Structure

A. models.ts

Contains all Zod schemas derived from your OpenAPI components:

  • Schemas - Converted from components.schemas definitions
  • Type exports - TypeScript types inferred from Zod schemas
  • Validation - Runtime validation using Zod

Example generated model:

import { z } from 'zod'

export const User = z.object({
  id: z.string(),
  username: z.string(),
  email: z.email(),
  createdAt: z.iso.datetime()
})

export type UserModel = z.infer<typeof User>

B. api.ts

Contains the hierarchical router mapping all API endpoints:

  • Base URL - Extracted from servers[0].url in the specification
  • Nested routes - Path segments become nested objects
  • HTTP methods - Each method is a pre-configured builder
  • Type inference - Full TypeScript support for all operations

Example generated API:

import { f } from '@freestylejs/fetch'
import { z } from 'zod'
import * as Model from './models'

export const api = f.router('https://api.example.com', {
  users: {
    $userId: {
      GET: f.builder()
        .def_json()
        .def_response(async ({ json }) => Model.User.parse(await json())),
      
      posts: {
        GET: f.builder()
          .def_json()
          .def_searchparams(z.object({
            page: z.number().optional(),
            limit: z.number().optional()
          }).parse)
          .def_response(async ({ json }) => z.array(Model.Post).parse(await json()))
      }
    }
  }
})

C. auth.ts

Contains type-safe authentication configuration and middleware factory:

  • AuthConfig - Interface matching your OpenAPI security schemes
  • Middleware Factory - Creates authentication middleware based on config
  • Supported Schemes - Bearer, Basic, API Key, OAuth2

Example generated auth:

import { f, type Middleware } from '@freestylejs/fetch'

export interface AuthConfig {
    /** Bearer authentication */
    bearerAuth?: {
        token: string | (() => Promise<string>)
    }
    /** API Key authentication */
    apiKey?: {
        value: string | (() => Promise<string>)
    }
}

export const createAuthMiddleware = (config: AuthConfig) => {
    const middlewares: Middleware[] = []
    // ... middleware implementation
    return middlewares
}

D. index.ts

Re-exports all generated code:

export * from './api'
export * from './models'
export * from './auth'

Usage Patterns

A. Client Initialization & Authentication

The generated code exports a createClient factory function that allows you to configure the base URL and authentication credentials.

import { createClient } from './api'

// Initialize with auth configuration
const client = createClient({
  baseUrl: 'https://api.example.com', // Optional override
  auth: {
    // Type-safe config based on your OpenAPI security schemes
    bearerAuth: {
      token: 'my-secret-token'
    },
    apiKey: {
      // Supports async functions for token rotation
      value: async () => await getApiKey()
    }
  }
})

// Use the authenticated client
const user = await client.users.me.GET()

B. Path Parameters

Dynamic path segments use the $paramName syntax:

// OpenAPI: /users/{userId}
// Generated: client.users.$userId

const user = await client.users.$userId.GET({
  params: { userId: '123' }
})

C. Query Parameters

Query parameters are validated with Zod schemas:

// OpenAPI: /users?page=1&limit=10
// Generated with searchparams

const users = await client.users.GET({
  query: {
    page: 1,
    limit: 10
  }
})

D. Request Bodies

Request bodies are validated against Zod schemas:

// OpenAPI: POST /users with body
// Generated with def_body

const newUser = await client.users.POST({
  body: {
    username: 'john',
    email: 'john@example.com'
  }
})

E. Response Validation

Responses are automatically validated:

// Response is validated with zod at runtime
const user = await client.users.$userId.GET({
  params: { userId: '123' }
})

// TypeScript knows the exact shape
user.username // string
user.email    // string

Type Inference

The generated client provides complete type inference:

A. Request Types

// TypeScript infers the shape of params, query, and body
client.users.$userId.GET({
  params: { userId: '123' },  // Type: { userId: string }
  query: { include: 'posts' } // Type: { include?: string }
})

B. Response Types

// Response type is inferred from the Zod schema
const user = await client.users.$userId.GET({
  params: { userId: '123' }
})

// user: UserModel

Schema Conversion

The generator converts OpenAPI types to Zod schemas:

OpenAPI TypeZod SchemaExample
stringz.string()"hello"
numberz.number()42
integerz.number().int()10
booleanz.boolean()true
arrayz.array(...)[1, 2, 3]
objectz.object({...}){ key: "value" }
string (format: date-time)z.iso.datetime()"2024-01-01T00:00:00Z"
string (format: email)z.email()"user@example.com"
string (format: uuid)z.uuid()"123e4567-e89b-12d3-..."
string (format: uri)z.url()"https://example.com"
string (enum)z.enum([...])"active"
allOfSchema1.and(Schema2)Intersection
oneOfz.union([...])Union
oneOf (discriminator)z.discriminatedUnion(...)Discriminated union

Naming Conventions

The generator converts OpenAPI names to PascalCase:

  • user-profileUserProfile
  • api_keyApiKey
  • userSettingsUserSettings

Special characters like $ are preserved:

  • $special$Special