A modern, type-safe HTTP server template built with Bun, TypeScript, Zod schema validation, and custom routing architecture.
- 🚀 Bun Runtime - Fast, modern JavaScript runtime with built-in bundler and test runner
- 📁 File-based Routing - Automatic route discovery and registration
- 🛡️ Type Safety - Full TypeScript support with strict type checking
- ✅ Schema Validation - Runtime request validation with Zod and automatic type inference
- 🔧 Middleware Support - Composable middleware system with route-level configuration
- 📊 Structured Logging - Production-ready logging with Pino
- 🐳 Docker Ready - Containerized deployment with multi-stage builds
- 🧪 Comprehensive Testing - Unit and integration tests with Bun's test runner
- 🔍 Code Quality - ESLint, Prettier, and TypeScript configuration
- ⚡ Hot Reload - Development server with automatic restart
- Bun >= 1.0.0
- Node.js >= 18 (for some development tools)
- Bun - JavaScript runtime and package manager
- TypeScript - Static type checking
- Zod - Schema validation and type inference
- Pino - Fast, low overhead logging
- @3xpo/events - Type-safe event emitter
# Clone the repository
git clone https://github.com/0neShot101/bun-http-template.git
cd bun-http-template
# Install dependencies
bun install
# Start development server
bun run devThe server will start on http://localhost:3000 with hot reload enabled.
Create a new route with schema validation:
// src/routes/example.ts
import RouteBuilder from '@structures/RouteBuilder';
export default new RouteBuilder()
.schema('post', (z) => ({
body: z.object({
message: z.string().min(1),
priority: z.enum(['low', 'medium', 'high']).default('medium')
})
}))
.on('post', async (req) => {
// req.body is fully typed!
const { message, priority } = req.body;
return Response.json({
received: message,
priority,
timestamp: new Date().toISOString()
});
});| Command | Description |
|---|---|
bun run dev |
Start development server with hot reload |
bun run build |
Build the application for production |
bun run start |
Start the production server |
bun run test |
Run all tests |
bun run test:watch |
Run tests in watch mode |
bun run test:coverage |
Run tests with coverage report |
bun run lint |
Run ESLint |
bun run lint:fix |
Run ESLint and fix issues |
bun run format |
Format code with Prettier |
bun run type-check |
Run TypeScript type checking |
bun run docker:build |
Build Docker image |
bun run docker:run |
Run Docker container |
src/
├── index.ts # Application entry point
├── http.ts # HTTP server setup and route loading
├── middleware/ # Middleware functions
│ └── logging.ts # Request logging middleware
├── routes/ # File-based routes
│ └── index.ts # Root route handler
├── structures/ # Core classes and builders
│ └── RouteBuilder.ts # Route definition and middleware builder
├── types/ # TypeScript type definitions
│ ├── routing.ts # Route and middleware types
│ ├── schema.ts # Schema validation types
│ └── server.ts # Server configuration types
└── utils/ # Utility functions
├── logger.ts # Logging configuration
└── walkDirectory.ts # File system utilities
tests/ # Test suite
├── unit/ # Unit tests
├── integration/ # Integration tests
├── helpers/ # Test utilities
├── fixtures/ # Test data and mocks
└── setup.ts # Test configuration
This template uses a file-based routing system where each file in the src/routes/ directory automatically becomes a route.
Create a new file in src/routes/ and export a RouteBuilder instance:
// src/routes/users.ts
import RouteBuilder from '@structures/RouteBuilder';
export default new RouteBuilder()
.on('get', async (req) => {
return Response.json({ users: [] });
})
.on('post', async (req) => {
const body = await req.json();
return Response.json({ created: body });
});Use Zod schemas to validate incoming requests with full type safety:
// src/routes/users.ts
import RouteBuilder from '@structures/RouteBuilder';
const createUserSchema = {
body: z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(18)
}),
query: z.object({
source: z.string().optional()
})
};
export default new RouteBuilder()
.schema('post', (z) => createUserSchema)
.on('post', async (req) => {
// req.body and req.query are now fully typed!
const { name, email, age } = req.body; // TypeScript knows these types
const { source } = req.query;
return Response.json({
created: { name, email, age },
source: source || 'direct'
});
});Validation Features:
- Body Validation - Validate JSON request bodies
- Query Parameters - Validate URL query parameters
- Headers - Validate request headers
- Automatic Errors - Returns 400 with detailed error messages
- Type Safety - Full TypeScript inference for validated data
Use underscores in filenames to create parameterized routes:
src/routes/users/_id.ts→/users/:idsrc/routes/posts/_slug/comments.ts→/posts/:slug/comments
Apply middleware per route or per HTTP method:
import RouteBuilder from '@structures/RouteBuilder';
import authMiddleware from '@middleware/auth';
import validation from '@middleware/validation';
export default new RouteBuilder({
// Apply to all methods
'*': [authMiddleware],
// Apply only to POST
'post': validation
})
.schema('post', (z) => ({
body: z.object({
title: z.string(),
content: z.string()
})
}))
.on('get', async (req) => {
// Handler code
})
.on('post', async (req) => {
// req.body is typed as { title: string, content: string }
const { title, content } = req.body;
return Response.json({ created: { title, content } });
});Middleware runs in this order:
- Route-level middleware (from constructor)
- Schema validation middleware (automatically added)
- Route handler
The template includes powerful runtime validation using Zod with full TypeScript integration.
Define validation schemas for any HTTP method using the schema() method:
import RouteBuilder from '@structures/RouteBuilder';
export default new RouteBuilder()
.schema('post', (z) => ({
// Validate request body
body: z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(18).max(120)
}),
// Validate query parameters
query: z.object({
includeProfile: z.boolean().optional(),
format: z.enum(['json', 'xml']).default('json')
}),
// Validate headers
headers: z.object({
'x-api-key': z.string().min(10)
})
}))
.on('post', async (req) => {
// All validated data is now available with full type safety
const { name, email, age } = req.body;
const { includeProfile, format } = req.query;
const apiKey = req.headers['x-api-key'];
return Response.json({ success: true });
});- Automatic Type Inference - Request objects are automatically typed based on schemas
- Runtime Validation - Invalid requests return 400 with detailed error messages
- Multiple Parts - Validate body, query parameters, and headers independently
- Zod Integration - Use any Zod validation features (transforms, refinements, etc.)
- Error Handling - Graceful error responses with validation details
// Complex validation with transforms and custom validations
.schema('post', (z) => ({
body: z.object({
email: z.string().email().transform(val => val.toLowerCase()),
tags: z.array(z.string()).min(1).max(5),
metadata: z.record(z.string(), z.any()).optional(),
publishedAt: z.string().datetime().transform(val => new Date(val))
}).refine(data => {
// Custom validation logic
return data.tags.every(tag => tag.length > 0);
}, { message: "Tags cannot be empty" })
}))The project includes a comprehensive test suite using Bun's built-in test runner.
# Run all tests
bun test
# Run specific test file
bun test tests/unit/RouteBuilder.test.ts
# Run tests with coverage
bun test --coverage
# Watch mode for development
bun test --watch- Unit Tests - Test individual components in isolation
- Integration Tests - Test the complete HTTP server functionality
- Test Helpers - Utilities for creating requests, assertions, and mocks
- Fixtures - Sample data and configurations for testing
import { describe, expect, it } from 'bun:test';
describe('Component', () => {
it('should do something', () => {
expect(true).toBe(true);
});
});Create a .env file in the root directory:
NODE_ENV=development
PORT=3000
LOG_LEVEL=info# Build the image
bun run docker:build
# Run the container
bun run docker:runThis project enforces code quality through:
- TypeScript - Static type checking
- ESLint - Code linting with TypeScript rules
- Prettier - Code formatting
- Bun Test - Unit and integration testing
Run quality checks before committing:
bun run lint:fix
bun run format
bun run type-check
bun run testGET /health- Basic health checkGET /health/ready- Readiness probe
GET /- Hello world endpoint
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Run the test suite
- Submit a pull request
MIT License - see LICENSE file for details.