Philosophy
The design principles and trade-offs behind chowbea-axios.
You're reading this because you want to know what you're signing up for before the generator owns your src/services/api folder. Smart.
chowbea-axios is opinionated by design. This page explains those opinions, the trade-offs, and why we think "trust the contract" beats "trust the intern who wrote the types last quarter."
Server Types as Source of Truth
The Core Idea
Your server contract is the source of truth at the query boundary. Full stop.
When you import named types from api.contracts.ts, you're coupling the frontend to the backend on purpose. That's not a leak — it's the feature.
Your API returns a specific shape. Your query layer should reflect that shape exactly. Not approximately. Not "close enough until QA finds out."
import type {
UserDto,
BusinessDto,
CreateUserBody,
} from "@/services/api/_generated/api.contracts";
type User = UserDto;
type Business = BusinessDto;These types regenerate when the spec changes. Prefer named contracts over helpers like ServerModel<"…"> — cmd+click lands on a real interface, and request bodies stay distinct from entity DTOs. See Type Helpers for the escape hatches; see The Query Layer for where those types actually live in app code.
What this gives you:
| Benefit | Why It Matters |
|---|---|
| Compile-time errors | Backend changes something you depend on? TypeScript tells you immediately. |
| Zero type drift | What the server sends = what your code expects. Always. |
| Full IntelliSense | Your IDE knows your entire API contract. |
When UI Needs Different Shapes
Sometimes the UI genuinely needs a different shape than the server returns. When that happens, create explicit UI types with transformers — don't silently mutate DTOs in components and hope nobody notices.
import type { UserDto } from "@/services/api/_generated/api.contracts";
// Server type (from OpenAPI contracts)
type User = UserDto;
// UI-specific type (defined in your app)
type UserCardUI = {
id: string;
displayName: string;
avatarUrl?: string;
};
// Explicit transformation at the boundary
const toUserCardUI = (user: User): UserCardUI => ({
id: user.id,
displayName: `${user.firstName} ${user.lastName}`,
avatarUrl: user.profile?.avatar,
});This keeps boundaries honest:
Server types stay pure
They mirror the API exactly — no frontend-specific fields mixed in.
UI types are derived
Explicitly transformed from server types, with clear ownership.
Changes are localized
Backend changes affect the transformer, not 40 components scattered across your app.
But here's the thing...
For most well-designed apps, you won't need many transformations.
Proper backends already shape data for consumers, filter internal fields, and return display-ready formats.
If you're writing transformers for everything, that's often a signal the API contract could improve — not that you need more frontend logic.
The rule: Start with server types everywhere. Add a mapping layer only when you genuinely need it.
Built for Builders Who Own Their Stack
If you're a solo developer, indie hacker, or vibe coder who ships their own backend — this tool was made for you.
You control both sides of the contract. Tight coupling isn't a risk; it's a superpower.
- Instant type sync — Change an endpoint, regenerate, see TypeScript errors immediately
- No coordination overhead — No waiting for another team to update types or docs
- Rapid iteration — Ship features without maintaining two type systems
- AI-friendly structure — Opinionated patterns help tools generate consistent code instead of spaghetti
The workflow:
1. Change your backend endpoint
2. chowbea-axios regenerates types (watch mode)
3. TypeScript shows you what broke
4. Fix it before it shipsNo manual type updates. No "oops, forgot to sync the types" postmortems.
The Recommended App Stack
Generation is step one. The stack that ships:
contracts → api.op → exec → TanStackTypes from api.contracts.ts. Calls via api.op. Unwrap { data, error } once in a query class. Expose queryOptions and hooks to components. Details: The Query Layer.
Wire it into daily habits with api:watch and dev:all:
{
"scripts": {
"api:watch": "chowbea-axios watch",
"dev:all": "concurrently \"npm run api:watch\" \"npm run dev\""
}
}Staying Fast Without Losing Structure
The trap with moving fast is losing structure. Codebases turn into spaghetti. Types drift. AI tools start inventing patterns.
chowbea-axios adds guardrails:
- Generated types can't drift — They come from the spec, period
- Consistent patterns — Same file structure, same error handling, everywhere
- Watch mode — Types update automatically as you iterate on the backend
Move fast and keep the codebase legible. Novel concept.
Trade-offs to Be Aware Of
Every tool makes trade-offs. Here's what you're signing up for.
Opinionated Design
The tool decides: file structure, error handling pattern, client organization. You can't configure your way out of these choices.
The upside: Convention over configuration. Less bikeshedding. New team members know where things live.
Server DTOs First
Components start by consuming server types from api.contracts.ts. If the UI needs different shapes, you add a mapping layer yourself.
The upside: You avoid premature abstraction. Most teams need fewer transformers than they expect.
Hands-Off Generated Code
The _generated/ folder gets overwritten on every generation. Customizations go in the editable files only.
The upside: No merge conflicts with generated code. Regeneration is always safe.
Is This Right for You?
chowbea-axios works best when:
- Your OpenAPI spec is accurate and kept up-to-date
- You want "it just stays synced" without manual type maintenance
- You value convention and structure over maximum flexibility
- You're okay with the Result-based error pattern (unwrap once in the query layer)
- You own your backend (or trust the team that does)
It might not be the best fit if:
- You need extensive customization of the generated output
- Your UI shapes differ significantly from API responses and you can't fix the API
- You're consuming an external API you don't control and the spec is fiction
The Bottom Line
The philosophy is deliberately simple: trust your API contract, generate from it, build your app on that foundation.
If that resonates, chowbea-axios will feel like a superpower. If not, at least you read this before init.