chowbea-axios

Introduction

CLI tool that generates type-safe Axios clients from OpenAPI specifications.

chowbea-axios generates fully-typed Axios clients from your OpenAPI specification. You get type safety, autocomplete, and Result-based error handling—without writing a single type definition.

The Problem

Working with REST APIs in TypeScript often means:

  • Manual type definitions that drift out of sync with your backend
  • No autocomplete for endpoints, parameters, or response shapes
  • Try/catch everywhere to handle errors, with inconsistent error shapes
  • Runtime surprises when the API changes and your types don't

You end up maintaining two sources of truth: your OpenAPI spec and your TypeScript types. They inevitably diverge.

The Solution

chowbea-axios reads your OpenAPI spec and generates everything you need:

  • Types extracted directly from your spec—always in sync
  • A typed client with autocomplete for every endpoint
  • Normalized errors with a consistent shape across all calls
  • Watch mode that regenerates when your spec changes

One command. Zero manual types. Your IDE knows your entire API.

How It Works

Understanding the internals helps you get the most out of chowbea-axios.

1. Configuration

Everything starts with api.config.toml, created when you run init. This file is the single source of truth for the CLI:

api_endpoint = "http://localhost:3000/docs/swagger/json"
poll_interval_ms = 10000

[output]
folder = "src/services/api"

[instance]
base_url_env = "VITE_API_URL"
timeout = 30000

The config tells chowbea-axios where to fetch your spec, where to output files, and how to configure the Axios instance.

2. Fetching and Caching

When you run fetch, the CLI:

  1. Reads api_endpoint from your config (or spec_file for local specs)
  2. Downloads the OpenAPI JSON and stores it in _internal/openapi.json
  3. Computes a hash of the spec and saves it to _internal/.api-cache.json

On subsequent runs, chowbea-axios compares the current spec hash against the cached one. If they match, generation is skipped entirely. This is why fetch is fast after the first run—no redundant work.

3. Type Generation

The generator parses your OpenAPI spec and extracts:

  • Paths — Each endpoint becomes a typed method (/users/{id}api.get("/users/{id}", { id }))
  • Schemas — Types from components.schemas become TypeScript interfaces in api.types.ts
  • Operations — Each operationId becomes a named method (getUserByIdapi.op.getUserById())
  • Parameters — Path, query, and body params are extracted and typed

The result is two generated files:

  • api.types.ts — All TypeScript types from your spec
  • api.operations.ts — Operation-based methods with full typing

The Flow

┌─────────────────────────────────────────────────────────────────────────┐
│                           chowbea-axios fetch                           │
└─────────────────────────────────────────────────────────────────────────┘


                    ┌───────────────────────────────┐
                    │    Read api.config.toml       │
                    │    (endpoint, output folder)  │
                    └───────────────────────────────┘


                    ┌───────────────────────────────┐
                    │    Fetch OpenAPI Spec         │
                    │    (remote URL or local file) │
                    └───────────────────────────────┘


                    ┌───────────────────────────────┐
                    │    Compute Spec Hash          │
                    └───────────────────────────────┘


                         ┌──────────────────┐
                         │  Hash Changed?   │
                         └──────────────────┘
                           │              │
                      No   │              │  Yes
                           ▼              ▼
              ┌─────────────────┐   ┌─────────────────────┐
              │  Skip Generation │   │  Parse & Generate   │
              │  (use cached)    │   │                     │
              └─────────────────┘   └─────────────────────┘

                        ┌─────────────────────┴─────────────────────┐
                        ▼                                           ▼
          ┌─────────────────────────┐             ┌─────────────────────────┐
          │    api.types.ts         │             │   api.operations.ts     │
          │    (from schemas)       │             │   (from operationIds)   │
          └─────────────────────────┘             └─────────────────────────┘

If the spec hasn't changed, the entire generation step is skipped. If it has, only the _generated/ folder is overwritten—your customizations in api.client.ts and api.instance.ts are preserved.

Two Ways to Call Your API

chowbea-axios gives you two equivalent ways to make API calls. Both are fully typed—pick whichever reads better in your codebase.

Path-based — Reference endpoints by their URL path:

const { data, error } = await api.get("/users/{id}", { id: "123" });

Operation-based — Reference endpoints by their operationId:

const { data, error } = await api.op.getUserById({ id: "123" });

The path-based style is intuitive if you think in REST resources. The operation-based style is cleaner if your spec has well-named operationIds. Use both interchangeably.

Result-Based Errors

API calls return { data, error } instead of throwing exceptions:

const { data, error } = await api.get("/users/{id}", { id: "123" });

if (error) {
  console.log(error.status);  // HTTP status code
  console.log(error.message); // Normalized message
  return;
}

// data is fully typed here
console.log(data.name);

No try/catch blocks. No wondering what shape the error might be. Every error has the same structure, whether it's a network failure, a 404, or a validation error.

This pattern makes error handling explicit and predictable. You always know exactly where errors are handled because you have to check for them.

What Gets Generated

Your OpenAPI spec is processed into these files:

api.types.ts
api.operations.ts
api.client.ts
api.instance.ts
api.error.ts
api.helpers.ts

The _generated/ folder is overwritten on each generation. The other files are created once and safe to modify—add interceptors, change the base URL, customize error handling.

Built for Developer Experience

  • Self-healing — Auto-creates directories and guides you through setup
  • Smart caching — Skips regeneration when your spec hasn't changed
  • Retry logic — Network requests retry with exponential backoff
  • Atomic writes — Generation never leaves files in a partial state
  • Graceful shutdown — Watch mode preserves cache on interruption

Support the Project

If chowbea-axios helps you ship faster, consider giving it a ⭐ star on GitHub. It helps others discover the project and motivates continued development.

Next Steps

On this page