Installation

Get a new tekir project up and running in under a minute.

Prerequisites

tekir requires Bun 1.3.10 or higher. It also runs on Node.js 22+ via @tekir/runtime, but Bun is the recommended runtime. If you haven't installed Bun yet:

# macOS / Linux
curl -fsSL https://bun.sh/install | bash

# Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"

Verify your Bun version:

bun --version

Create a New Project

Use the official tekir CLI to scaffold a new project. The CLI will prompt you for a few options (database driver, auth strategy, frontend type, etc.) and then generate a fully configured project.

bunx create-tekir-app my-app
tekir v0.1.0, creating project...

  ✓ Scaffolding project structure
  ✓ Writing configuration files
  ✓ Installing dependencies

  Done! To get started:

    cd my-app
    bun run dev

Alternatively, you can install the packages manually if you want to add tekir to an existing Bun project:

bun add @tekir/core @tekir/http-decorators @tekir/config @tekir/logger

Project Templates

The CLI ships with 5 starter templates. Pass --template to skip the interactive prompt:

Minimal

Single-file TODO API with SQLite, Swagger, and CORS. Perfect for prototyping or learning tekir.

bunx create-tekir-app my-app --template=minimal

API

Full API project with auth, database, validation, mail, and more. The recommended starting point for backend services.

bunx create-tekir-app my-app --template=api

Fullstack

API + React frontend with views, auth, and database. Everything you need for a full-stack web application.

bunx create-tekir-app my-app --template=fullstack

Vite + React

tekir API backend + Vite React frontend served on the same port. Full HMR in development, optimized build in production.

bunx create-tekir-app my-app --template=with-vite

Next.js

tekir API backend + Next.js SSR frontend on the same port. Pages router with API passthrough to tekir.

bunx create-tekir-app my-app --template=with-next

Project Structure

A freshly scaffolded tekir project has the following layout:

my-app/
my-app/
├── index.ts              # Entry point: tekir()
├── services.ts           # Typed service accessors
├── types.ts              # Module augmentation
├── env.ts                # Environment validation
├── config/
│   ├── app.ts            # Application config
│   ├── database.ts       # Database config
│   ├── auth.ts           # Auth config
│   ├── cache.ts          # Cache config
│   └── ...               # Other package configs
├── start/
│   ├── kernel.ts         # Providers, middleware
│   ├── boot.ts           # DB setup, cron registration
│   ├── events.ts         # Event listeners
│   └── routes.ts         # Route/controller registration
├── core/
│   ├── controllers/      # HTTP controllers
│   ├── models/           # Database models
│   ├── middleware/        # Custom middleware
│   ├── listeners/        # Event listeners
│   ├── schedules/        # Cron schedules
│   └── jobs/             # Background jobs
├── database/
│   ├── migrations/       # Database migrations
│   └── seeders/          # Database seeders
└── tests/                # Test files

core/

The core/ directory contains your application code, controllers, models, middleware, listeners, schedules, and jobs. This is where you will spend most of your time.

config/

All framework configuration lives here. Each file exports a typed configuration object using tekir's config utilities. You never have to parse environment variables manually; use env.ts for that.

start/

The start/ directory is the application bootstrap layer. Files are auto-loaded in order: kernel.ts first, boot.ts second, then remaining files alphabetically, and routes.ts last. Each file exports a default function that receives the TekirApp instance.

index.ts

The entry point for your application. It calls tekir() which returns a TekirApp with app, server, router, logger, config, service, start, onStart, and onShutdown:

index.ts
import { tekir } from '@tekir/core'

const app = await tekir({
  envFile: 'env.ts',
  configDir: 'config',
  startDir: 'start',
})
app.start()

Environment Variables

tekir uses @tekir/env for type-safe environment variables. Your .env file looks like:

.env
TZ=UTC
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=info
APP_KEY=              # run: tekir generate:key

# Database
DB_CONNECTION=sqlite
DB_FILE=./database/app.sqlite

# Or with PostgreSQL
# DB_CONNECTION=pg
# DB_HOST=localhost
# DB_PORT=5432
# DB_USER=postgres
# DB_PASSWORD=secret
# DB_DATABASE=myapp

And env.ts validates and types those values at startup:

env.ts
import { defineEnv, str, port } from '@tekir/env'

export default defineEnv({
  TZ: str({ default: 'UTC' }),
  PORT: port({ default: 3000 }),
  APP_KEY: str(),
  DB_CONNECTION: str({ choices: ['sqlite', 'pg', 'mysql'] }),
  DB_FILE: str({ default: '' })
})

If a required variable is missing or has the wrong type, tekir will throw a descriptive error at boot time, never at request time.

Running the Server

Development

The dev server uses Bun's native file watcher for instant hot-reload:

# Start the development server with hot reload
tekir serve --dev

# Server started on http://localhost:3000

Production

Build a plain bundle with --outdir, or a single self-contained executable with --compile. Compiled binaries dispatch commands directly: ./server routes, ./server generate:key. The tekir bin is only needed during development.

# Plain bundle
tekir build --outdir ./dist

# Single executable
tekir build --compile --outfile server

# Run the production binary
./server

# Or in source mode:
tekir serve

Entry File Resolution

tekir figures out which file to import for every command using a fixed lookup order. In a default project layout you never type the entry path.

  • --entry <path> flag, accepted by every command. Use it when you have a one-off run from a custom file.
  • "tekir": { "entry": "<path>" } in package.json. Set this once if your entry is not one of the defaults; every command picks it up automatically.
  • First match among index.ts, api/index.ts, app/index.ts, src/index.ts, index.js. The bin never treats a positional argument as an entry, so command arguments like tekir make:controller User are passed through verbatim.
# tekir resolves the entry file in this order:
#   1. --entry <path> flag, anywhere in argv
#   2. "tekir": { "entry": "<path>" } in package.json
#   3. First match among these defaults:
#      index.ts, api/index.ts, app/index.ts, src/index.ts, index.js
#
# 99% of projects keep the default and never specify an entry.
# Set "tekir.entry" once if you renamed the file:

# package.json
{
  "name": "my-app",
  "tekir": { "entry": "server.ts" }
}

# Then everything else stays clean:
tekir serve
tekir build --outdir ./dist
tekir routes

Editor Setup

tekir is fully typed and works best with Visual Studio Code or any editor with TypeScript LSP support.

  • Install the official TypeScript extension for VS Code.
  • Enable "typescript.tsdk": "node_modules/typescript/lib" in your workspace settings to use the project's TypeScript version.
  • Decorators require "experimentalDecorators": true and "emitDecoratorMetadata": true in your tsconfig.json. The scaffolded project includes these by default.