Skip to content

Getting Started ​

1. Scaffold ​

bash
npx create-tzin my-api
cd my-api

Pick a template: node, bun or workers.

2. Define a contract ​

ts
// src/routes/users.ts
import { t, contract, impl } from '@carlos-tzin/tzin'

export const getUser = contract({
  method: 'GET',
  path: '/users/:id',
  params: t.Object({ id: t.String() }),
  responses: {
    200: t.Object({ id: t.String(), name: t.String() }),
    404: t.Object({ error: t.String() }),
  },
})

export const getUserRoute = impl(getUser, async ({ params }) => {
  return { status: 200, body: { id: params.id, name: 'Ada' } }
})

3. Create the app ​

ts
// src/app.ts
import { createApp } from '@carlos-tzin/tzin'
import { getUserRoute } from './routes/users.js'

export const app = createApp([getUserRoute], {
  openapi: true,
  mcp: true,
  meta: { title: 'My API', version: '1.0.0' },
})

This automatically exposes:

  • GET /openapi.json — OpenAPI 3.1
  • POST /mcp — MCP Streamable HTTP
  • GET /llms.txt / GET /llms-full.txt — when llms: true

4. Run ​

bash
npm run dev    # tzin dev  -> dev server at http://localhost:3000
npm run build  # tzin build -> production build -> dist/

The tzin binary comes from the @carlos-tzin/tzin dependency, so it is available in node_modules/.bin after npm install. Without a local install:

bash
npx --package @carlos-tzin/tzin tzin dev

CLI ​

bash
tzin dev [--port N]                                # dev server with hot reload
tzin build                                         # build for production
tzin deploy --target node|workers [--pack] [--docker <tag> [--push]]  # deploy
tzin generate route <name>                         # scaffold a route
tzin generate middleware <name>                    # scaffold middleware
tzin generate test <name>                          # scaffold a test

Typed client ​

ts
import { client } from '@carlos-tzin/tzin'
import { getUser } from './routes/users.js'

const api = client({ getUser }, 'http://localhost:3000')
const res = await api.getUser({ params: { id: '42' } })
if (res.status === 200) {
  console.log(res.body.name) // string
}

Next steps ​

Released under the MIT License.