New to Claude Skills? Learn how to install them →

Aposthog on GitHub

Adopting Generated API Types

Free

Streamline your API migration to generated types and functions.

by posthog37.6k stars on posthog/posthog
1 views
Updated Aug 11, 2026
Get this skill

Free · Opens the source repo

What Adopting Generated API Types does

The Adopting Generated API Types skill facilitates the migration of frontend code from manual API calls and handwritten TypeScript interfaces to generated API functions and types. This skill is particularly useful for developers working with PostHog, where API client functions are generated from Django serializers through an OpenAPI pipeline. By automating the migration process, this skill helps ensure that frontend code aligns with the latest backend changes, reducing the likelihood of errors and improving maintainability.

When using this skill, developers will be guided through a systematic workflow that identifies manual API calls and their corresponding generated equivalents. The skill covers three common patterns: high-level object API calls, raw HTTP methods with manual URLs, and the ApiRequest builder for fluent URL construction. By replacing these manual patterns with generated functions, developers can enhance code clarity and consistency while leveraging the benefits of type safety provided by the generated TypeScript types.

The skill includes detailed instructions on identifying API calls, finding generated equivalents, checking type compatibility, and cleaning up unused types. Developers will also find references to migration patterns that illustrate how to transition from manual calls to generated functions with specific examples. This comprehensive approach ensures that users can confidently migrate their codebase while adhering to best practices in API integration.

Overall, this skill is designed for frontend developers who are updating their code to utilize PostHog's generated API functions and types. It streamlines the migration process, making it easier to maintain and improve code quality in line with backend changes.

When to use it

Use this skill when updating frontend files that interact with the PostHog API, especially if they contain manual API calls or handwritten TypeScript interfaces.

When not to use it

This skill is not suitable for projects that do not utilize PostHog's generated API functions or for those that do not require migration from manual API calls.

What you can build with it

Migrating API Calls

When updating a file that calls `api.surveys.get()`, this skill helps replace it with the generated function `surveysRetrieve()`.

Updating TypeScript Interfaces

If you have handwritten TypeScript interfaces for API responses, this skill guides you in replacing them with the generated equivalents.

Cleaning Up Legacy Code

Use this skill to identify and remove dead manual types and imports after migrating all usages to generated API types.

How to install Adopting Generated API Types

View source

1. Install with the skills CLI

npx skills add posthog/posthog/adopting-generated-api-types --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by posthog

Adopting generated API types

Overview

PostHog generates TypeScript API client functions and types from Django serializers via the OpenAPI pipeline:

Django serializer → drf-spectacular → OpenAPI JSON → Orval → TypeScript (api.ts + api.schemas.ts + api.zod.ts)

Generated files live in:

  • Core: frontend/src/generated/core/api.ts, api.schemas.ts, and api.zod.ts
  • Products: products/<product>/frontend/generated/api.ts, api.schemas.ts, and api.zod.ts

Generated types use the Api suffix (DashboardApi, SurveyApi). Handwritten types never do.

This skill guides replacing manual API calls and handwritten types with generated equivalents.

The three manual patterns to migrate

The legacy frontend/src/lib/api.ts (~6000 lines) has three layers, all migration targets:

1. High-level object API (most common)

Domain-specific convenience methods on the api object:

api.surveys.get(id)
api.surveys.create(data)
api.dashboards.list()
api.cohorts.update(id, data)
api.actions.create(data)

These are the most widely used pattern — every entity has its own namespace with CRUD plus custom methods (e.g., api.surveys.getResponsesCount(), api.dashboards.streamTiles()).

2. Raw HTTP methods with manual URLs

api.get<SomeType>(`api/projects/${id}/surveys/`)
api.create<SomeType>(`api/projects/${id}/surveys/`, data)
api.update<SomeType>(url, data)
api.put<SomeType>(url, data)
api.delete(url)

3. ApiRequest builder (fluent URL construction)

const url = new ApiRequest().surveys().assembleFullUrl()
const response = await api.get(url)

// or directly:
await new ApiRequest().survey(surveyId).withAction('summarize_responses').create({ data })

All three patterns should be replaced with generated functions where available.

When to use

  • Touching a file that calls api.<entity>.<method>() (e.g., api.surveys.get())
  • Touching a file that calls api.get<T>(...), api.create<T>(...), etc.
  • Touching a file that uses new ApiRequest() to build URLs
  • Touching a file that imports handwritten interfaces from ~/types for API response shapes
  • Cleaning up frontend code after backend serializer improvements

Step-by-step workflow

1. Identify what the manual call does

Look at the existing call and extract:

  • HTTP method — GET, POST, PUT, PATCH, DELETE
  • Entity and action — what resource, what operation
  • Type parameter — the handwritten type used for the response

2. Find the generated equivalent

Generated function names follow the {resource}{Action} convention:

