Bun.js Native
Built specifically for Bun.js runtime. Fast startup, native TypeScript support, and modern JavaScript features.
One framework. One runtime. Everything you need for production backend services.

A complete, batteries-included TypeScript backend framework for Bun.js.
OneBun brings NestJS-style architecture — modules, dependency injection, decorators — to the Bun.js runtime, with a full ecosystem of built-in packages: WebSocket (+ Socket.IO + typed client), microservices with single-image deployment, database integration (Drizzle ORM), message queues (Redis, NATS, JetStream), caching, Prometheus metrics, OpenTelemetry tracing, ArkType validation with auto-generated OpenAPI documentation, and typed inter-service HTTP clients.
One framework. One runtime. Everything you need for production backend services.
| Aspect | Details |
|---|---|
| Runtime | Bun.js (not Node.js compatible) |
| Language | TypeScript (strict mode, no any) |
| Architecture | NestJS-inspired with Effect.ts for side effects |
| Philosophy | One default solution per problem |
| API Style | Promise-based for client code, Effect.js internally |
my-app/
├── src/
│ ├── index.ts # Entry point - creates OneBunApplication
│ ├── app.module.ts # Root module
│ ├── config.ts # Environment schema
│ ├── users/
│ │ ├── users.module.ts # Feature module
│ │ ├── users.controller.ts
│ │ ├── users.service.ts
│ │ └── entities/
│ │ └── user.entity.ts
│ └── posts/
│ └── ...
├── package.json
└── tsconfig.json| Decorator | Purpose | Example |
|---|---|---|
@Module() | Define module with imports, controllers, providers | @Module({ controllers: [UserController] }) |
@Controller(path) | Define HTTP controller | @Controller('/users') |
@Service() | Mark class as injectable service | @Service() |
@Get(), @Post(), etc. | Define HTTP endpoints | @Get('/:id') |
@Body(), @Param(), @Query() | Parameter injection | @Param('id') id: string |
@Inject() | Explicit DI (edge cases only) | @Inject(AbstractService) |
@WebSocketGateway() | Define WebSocket gateway | @WebSocketGateway({ path: '/ws' }) |
@OnMessage(), @OnConnect(), etc. | WebSocket event handlers | @OnMessage('chat:*') |
| Class | Purpose | Inherit When |
|---|---|---|
BaseController | HTTP controller functionality | Creating any controller |
BaseService | Service with logger and config | Creating any service |
BaseWebSocketGateway | WebSocket gateway with rooms and emit | Creating WebSocket gateway |
import {
BaseController,
BaseService,
Controller,
Env,
Get,
Module,
OneBunApplication,
Post,
Body,
Service,
} from '@onebun/core';
// ============================================================================
// 1. Environment Schema (src/config.ts)
// ============================================================================
const envSchema = {
server: {
port: Env.number({ default: 3000 }),
host: Env.string({ default: '0.0.0.0' }),
},
};
// ============================================================================
// 2. Service Layer (src/counter.service.ts)
// ============================================================================
@Service()
class CounterService extends BaseService {
private value = 0;
getValue(): number {
this.logger.debug('Getting counter value', { value: this.value });
return this.value;
}
increment(amount = 1): number {
this.value += amount;
return this.value;
}
}
// ============================================================================
// 3. Controller Layer (src/counter.controller.ts)
// ============================================================================
@Controller('/api/counter')
class CounterController extends BaseController {
constructor(private counterService: CounterService) {
super();
}
@Get('/')
async getValue(): Promise<Response> {
const value = this.counterService.getValue();
return this.success({ value });
}
@Post('/increment')
async increment(@Body() body?: { amount?: number }): Promise<Response> {
const newValue = this.counterService.increment(body?.amount);
return this.success({ value: newValue });
}
}
// ============================================================================
// 4. Module Definition (src/app.module.ts)
// ============================================================================
@Module({
controllers: [CounterController],
providers: [CounterService],
})
class AppModule {}
// ============================================================================
// 5. Application Entry Point (src/index.ts)
// ============================================================================
const app = new OneBunApplication(AppModule, {
port: 3000,
envSchema,
metrics: { enabled: true },
tracing: { enabled: true },
});
app.start();Always use standardized responses:
// Success
return this.success(data); // { success: true, result: data }
// Error
return this.error('message', 400); // { success: false, code: 400, message: 'message' }Dependencies are auto-detected from constructor:
@Controller('/users')
export class UserController extends BaseController {
// CounterService is automatically injected
constructor(private counterService: CounterService) {
super();
}
}import { type } from 'arktype';
const createUserSchema = type({
name: 'string',
email: 'string.email',
age: 'number > 0',
});
@Post('/')
async createUser(@Body(createUserSchema) user: typeof createUserSchema.infer) {
// user is validated and typed
}# Development
bun run dev # Start with watch mode
bun run dev:once # Start once
# Testing
bun test # Run all tests
# Type checking
bunx tsc --noEmit # Check TypeScript errors
# Linting
bun lint # Check lint errors{
"dependencies": {
"@onebun/core": "^0.2.6",
"@onebun/logger": "^0.2.0",
"@onebun/envs": "^0.2.0",
"@onebun/requests": "^0.2.0",
"@onebun/metrics": "^0.2.0",
"@onebun/trace": "^0.2.0",
"@onebun/cache": "^0.2.2",
"@onebun/drizzle": "^0.2.1",
"effect": "^3.x",
"arktype": "^2.x"
}
}