baguette
Bun + Hono API framework

Blazingly fast.
Zero-config.
Type-safe end to end.

The framework your AI can’t make a mess of. Drop a file, get a typed, documented, validated endpoint.

bun add baguette
Read the docs
api/customers/[id].ts
// api/customers/[id].ts
import { defineRoute, z } from "baguette";

export default defineRoute({
  method: "get",
  request: { params: z.object({ id: z.string() }) },
  response: z.object({ id: z.string(), name: z.string() }),
  handler: (c, { params }) =>
    c.json({ id: params.id, name: "Ada" }),
});

One zod declaration → validation, types, docs, and an error funnel.

The whole idea

One file becomes a typed, documented endpoint.

Point serve() at a directory. Every file that default-exports a defineRoute is mounted at the path its location implies. That’s the entire wiring step.

server.ts
// server.ts
import { serve } from "baguette";

serve({ routesDir: "./api" });
// routes loaded · validation on · docs at /api/docs
01

Runtime validation

Bad input never reaches your handler — 400 automatically.

02

Inferred TS types

params, query and body arrive fully typed. No casts.

03

OpenAPI + Scalar docs

The spec and the docs page write themselves.

04

Error funnel

Unhandled throws become a clean 500. One place to look.

Everything, by convention.

Six moving parts
01

File-based routing

The file's location is the URL. api/customers/[id].ts becomes /api/customers/{id}. Nothing to register, nothing to hardcode.

02

zod-first, one source of truth

Declare request and response as zod schemas once. Validation, inferred handler types, and the OpenAPI spec all fall out of that single declaration.

03

Auto OpenAPI docs

A live Scalar UI at /api/docs and the raw spec at /api/doc — generated from your schemas, never hand-wired, never stale.

04

ORM-agnostic

The framework imports no ORM. Bring Prisma, Drizzle, or raw SQL. Nothing in the hot path you didn't put there.

05

Optional cron & automations

Drop a file in cron/ for scheduled jobs (Postgres advisory lock) or automations/ for LISTEN/NOTIFY event handlers. Off until the directory exists.

06

AI-proof by convention

One obvious way to do each thing, enforced. A shipped clean-code contract, a baguette/eslint preset, and baguette check keep the codebase boring and typed.

The moat

The framework your AI can’t make a mess of.

Generated code drifts because there are a hundred ways to do everything. baguette ships one. A clean-code contract in AGENTS.md, a baguette/eslint preset, and a checker turn those conventions into CI failures — so an agent physically can’t merge the mess.

  • One route per file, path from location
  • All I/O through zod — no manual parsing
  • No as any / as never in app code
  • logger, not console.log
  • Scaffold with baguette new, never freehand
  • Boring, typed, and deletable beats clever
Read the contract →
terminal
$ baguette check

  api/customers/create.ts
    warn  hardcoded path  derive it from the file location
    warn  manual c.req.json()  declare a request.body schema

  api/legacy/proxy.ts
    error  'as any' in app code  the framework is missing a type

   warnings ·  error ·  routes clean

baguette check runs in CI. Clean code passes; clever code doesn’t.