surveysList          — GET    /api/projects/{id}/surveys/
surveysCreate        — POST   /api/projects/{id}/surveys/
surveysRetrieve      — GET    /api/projects/{id}/surveys/{id}/
surveysPartialUpdate — PATCH  /api/projects/{id}/surveys/{id}/
surveysDestroy       — DELETE /api/projects/{id}/surveys/{id}/

Where to search:

  • Core endpoints: frontend/src/generated/core/api.ts
  • Product endpoints: products/<product>/frontend/generated/api.ts

Search strategies:

  1. Grep for the entity name in the generated api.ts files
  2. Search by the get*Url helper functions — every generated function has a URL builder above it
  3. Search api.schemas.ts for the type name with Api suffix

If no generated function exists, the backend endpoint may lack @extend_schema or @validated_request. Fix the backend first using the improving-drf-endpoints skill, then run hogli build:openapi.

Custom actions (like api.surveys.summarize_responses()) may not have generated equivalents if the backend @action lacks @extend_schema. Check generated files first; if missing, fix the backend.

3. Check type compatibility

Compare the handwritten type with the generated Api type. Key differences:

  • readonly modifiers — generated types mark read-only fields
  • Optional vs required — generated types reflect required= precisely
  • Nullabilitynull types are explicit
  • Extra fields — generated types may include fields the handwritten type omits

See type-compatibility.md for details.

4. Replace the call

See migration-patterns.md for detailed before/after examples covering:

  • High-level object API (api.surveys.get()surveysRetrieve())
  • Raw HTTP methods (api.get<T>(url) → generated function)
  • ApiRequest builder → generated function
  • Paginated list calls
  • Create/update with request bodies
  • Delete calls
  • Kea logic loaders and listeners
  • Calls with abort signals

5. Replace the type at usage sites

Update downstream references from the handwritten type to the generated one:

// Before
function renderSurvey(survey: Survey): JSX.Element { ... }

// After
function renderSurvey(survey: SurveyApi): JSX.Element { ... }

6. Clean up dead types

After migrating all usages of a handwritten type:

  1. Remove the type definition from ~/types or the local file
  2. Remove unused imports
  3. Run pnpm --filter=@posthog/frontend typescript:check to verify no breakage

Decision guide

ScenarioAction
Generated function existsReplace manual call with generated function
Generated type exists but function doesn'tUse the generated type as the generic parameter on the manual call, file a follow-up to add @extend_schema
Neither existsKeep the manual pattern, fix the backend serializer/viewset first
Custom action without generated equivalentKeep the api.<entity>.<method>() call, fix the backend @action annotation first
Generated type has different shape than handwrittenAdapt call sites to the generated shape — the serializer is the source of truth
Code mutates the response objectUse a local mutable copy: const mutable = { ...response } and mutate that
Need both read and write typesUse FooApi for reads, derive write types via Parameters<typeof fooCreate>[1] or use PatchedFooApi

Import conventions

// Core generated functions — import from api.ts
import { domainsList, domainsCreate, domainsRetrieve } from '~/generated/core/api'

// Core generated types — import type from api.schemas.ts
import type { OrganizationDomainApi } from '~/generated/core/api.schemas'

// Core generated Zod schemas — import from api.zod.ts
import { DomainsCreateBody } from '~/generated/core/api.zod'

// Product generated functions — NO tilde prefix, use 'products/' path
import { surveysList, surveysRetrieve } from 'products/surveys/frontend/generated/api'
import type { SurveyApi } from 'products/surveys/frontend/generated/api.schemas'
import { SurveysCreateBody } from 'products/surveys/frontend/generated/api.zod'

// Within a product, relative imports also work
import { logsAlertsCreate } from '../generated/api'
import type { LogsAlertConfigurationApi } from '../generated/api.schemas'
import { LogsAlertsCreateBody } from '../generated/api.zod'

Path rules:

  • Core: ~/generated/core/... (tilde prefix)
  • Products from outside: products/<product>/frontend/generated/... (no tilde)
  • Products from inside: relative ../generated/... or ./generated/...

Use import type for types to enable proper tree-shaking.

How generated functions work under the hood

Generated functions wrap the same api module via api-orval-mutator.ts:

surveysList(projectId, params)
  → apiMutator(url, { method: 'GET' })
    → api.get(url)

Switching to generated functions does not change HTTP behavior — same cookies, same CSRF, same error handling. The only difference is type safety and URL construction.

Verifying the migration

  1. TypeScript check: pnpm --filter=@posthog/frontend typescript:check
  2. Grep for leftover manual types: search for the old type name across the codebase
  3. Run relevant tests: hogli test <test_file>

Related

  • Backend side: use improving-drf-endpoints to fix serializers that produce poor types
  • Type system docs: docs/published/handbook/engineering/type-system.md
  • API mutator: frontend/src/lib/api-orval-mutator.ts
  • Regenerate types: hogli build:openapi

Frequently asked questions about Adopting Generated API Types

Similar skills