Type-safe APIs on Bun, without the setup.
Documentation · Getting started · Example · Contributing
Lyn is a small backend framework for Bun. Chain your routes, describe their inputs with Zod, and get typed handlers, request validation, consistent JSON errors and Postgres out of the box.
It is made for proofs of concept, solo developers and agencies: a working, safe API quickly, without assembling a stack first.
bun add @rouret/lynLyn runs on Bun only (>= 1.3.6) and needs TypeScript >= 5.9.
Describe your tables with the Drizzle helpers exported by @rouret/lyn/db:
// src/db/schema.ts
import { pgTable, text, timestamp, uuid } from "@rouret/lyn/db";
export const users = pgTable("users", {
id: uuid().primaryKey().defaultRandom(),
email: text().notNull().unique(),
displayName: text(),
createdAt: timestamp().notNull().defaultNow(),
});Pass the schema to Lyn, and every handler gets a typed db:
// src/index.ts
import { Lyn, z } from "@rouret/lyn";
import * as schema from "./db/schema";
await new Lyn({ database: { schema } })
.get("/users", ({ db }) => db.query.users.findMany())
.post(
"/users",
async ({ db, body }) => {
const [user] = await db.insert(schema.users).values(body).returning();
return user;
},
{ body: z.object({ email: z.email(), displayName: z.string().optional() }) }
)
.listen(3000);Create the table, then start the server:
echo "DATABASE_URL=postgres://postgres:changeme@localhost:5432/app" > .env
bun add -d drizzle-kit
bunx lyn db generate && bunx lyn db migrate
NODE_ENV=development bun run src/index.tscurl -X POST localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"email":"ada@example.com"}'
# 201 {"id":"…","email":"ada@example.com","displayName":null,"createdAt":"…"}A missing DATABASE_URL, an unreachable database or an invalid body are
caught before any query runs.
Next steps are in Getting started.
- Postgres built in: pass a Drizzle schema, get a typed
dbin every handler. - Migrations:
lyn db generateandlyn db migrate, ormigrateOnStart. - Typed handlers:
body,paramsandqueryare typed from your Zod schemas. - Validation: invalid requests get a
400before your handler runs. - One error format: every error has the same JSON shape, and internal details never leak.
- Checked configuration: environment variables are validated and typed at startup.
- CORS: one option, preflight included.
- Logs: one line per request with a request id, JSON in production.
- Tests without a server:
app.handle(request)runs a request in memory.
| Topic | Page |
|---|---|
| Introduction | Why Lyn |
| Essentials | Routing · Handlers · Validation · Errors |
| Configuration | Overview · Environment variables · CORS · Logging |
| Database | Postgres with Drizzle |
| Guides | Testing |
| Reference | API |
examples/feedback-board is a complete
product-feedback API: Postgres, migrations, validation, transactions, CORS
and tests.
- Routing, validation, errors
- Environment variables
- Logging
- CORS
- Postgres with Drizzle
- Documentation
- CLI and migrations
- Example project
- Publish to npm
- Perf
- Benchmarking
- Rate limit
- Configuration
- Custom Middleware
- Cloudflare Workers
- Testing
- Cron
- OpenAPI
- OpenTelemetry
- Cache
- Authentication
- Security checklist
Issues and pull requests are welcome. Read CONTRIBUTING.md for the workflow and the checks to run.
