CLI#

Buntok provides a CLI for scaffolding and managing projects.

init#

Initialize a new Buntok project with optimal configuration.

typescript
bunx buntok init

Interactive Prompts#

During initialization, you'll be asked:

typescript
? Do you want to deploy to Vercel? (y/N):
? Do you want to add Docker support? (Y/n):

If you answer y, a vercel.json file will be created with the recommended configuration for Bun projects.

What Gets Created#

typescript
$ bunx buntok init

✓ Created .agents/skills/buntok-skill/SKILL.md
✓ Updated package.json (added 6 scripts)
• Installing @buntok/core...
✓ Installed @buntok/core
• Installing TypeScript toolchain (@types/bun, @types/node, typescript)...
✓ Installed @types/bun, @types/node, typescript
✓ Created tsconfig.json
• Installing @biomejs/biome...
✓ Installed @biomejs/biome
✓ Created biome.json
✓ Created .vscode/settings.json
✓ Created src/env.ts
✓ Created src/index.ts
✓ Created .env
✓ Created .env.example
✓ Created .gitignore

? Do you want to deploy to Vercel? (y/N): y
✓ Created vercel.json
✓ Created server.ts
✓ Updated dev script to: bun --watch server.ts

? Do you want to add Docker support? (Y/n): y
✓ Created Dockerfile
✓ Created .dockerignore

Generated env.ts

The src/env.ts file contains a type-safe environment schema using validateEnv(). All variables have sensible defaults and are validated at startup.

typescript
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"),
});

Add more variables as needed (e.g., DATABASE_URL, JWT_SECRET). The server will exit with a clear error if any required variable is missing or invalid.

build#

Build project for production. Compiles TypeScript to JavaScript and outputs to buntok/.

typescript
bunx buntok build

