Contract-first
Flat registry, O(1) inference per endpoint. No builder-chain blowup โ 15k types for 100 routes vs 112k in Hono.
Types that scale. Realtime built in. AI-native from day one. Declare a contract once โ get validation, OpenAPI, typed clients and MCP for free.
import { t } from '@carlos-tzin/tzin'
import { contract, impl, createApp, listen } from '@carlos-tzin/tzin'
const getUser = contract({
method: 'GET',
path: '/users/:id',
params: t.Object({ id: t.String() }),
responses: {
200: t.Object({ id: t.String(), name: t.String(), tags: t.Array(t.String()) }),
404: t.Object({ error: t.String() }),
},
})
export const getUserRoute = impl(getUser, async ({ params }) => {
const user = await findUser(params.id)
if (!user) throw new HttpError(404, 'user not found')
return { status: 200, body: user }
})
const app = createApp([getUserRoute], { openapi: true, mcp: true })
listen(app, 3000)From that single declaration you get:
params/query/body appear only if declared, fully typed and validated.200 shape is a type error.| N routes | tzin: types | Hono: types | tzin: instantiations |
|---|---|---|---|
| 20 | 4,851 | 72,368 | 22,472 |
| 100 | 15,779 | 111,952 | 93,128 |
| 300 | 43,099 | 210,912 | 270k |
Strictly linear โ ~130 types/endpoint.
| Runtime | req/s | p99 |
|---|---|---|
| raw node:http | ~42k | 2โ3ms |
| hono | ~34โ36k | 3ms |
| tzin | ~30โ31k | 6ms |
| express | ~15k | 7ms |
npx create-tzin my-api
cd my-api
npx tzin dev # http://localhost:3000Want the full story? Start with Why tzin? โ Getting Started.