Getting Started#
A comprehensive guide to get started building web applications with the Buntok Framework.
Prerequisites#
Before getting started, make sure you have installed:
- Bun >= 1.2.0 - https://bun.sh
- TypeScript >= 5.0
- Node.js (optional, for npm package compatibility)
enableReusePort() (SO_REUSEPORT) are Linux-only. On other platforms, this feature is disabled with a warning in development mode.Quick Setup#
The fastest way to get started is by using buntok init:
# 1. Create a new project
mkdir my-app && cd my-app
bun init -y
# 2. Install buntok
bun add @buntok/core
# 3. Run the init command
bunx buntok initbuntok init will automatically set up your project with an optimal configuration. You'll be asked if you want to deploy to Vercel - if yes, a vercel.json will be created for you.Generated src/index.ts
Clean app setup - same for both Vercel and non-Vercel:
import { Buntok } from "@buntok/core";
export const app = new Buntok();
app.get("/", (ctx) => {
return ctx.json({ message: "Hello from Buntok!" });
});
export default app;Generated server.ts
Local server entry point used by the development script:
import { app } from "./src/index";
import { env } from "./src/env";
app.listen(env.PORT);Generated src/env.ts
Type-safe environment schema with sensible defaults:
import { Buntok } from "@buntok/core";
import { z } from "@buntok/core/middlewares/validator";
export const env = Buntok.validateEnv({
PORT: z.coerce.number().default(1212),
AUTH_STORE: z.enum(["header", "cookie"]).default("header"),
AUTH_COOKIE: z.string().default("session"),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
});What does buntok init do?
This command will create and configure:
| File | Description |
|---|---|
src/index.ts | Application entry point - exports the app instance |
tsconfig.json | TypeScript configuration |
src/env.ts | Type-safe environment schema |
biome.json | Biome linter configuration |
.env | Environment variables |
.env.example | Environment variables template |
.vscode/settings.json | VS Code settings for this project |
.gitignore | Git ignore rules |
.agents/skills/buntok-skill/SKILL.md | Skill guide for AI assistants |
vercel.json (optional) | Vercel deployment config - includes framework: "bun" |
server.ts | Local server entry point - calls app.listen(env.PORT) |
.agents/skills/buntok-skill/SKILL.md contains the complete framework documentation that can be read by AI assistants such as Cursor, Copilot, or Claude.Environment Configuration#
Buntok uses environment variables for configuration. The buntok init command creates a type-safe src/env.ts file and a .env file automatically.
Default Schema#
The generated src/env.ts validates your environment at startup with sensible defaults:
import { Buntok } from "@buntok/core";
import { z } from "@buntok/core/middlewares/validator";
export const env = Buntok.validateEnv({
PORT: z.coerce.number().default(1212),
AUTH_STORE: z.enum(["header", "cookie"]).default("header"),
AUTH_COOKIE: z.string().default("session"),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
});Adding More Variables#
Extend the schema as needed. The server will exit with a clear error if any required variable is missing or invalid:
export const env = Buntok.validateEnv({
PORT: z.coerce.number().default(1212),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
});process.exit(1). The app will not start.Project Structure#
Project structure after buntok init (with Vercel selected):
my-app/
├── src/
│ ├── index.ts # export const app = new Buntok()
│ ├── env.ts # Type-safe env schema
│ └── modules/ # Feature-based modules
│ └── user/
│ ├── index.ts # barrel export
│ ├── user.schema.ts
│ ├── user.repository.ts
│ ├── user.service.ts
│ └── user.controller.ts
├── server.ts # app.listen(env.PORT) - local server entry point
├── vercel.json # Vercel config (framework: "bun")
├── .agents/
│ └── skills/
│ └── buntok-skill/
│ └── SKILL.md # AI skill guide
├── .vscode/
│ └── settings.json # VS Code settings
├── .env # Environment variables
├── .env.example # Environment variables template
├── .gitignore # Git ignore rules
├── biome.json # Linter config
├── tsconfig.json # TypeScript config
└── package.jsonvercel.json is not created. server.ts is always created for local development. A serverless deployment uses app.fetch through its deployment adapter.Database Setup#
Buntok supports multiple database drivers through official packages:
| Package | Description |
|---|---|
@buntok/prisma | Prisma ORM integration |
@buntok/drizzle | Drizzle ORM integration |
@buntok/typeorm | TypeORM integration |
Prisma v7 (Recommended)#
We recommend using Prisma v7 for better stability and performance with Bun. Prisma v7 includes native Bun support and improved query performance.
# Install Prisma v7 with Buntok integration
bun add @buntok/prisma prisma@7 @prisma/client@7
# Initialize Prisma schema
bunx prisma init
# Generate Prisma client
bunx prisma generate- Native Bun runtime support (no Node.js polyfills needed)
- Faster query performance with optimized SQL generation
- Smaller bundle size compared to v6
- Better TypeScript inference and type safety
// src/lib/prisma.ts
import { PrismaClient } from "@prisma/client";
export const prisma = new PrismaClient();// src/repositories/user.repository.ts
import { BaseRepository } from "@buntok/prisma";
import { prisma } from "@/lib/prisma";
import type { User, PrismaClient, Prisma } from "@prisma/client";
export class UserRepository extends BaseRepository<
User,
PrismaClient,
Prisma.UserCreateInput,
Prisma.UserUpdateInput
> {
constructor() {
super(prisma, "user");
}
}// src/services/user.service.ts
import { BaseService } from "@buntok/core";
import { UserRepository } from "@/repositories/user.repository";
import type { User } from "@prisma/client";
export class UserService extends BaseService<User> {
constructor(private userRepository: UserRepository) {
super(userRepository);
}
}buntok db migrate to run migrations and buntok db seed to seed your database.Architecture Overview#
Buntok follows a layered architecture pattern for organized, maintainable code. Base classes auto-generate CRUD operations so you don't have to write boilerplate.
Request Flow#
Request
│
▼
Route (decorator-based or manual)
│
▼
Controller (handles HTTP, validates input)
│
▼
Service (business logic)
│
▼
Repository (data access)
│
▼
DatabaseLayer Descriptions#
| Layer | Base Class | Responsibility |
|---|---|---|
| Controller | BaseController | Handles HTTP requests/responses, auto-registers CRUD routes |
| Service | BaseService | Business logic, data transformation, NotFoundError handling |
| Repository | BaseRepository | Data access layer, database queries, lifecycle hooks |
Auto-Generated CRUD#
Extend the base classes to get a full CRUD API without writing boilerplate:
// 1. Repository - extends BaseRepository from your ORM
import { BaseRepository } from "@buntok/prisma"; // or @buntok/drizzle, @buntok/typeorm
export class UserRepository extends BaseRepository<User, PrismaClient> {
constructor(prisma: PrismaClient) {
super(prisma, "user");
}
}
// 2. Service - extends BaseService
import { BaseService } from "@buntok/core";
export class UserService extends BaseService<User, CreateUser, UpdateUser> {
constructor(private repo: UserRepository) {
super(repo);
}
}
// 3. Controller - extends BaseController (auto-registers 5 CRUD routes!)
import { Controller } from "@buntok/core";
import { BaseController } from "@buntok/core";
@Controller("/users")
export class UserController extends BaseController<User, CreateUser, UpdateUser> {
constructor(private service: UserService) {
super(service);
}
}
// This automatically creates:
// GET / → service.getAll()
// GET /:id → service.getById(id)
// POST / → service.create(body)
// PUT /:id → service.update(id, body)
// DELETE /:id → service.delete(id)BaseRepository: @buntok/prisma, @buntok/drizzle, or @buntok/typeorm. Choose the one that matches your stack.Routing Approaches#
Buntok supports two routing approaches:
1. Decorator-based (Recommended)#
Routes are automatically registered using decorators:
import { Controller, Get, Post } from "@buntok/core";
@Controller("/users")
export class UserController {
@Get("/")
findAll(ctx) {
return ctx.json({ users: [] });
}
@Post("/")
create(ctx) {
return ctx.json({ message: "User created" }, 201);
}
}2. Manual Routing (Optional)#
For simple cases, you can define routes directly on the app:
import { Buntok } from "@buntok/core";
const app = new Buntok();
app.get("/", (ctx) => {
return ctx.json({ message: "Hello, Buntok!" });
});
app.post("/users", (ctx) => {
return ctx.json({ message: "User created" }, 201);
});CLI Commands#
Buntok provides several commands to help with development:
| Command | Description |
|---|---|
buntok init | Set up project with optimal configuration |
buntok build | Build project for production (output to buntok/) |
buntok create <entity> | Generate repository, service, and controller in src/modules/<entity>/ |
buntok db migrate | Run pending database migrations |
buntok db seed | Seed database with initial data |
buntok make:test <name> | Generate unit test file |
buntok make:test:e2e <name> | Generate E2E test file |
buntok make:middleware <name> | Generate middleware file |
buntok make:seeder <name> | Generate database seeder |
buntok make:docs | Manual OpenAPI docs regeneration (auto-generated on startup) |
Code Generation Example#
Running buntok create user generates:
$ bunx buntok create user
Creating User entity (orm: prisma)...
✓ Repository: src/modules/user/user.repository.ts
✓ Service: src/modules/user/user.service.ts
✓ Controller: src/modules/user/user.controller.ts
✓ Schema: src/modules/user/user.schema.ts
✓ Barrel: src/modules/user/index.ts// src/modules/user/user.controller.ts
import { Controller, Get, Post, Put, Delete } from "@buntok/core";
import { Dependencies } from "@buntok/core";
import { UserService } from "./user.service";
@Dependencies(UserService)
@Controller("/users")
export class UserController {
constructor(private service: UserService) {}
@Get("/")
async findAll(ctx) {
const users = await this.service.findAll();
return ctx.json({ data: users });
}
@Get("/:id")
async findById(ctx) {
const user = await this.service.findById(ctx.params.id);
if (!user) {
return ctx.json({ message: "User not found" }, 404);
}
return ctx.json({ data: user });
}
@Post("/")
async create(ctx) {
const body = await ctx.body();
const user = await this.service.create(body);
return ctx.json({ data: user }, 201);
}
}buntok create user --repo, --service, or --controller for partial generation. Add --prisma, --drizzle, or --typeorm to specify ORM.Package Scripts#
buntok init will also add scripts to package.json:
{
"scripts": {
"dev": "bun --watch server.ts",
"build": "bunx buntok build",
"start": "bun buntok/server.js",
"check": "bunx @biomejs/biome check --write .",
"format": "bunx @biomejs/biome format --write .",
"lint": "bunx @biomejs/biome lint ."
}
}Run the Server#
Development Mode#
# Hot reload with file watching
bun run dev
# Or directly
bun run server.tsProduction Mode#
# Build for production
bun run build
# Run the built output
bun run startDeploy to Vercel#
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Production deploy
vercel --prodOpen http://localhost:1212 in your browser. You will see:
{ "message": "Hello, Buntok!" }Port Auto-Increment#
If the specified port is already in use, Buntok will automatically try the next available port:
⚠ Port 3000 is already in use, using port 3001 instead
Server listening at http://localhost:3001Troubleshooting#
Port Already in Use#
If you see a port conflict warning, Buntok will automatically use the next available port. You can also specify a different port:
app.listen(3000); // Try port 3000
// Or via environment variable
PORT=3000 bun run devError During bunx buntok init
If the init command fails, try these steps:
- Make sure you're in the correct directory with a
package.json - Ensure Bun is installed and up to date:
bun --version - Check if you have write permissions in the current directory
Environment Variables Not Read#
If your environment variables aren't being read:
- Make sure
.envis in your project root (same level aspackage.json) - Use
validateEnv()to catch missing/invalid variables early - Restart the server after changing
.envfiles
Database Connection Refused#
If you can't connect to your database:
- Verify
DATABASE_URLis set correctly in.env - Ensure your database server is running
- Run migrations:
buntok db migrate - For Prisma, regenerate the client:
bunx prisma generate
Vercel Build Error: `readFile` undefined#
If you see cannot read properties of undefined (reading 'readFile') on Vercel:
- Remove
typescriptfrompeerDependenciesinpackage.json - Keep it only as
devDependencyif needed - TypeScript is already included via
tsupand other build tools
Next Steps#
- Routing - Learn how to define routes
- Controllers - Organize routes with controllers and
BaseController - Repository - CRUD operations with
BaseRepository - Context - Access request and response
- Plugins - Extend apps with the plugin system
- GraphQL - Apollo Server and Yoga integration
- OpenTelemetry - Distributed tracing with per-request spans
- Client SDK - Type-safe RPC client with retry and interceptors
- Vercel Deploy - Deploy to Vercel with Bun runtime
- Validation - Validate input data
- Buntok Configuration - Environment validation and settings