How the Typed API Client Library Is Generated for Kaneo: OpenAPI to TypeScript Pipeline
Kaneo generates its typed API client library automatically from an OpenAPI specification using openapi-typescript, then wraps the generated types in a lightweight Hono client that provides compile-time type safety for all API operations.
The Kaneo project (usekaneo/kaneo) maintains a type-safe API client that React applications and external consumers import as @kaneo/libs. Rather than maintaining handwritten type definitions that drift out of sync with the backend, the typed API client library is generated directly from the OpenAPI specification, ensuring zero-runtime-overhead type safety across the entire stack.
The OpenAPI-to-TypeScript Generation Pipeline
OpenAPI Specification Source
The single source of truth for the API surface resides in apps/docs/openapi.json. This file is produced from Hono route definitions in apps/api/src/openapi.ts that use the @hono/zod-openapi library to attach Zod schemas to each route. Keeping the specification in a JSON file allows the codebase to treat it as a stable contract between the backend implementation and the client types.
TypeScript Declaration Generation
The packages/libs workspace automates type generation through a dedicated npm script. In packages/libs/package.json, the gen:libs script executes:
"scripts": {
"gen:libs": "openapi-typescript apps/docs/openapi.json --output packages/libs/src/openapi.d.ts"
}
This command processes the canonical spec and outputs packages/libs/src/openapi.d.ts, a declaration file containing fully typed representations of every request payload, query parameter, and response shape. Because this file contains only type declarations, it adds zero bytes to the JavaScript bundle while providing full IntelliSense support in IDEs.
Hono Client Wrapper
The generated types are consumed by a minimal wrapper located in packages/libs/src/hono.ts. This module imports the paths type from the generated declarations and creates a client instance typed against the OpenAPI specification:
import { Hono } from "hono";
import type { paths } from "./openapi";
export const client = new Hono<paths>();
export const windowId = "kaneo-client";
The wrapper integrates with resolveApiBaseUrl from packages/libs/src/api-url.ts to handle runtime URL resolution while maintaining strict compile-time constraints on all request paths and payloads.
Key Files in the Generation Architecture
apps/docs/openapi.json– The canonical OpenAPI specification generated from Hono route definitions using@hono/zod-openapi.apps/api/src/openapi.ts– Source file containing the Zod-annotated Hono routes that produce the OpenAPI output.packages/libs/src/openapi.d.ts– Auto-generated TypeScript declarations produced by theopenapi-typescripttool.packages/libs/src/hono.ts– Hono client factory that imports the generatedpathstype and exports the typedclientinstance.packages/libs/src/api-url.ts– Utility functionresolveApiBaseUrlfor determining API endpoints across different environments.packages/libs/src/index.ts– Public API surface that re-exportsclient,resolveApiBaseUrl, andwindowIdfor consumers.
Using the Generated Typed Client
Consumers import the fully typed client from @kaneo/libs. The TypeScript compiler validates request parameters and response types against the OpenAPI specification at build time, catching mismatches before runtime.
Fetching Workspace Data
import { client } from "@kaneo/libs";
async function listWorkspaces() {
const resp = await client.get("/workspace");
const workspaces = await resp.json(); // Typed as Workspace[]
return workspaces;
}
Creating Resources with Typed Payloads
import { client } from "@kaneo/libs";
async function createTask(projectId: string, title: string) {
const resp = await client.post("/task/{projectId}", {
json: { title },
params: { projectId },
});
const task = await resp.json(); // Type: Task (from generated OpenAPI types)
return task;
}
Configuring Custom API Endpoints
import { resolveApiBaseUrl, client } from "@kaneo/libs";
const apiUrl = resolveApiBaseUrl({ apiUrl: "https://api.my-kaneo.com" });
client.baseURL = apiUrl;
Build Automation and Type Synchronization
The generation process integrates with the workspace build system to prevent type drift. Running pnpm build in the libs workspace triggers npm run gen:libs first, guaranteeing that packages/libs/src/openapi.d.ts always reflects the current API surface defined in apps/docs/openapi.json.
This automation ensures that any modification to the Hono routes in apps/api/src/openapi.ts cascades through to the TypeScript declarations before the library compiles. Developers never need to manually update client types when adding new endpoints or changing request schemas.
Summary
- Single Source of Truth: The OpenAPI specification in
apps/docs/openapi.jsondrives all client types. - Automatic Generation: The
openapi-typescripttool producespackages/libs/src/openapi.d.tsvia thegen:libsnpm script. - Type-Safe Wrapper:
packages/libs/src/hono.tswraps generated types in a Hono client instance with runtime URL resolution viaresolveApiBaseUrl. - Zero Runtime Overhead: Generated files contain only TypeScript declarations; network logic remains in the lightweight Hono wrapper.
- Build Integration: The
pnpm buildcommand regenerates types automatically, ensuring synchronization between API implementation and client library.
Frequently Asked Questions
How does Kaneo keep the API client types synchronized with the backend?
Kaneo uses a build-time generation pipeline. The gen:libs script in packages/libs/package.json runs openapi-typescript against apps/docs/openapi.json before compilation. This ensures the TypeScript declarations in packages/libs/src/openapi.d.ts always match the current Hono route definitions in apps/api/src/openapi.ts, eliminating manual type maintenance.
Can I use the Kaneo typed client in a non-React project?
Yes. The @kaneo/libs package exports framework-agnostic utilities including the typed client instance, resolveApiBaseUrl for URL configuration, and windowId constants. Any TypeScript project can import these to make type-safe API calls using the Hono client pattern without React dependencies.
What tool generates the TypeScript definitions from OpenAPI?
The project uses openapi-typescript, an open-source CLI tool that converts OpenAPI 3.x specifications into TypeScript declaration files. Kaneo invokes this tool via the gen:libs script, outputting to packages/libs/src/openapi.d.ts to provide compile-time type checking for all API operations defined in the spec.
Where is the API base URL configured in the typed client?
URL resolution logic lives in packages/libs/src/api-url.ts. The resolveApiBaseUrl function determines the appropriate API endpoint from environment variables or explicit configuration objects. The Hono client in packages/libs/src/hono.ts consumes this resolver to set client.baseURL while maintaining full type safety for all routes defined in the generated paths interface.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →