Introduction
A full-stack TypeScript framework for Bun, with Elysia-level performance and an AdonisJS-style developer experience.
What is tekir?
tekir is a full-stack, TypeScript-first web framework built for Bun (and also runs on Node 22+). It pairs a fast Bun-native HTTP layer, inspired by Elysia, with the MVC layout, providers, and ActiveRecord ORM style you get from AdonisJS.
Unlike Node.js frameworks bolted onto Bun, tekir targets Bun's native APIs directly: its HTTP server, file I/O, bun:sqlite, Bun.password, and Bun.file. When Bun is not available, tekir falls back to Node 22+ equivalents without any config changes.
tekir follows the MVC pattern with decorator-based controllers, an ActiveRecord ORM, typed configuration, and first-party packages covering authentication, caching, mail, queues, file storage, and more.
Key Features
Bun-native
tekir uses Bun's built-in HTTP server, Bun.serve, rather than wrapping Node's httpmodule. This means you get Bun's native performance with zero overhead from compatibility shims.
Elysia-level performance
tekir performs on par with Elysia, within 1% on average across all endpoints. Both sit at near-raw Bun speed.
AdonisJS developer experience
If you have used AdonisJS, tekir will feel immediately familiar. Decorator-based controllers, an ActiveRecord ORM with a fluent query builder, typed HTTP context, and a conventional project structure make large codebases easy to navigate and maintain.
TypeScript-first
Every package in the tekir ecosystem ships with full TypeScript types. Configuration, middleware, models, services, and the HTTP context are all fully typed.
46 first-party packages
Rather than leaving you to assemble a stack from the npm ecosystem, tekir ships first-party packages for auth, caching, mail, queues, file storage, notifications, and more, all designed to work together.
Quick Example
A complete TODO API in a single file:
import { tekir } from '@tekir/core'
import { DatabaseProvider } from '@tekir/db'
import type { Database } from '@tekir/db'
const { router, service } = await tekir({
config: {
app: { name: 'My App', port: 3000 },
database: {
default: 'sqlite',
connections: {
sqlite: { driver: 'sqlite', connection: { path: ':memory:' } }
}
}
},
providers: [DatabaseProvider]
})
const db = service<Database>('db')
await db.exec(`CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
done INTEGER DEFAULT 0
)`)
router.get('/todos', async () => {
return await db.query('SELECT * FROM todos')
})
router.post('/todos', async ({ body }) => {
await db.run('INSERT INTO todos (title) VALUES (?)', [body.title])
return await db.queryOne('SELECT * FROM todos ORDER BY id DESC LIMIT 1')
})
router.delete('/todos/:id', async ({ params }) => {
await db.run('DELETE FROM todos WHERE id = ?', [params.id])
return { deleted: true }
})As your app grows, use the full project structure:
index.ts await tekir()
env.ts defineEnv({ PORT: port({ default: 3000 }) })
services.ts export const db = service<Database>('db')
config/
app.ts { name: env.APP_NAME, port: env.PORT }
database.ts { default: 'sqlite', connections: { ... } }
start/
kernel.ts register providers, middleware
routes.ts define routes
boot.ts seed data, run migrations
app/
models/ User, Post, ...
controllers/ UserController, PostController, ...
middleware/ auth, logging, ...Package Ecosystem
tekir ships 45 first-party packages. Each package is independently versioned and can be used standalone or as part of the full framework:
Benchmarks
The following benchmarks were run with autocannon at 100 connections, 10x pipelining, over a 10-second window on Windows 11.
Endpoint Raw Bun tekir Elysia
──────────────────────────────────────────────────────────
/json 22,248 21,232 22,232
/users/42 23,891 21,025 21,304
/posts/1/comments/5 21,387 21,248 22,091
/search?q=hello&page=2 20,716 20,714 21,016
──────────────────────────────────────────────────────────
AVERAGE 22,060 21,055 21,660
Run on Ubuntu 24.04, 1 vCPU (DigitalOcean Premium Intel),
2 GB RAM, Bun 1.3.12, autocannon 100 conn, no pipelining.tekir and Elysia perform within 1% of each other on average. Both sit at near-raw Bun.serve() performance.