What It Does#

  • Compiles the root server.ts entry point using Bun.build()
  • Resolves path aliases (e.g., @/*) from tsconfig.json
  • Marks all packages as external (not bundled)
  • Outputs to buntok/server.js

Output#

typescript
$ bunx buntok build

🔨 Building project...
✅ Build successful → buntok/server.js
  Deploy: copy buntok/ + node_modules/ + package.json to server

Deploying#

After building, deploy these files to your server:

typescript
# Files needed for production
buntok/server.js    # Compiled application
node_modules/       # Dependencies
package.json        # Package manifest

Development vs Production#

typescript
# Development (hot reload)
bun run dev

# Build for production
bun run build

# Run production build
bun run start

check#

Run TypeScript type checking without emitting files. Displays all errors with file locations and a summary.

typescript
bunx buntok check
typescript
$ bunx buntok check

  Running type check...

src/index.ts(1,7): error TS2322: Type 'string' is not assignable to type 'number'.
src/index.ts(2,7): error TS2322: Type 'number' is not assignable to type 'string'.

  ✗ 2 errors in 2 files

Options#

FlagDescription
--jsonOutput results as JSON for CI/CD
--plainPlain text output without colors

JSON Output#

typescript
$ bunx buntok check --json

{
  "success": false,
  "errors": [
    {
      "file": "src/index.ts",
      "line": 1,
      "col": 7,
      "code": "TS2322",
      "message": "Type 'string' is not assignable to type 'number'."
    }
  ],
  "errorCount": 1,
  "fileCount": 1
}

create#

Generate repository, service, controller, and schema files for an entity using a modular structure. The generator is aware of your project: if a module already exists, new files are merged into its barrel and registration instead of being duplicated. The zod schema, faker sample data, and TypeScript types are derived from your Prisma schema when one exists (see --fields below otherwise).

typescript
bunx buntok create <entity>

Example Output#

typescript
$ bunx buntok create user

Creating User entity (orm: prisma)...

✓ Created src/modules/user/user.repository.ts
✓ Created src/modules/user/user.service.ts
✓ Created src/modules/user/user.controller.ts
✓ Created src/modules/user/user.schema.ts
✓ Created src/modules/user/index.ts

Generated Files#

By default, create generates all files using the modular structure with @Dependencies for auto DI:

Controller#

typescript
// src/modules/user/user.controller.ts
import { Dependencies, Controller } from "@buntok/core";
import { BaseController } from "@buntok/core";
import { UserService } from "./user.service";
import type { User } from "@prisma/client";

@Dependencies(UserService)
@Controller("/users")
export class UserController extends BaseController<User> {
  constructor(private readonly userService: UserService) {
    super(userService);
  }
}

Service#

typescript
// src/modules/user/user.service.ts
import { Dependencies } from "@buntok/core";
import { BaseService } from "@buntok/core";
import { UserRepository } from "./user.repository";
import type { User } from "@prisma/client";

@Dependencies(UserRepository)
export class UserService extends BaseService<User> {
  constructor(private readonly userRepository: UserRepository) {
    super(userRepository);
  }
}

Repository#

typescript
// src/modules/user/user.repository.ts
import { BaseRepository } from "@buntok/prisma";
import { prisma } from "@/lib/prisma";
import type { User, Prisma } from "@prisma/client";

export class UserRepository extends BaseRepository<
  User,
  Prisma.UserCreateInput,
  Prisma.UserUpdateInput
> {
  constructor() {
    super(prisma, "user");
  }
}

Schema (Zod)#

typescript
// src/modules/user/user.schema.ts
import { z } from "@buntok/core/middlewares/validator";

export const CreateUserSchema = z.object({
  name: z.string().min(1).max(100),
  // TODO: Add more fields
});

export const UpdateUserSchema = CreateUserSchema.partial();

export type CreateUserInput = z.infer<typeof CreateUserSchema>;
export type UpdateUserInput = z.infer<typeof UpdateUserSchema>;

Barrel Export#

typescript
// src/modules/user/index.ts
export { UserRepository } from "./user.repository";
export { UserService } from "./user.service";
export { UserController } from "./user.controller";
export { CreateUserSchema, UpdateUserSchema } from "./user.schema";
export type { CreateUserInput, UpdateUserInput } from "./user.schema";

Partial Generation#

Use flags to generate only specific files. Flags can be combined:

typescript
# Generate only the repository
bunx buntok create user --repo

# Generate only the service
bunx buntok create user --service

# Generate only the controller
bunx buntok create user --controller

# Generate only the schema
bunx buntok create user --schema

# Combined (repo + service)
bunx buntok create user --repo --service

Custom Fields & Base Classes#

No Prisma schema? Pass fields explicitly with --fields - the CLI generates the zod validation schema, faker sample data, and TypeScript types from them. Use --base to generate classes that extend the reusable BaseRepository / BaseService / BaseController instead of plain classes:

typescript
# Generate from custom fields (no Prisma schema needed)
bunx buntok create product --fields "name:string,price:number,isActive:boolean"

# Generate against existing Base* classes
bunx buntok create user --repo --service --base

ORM Selection#

By default, the CLI auto-detects your ORM from package.json dependencies. When no ORM is found it falls back to plain code - an in-memory repository, ORM-free service and controller, no @prisma/client imports. You can also specify it explicitly:

typescript
# Auto-detect ORM (default, plain fallback when none)
bunx buntok create user

# Force Prisma
bunx buntok create user --prisma

# Force Drizzle
bunx buntok create user --drizzle

# Force TypeORM
bunx buntok create user --typeorm

Controller Registration (‑‑app)#

The generated controller is registered automatically in the file that declares your Buntok (e.g. src/index.ts) - not the listener-only server.ts. Both const app = new Buntok(...) instances and group declarations like const apiv1 = app.group("/api/v1") are detected. Use --app to pick the target explicitly:

typescript
# Register into the apiV1 instance (or group)
bunx buntok create user --app apiV1

# Single Buntok instance → registered automatically, no flag needed
bunx buntok create user
Info
With multiple instances the CLI prompts on a TTY and, in non-interactive environments (CI), falls back to a deterministic default with a warning. An unknown --app name errors before any file is written, and existing registerController([…]) arrays are merged instead of duplicated.

Dry Run#

Preview what would be generated without writing files:

typescript
bunx buntok create user --dry-run

db#

Database management commands. They delegate to your detected ORM (Prisma / Drizzle / TypeORM); db seed falls back to a local seeder runner in src/db/seeders when the ORM has no seed configuration. Destructive commands ask for confirmation.

typescript
# Run pending migrations (optional name)
bunx buntok db migrate
bunx buntok db migrate add-users-table

# Reset database (drop all tables)
bunx buntok db reset

# Seed database with initial data
bunx buntok db seed

# Generate migration from schema changes
bunx buntok db generate

# Open studio (Prisma Studio / Drizzle Studio)
bunx buntok db studio

# Show migration status
bunx buntok db status
Info
Auto-detects ORM (@buntok/prisma, @buntok/drizzle, @buntok/typeorm) via detectOrm(). migrate [name] accepts optional migration name.

make:test#

Generate a unit test file for an entity.

typescript
bunx buntok make:test <entity> [--dry-run]
typescript
$ bunx buntok make:test user

✓ Created tests/user.test.ts

# Preview without writing
$ bunx buntok make:test user --dry-run

make:test:e2e#

Generate an E2E test file for an entity's API endpoints.

typescript
bunx buntok make:test:e2e <entity> [--dry-run]
typescript
$ bunx buntok make:test:e2e user

✓ Created tests/e2e/user.e2e.test.ts

# Preview without writing
$ bunx buntok make:test:e2e user --dry-run

make:seeder#

Generate a database seeder file for an entity. Works with any ORM (Prisma, Drizzle, TypeORM).

typescript
bunx buntok make:seeder <entity> [--factory] [--dry-run]
typescript
$ bunx buntok make:seeder user

✓ Created src/db/seeders/user.seeder.ts

# With factory pattern
$ bunx buntok make:seeder user --factory

# Preview without writing
$ bunx buntok make:seeder user --dry-run

Factory Pattern#

Use the --factory flag to generate a seeder that uses the Factory pattern for data generation:

typescript
bunx buntok make:seeder user --factory
typescript
// src/db/seeders/user.seeder.ts
import { prisma } from "@/lib/prisma";
import { UserFactory } from "@/factories/user.factory";

export async function seedUser() {
  console.log("Seeding User...");

  const items = UserFactory.buildMany(100);
  await prisma.user.createMany({ data: items });

  console.log("✓ User seeded successfully (100 records)");
}
Info
Requires a factory file at src/factories/<entity>.factory.ts. Run buntok make:factory <entity> first.

make:factory#

Generate a factory scaffold for an entity. Add the required fields to the generated definition before using it in tests or seeders.

typescript
bunx buntok make:factory <entity>
typescript
$ bunx buntok make:factory user

Created: src/factories/user.factory.ts

Usage:
  import { UserFactory } from "@/factories/user.factory";

  const user = await UserFactory.create();
  const users = await UserFactory.createMany(10);
  const admin = await UserFactory.create({ role: "admin" });

Generated Factory#

The generated file is a Prisma-oriented scaffold with a TODO definition. It is not ready to compile until you replace the placeholder with fields required by your User type.

typescript
// src/factories/user.factory.ts
import { Factory } from "@buntok/core";
import { faker } from "@faker-js/faker";
import type { User } from "@prisma/client";

export const UserFactory = Factory.define<User>(() => ({
  // TODO: Define your factory fields here
  // Example:
  // id: faker.number.int({ max: 10000 }),
  // name: faker.person.fullName(),
  // email: faker.internet.email(),
  // role: faker.helpers.arrayElement(["user", "admin"]),
}));

Factory API#

MethodDescription
Factory.define<T>(fn)Create a new factory with type T
.create(overrides?)Create one item (async, runs afterCreate)
.createMany(count, overrides?)Create multiple items
.build(overrides?)Generate one item synchronously
.buildMany(count, overrides?)Generate multiple items synchronously
.withDefaults(defaults)Set default overrides
.afterCreate(callback)Register post-creation hook
Factory.ref(fn)Lazy reference for relations
Factory.pick(arr, count?)Random item(s) from array
Info
Requires @faker-js/faker as a peer dependency. bunx buntok make:factory installs it automatically with bun add -d @faker-js/faker when it is missing.

debug:routes#

Show all registered routes with their middleware chains. Useful for debugging route registration and middleware ordering. The command tries app entry candidates in order (server.ts, src/index.ts, ...) and exits after printing - the server never stays running. A listener-only server.ts without export { app }; simply falls through to the next candidate; only when no candidate exports an app does it fail with exit code 1.

typescript
bunx buntok debug:routes
typescript
$ bunx buntok debug:routes

Method │ Path               │ Middlewares                    │ Handler
───────┼────────────────────┼────────────────────────────────┼──────────
GET    │ /                  │ -                              │ (index)
GET    │ /users             │ cors, compress                 │ getAll
GET    │ /users/:id         │ cors, compress, auth           │ getById
POST   │ /users             │ cors, compress, validation     │ create

4 routes registered

By source:
  UserController: 3 routes
  direct: 1 routes

JSON Output#

Use --json flag for programmatic access:

typescript
bunx buntok debug:routes --json

dev#

Start the development server with hot reload. Optionally expose it publicly for testing webhooks or sharing.

typescript
bunx buntok dev
typescript
$ bunx buntok dev

Starting development server...

  Server running at http://localhost:1212

  Press Ctrl+C to stop

Public Tunnel#

Use --expose to create a public URL via localtunnel. Useful for webhook testing, mobile testing, or sharing with others:

typescript
bunx buntok dev --expose
typescript
$ bunx buntok dev --expose

Starting development server with tunnel...

  Server running at http://localhost:1212
  Tunnel: https://buntok-1234.loca.lt → http://localhost:1212

  Press Ctrl+C to stop
Warning
Requires localtunnel as a dev dependency. Install with: bun add -d localtunnel

Options#

FlagDescription
--exposeCreate public tunnel URL via localtunnel
--port=PORTPort number (default: 1212)

make:middleware#

Generate a middleware file.

typescript
bunx buntok make:middleware <name> [--dry-run]
typescript
$ bunx buntok make:middleware auth

✓ Created src/middlewares/auth.ts

# Preview without writing
$ bunx buntok make:middleware auth --dry-run

make:docs#

Manually regenerate OpenAPI documentation. Optional - swagger.json is auto-generated on app.listen().

typescript
bunx buntok make:docs
Info
Use this for CI/CD pipelines or when you need to regenerate swagger.json without starting the server.

Aliases#

The create command has shorter aliases:

typescript
# These are all equivalent:
bunx buntok create user
bunx buntok g user
bunx buntok gen user
bunx buntok generate user

Project Structure#

After using buntok init and buntok create user:

typescript
my-app/
├── src/
│   ├── index.ts
│   ├── env.ts
│   └── modules/
│       └── user/
│           ├── index.ts              # barrel export
│           ├── user.controller.ts    # @Dependencies + BaseController
│           ├── user.service.ts       # @Dependencies + BaseService
│           ├── user.repository.ts    # BaseRepository (Prisma)
│           └── user.schema.ts        # Zod schemas
├── .agents/
│   └── skills/
│       └── buntok-skill/
│           └── SKILL.md
├── .env
├── .env.example
├── .gitignore
├── .vscode/
│   └── settings.json
├── biome.json
├── tsconfig.json
├── Dockerfile?          # buntok init (optional)
├── .dockerignore?       # buntok init (optional)
└── package.json
Info
Each entity lives in its own module directory under src/modules/. The @Dependencies decorator enables automatic dependency injection - just register your controllers with app.registerController() and the container resolves the entire dependency tree.

Flags Reference#

FlagDescription
--repoGenerate only repository
--serviceGenerate only service
--controllerGenerate only controller
--schemaGenerate only Zod schema
--prismaUse Prisma ORM (default: auto-detect, falls back to plain code)
--drizzleUse Drizzle ORM
--typeormUse TypeORM
--dry-runPreview files without writing