14 KiB
🚀 Bun Web Server
Современный, быстрый и масштабируемый веб-сервер на основе Bun с поддержкой middleware, маршрутизации и SSR.
📋 Оглавление
🚀 Быстрый старт
Установка зависимостей
bun install
Разработка (с hot reload)
bun run dev
Сервер запустится на http://localhost:3000
Запуск в продакшене
bun run start
Сборка для продакшена
bun run build
📁 Структура проекта
src/
├── index.ts # Точка входа приложения
├── server.ts # Класс Server (основной сервер)
├── router.ts # Класс Router (маршрутизация)
├── middleware.ts # Система middleware
├── render.ts # SSR рендеринг для React компонентов
├── utils.ts # Вспомогательные функции
├── types.ts # TypeScript типы и интерфейсы
├── handlers/
│ ├── homeHandler.ts # Обработчик главной страницы
│ └── apiHandler.ts # Обработчики API endpoints
├── middleware/
│ ├── builtIn.ts # Встроенные middleware (logging, CORS, etc)
│ └── advanced.ts # Продвинутые middleware (caching, validation)
└── components/
├── Layout.tsx # React компонент Layout
├── UI.tsx # UI компоненты (Button, Endpoint)
└── pages/
└── HomePage.tsx # React компонент главной страницы
Описание ключевых файлов
| Файл | Описание |
|---|---|
server.ts |
Основной класс сервера. Управляет портом, маршрутами и middleware |
router.ts |
Маршрутизатор. Регистрирует и сопоставляет HTTP методы с обработчиками |
middleware.ts |
Система цепочки middleware для обработки запросов |
handlers/ |
Обработчики запросов для каждого маршрута |
middleware/builtIn.ts |
Готовые middleware: логирование, CORS, обработка ошибок, rate limiting |
middleware/advanced.ts |
Продвинутые middleware: кеширование, валидация, сжатие |
utils.ts |
Вспомогательные функции для формирования ответов |
🌐 API Endpoints
Главная страница
GET /
Возвращает HTML страницу с интерактивным интерфейсом для тестирования endpoints.
Ответ: HTML (200)
Приветствие
GET /api/hello
Простой JSON endpoint с приветствием.
Ответ:
{
"success": true,
"data": {
"message": "Hello from Bun API! 👋",
"timestamp": "2025-12-11T14:35:00.000Z"
}
}
Статус сервера
GET /api/status
Возвращает информацию о состоянии сервера.
Ответ:
{
"success": true,
"data": {
"status": "online",
"uptime": 1234.56,
"timestamp": "2025-12-11T14:35:00.000Z",
"memory": {
"rss": 52428800,
"heapTotal": 16777216,
"heapUsed": 8388608
}
}
}
Эхо данных
POST /api/echo
Возвращает отправленные данные обратно.
Body:
{
"message": "Hello from Bun!",
"timestamp": "2025-12-11T14:35:00.000Z"
}
Ответ:
{
"success": true,
"data": {
"echo": {
"message": "Hello from Bun!",
"timestamp": "2025-12-11T14:35:00.000Z"
},
"receivedAt": "2025-12-11T14:35:00.000Z"
}
}
🔗 Middleware
Middleware выполняются по цепочке и могут модифицировать запрос и ответ.
Встроенные Middleware
| Middleware | Описание |
|---|---|
errorHandlingMiddleware |
Глобальная обработка ошибок |
loggingMiddleware |
Логирование всех запросов с временем выполнения |
rateLimitMiddleware |
Ограничение количества запросов (100 в минуту) |
corsMiddleware |
Добавление CORS заголовков |
authMiddleware |
Проверка авторизации (Bearer token) |
Продвинутые Middleware
| Middleware | Описание |
|---|---|
cachingMiddleware |
Кеширование GET запросов с настраиваемым TTL |
compressionMiddleware |
Поддержка сжатия ответов |
validationMiddleware |
Валидация входных данных |
Пример использования middleware:
const middlewareChain = server.getMiddleware();
middlewareChain.use(errorHandlingMiddleware);
middlewareChain.use(loggingMiddleware);
middlewareChain.use(rateLimitMiddleware);
middlewareChain.use(corsMiddleware);
📚 Как расширять
1. Добавить новый API endpoint
Создайте файл обработчика в src/handlers/:
src/handlers/userHandler.ts
import { jsonResponse, errorResponse } from "../utils";
export async function getUserHandler(_req: Request, url: URL): Promise<Response> {
const userId = url.searchParams.get("id");
if (!userId) {
return errorResponse("Missing user ID", 400);
}
return jsonResponse({
id: userId,
name: "John Doe",
email: "john@example.com"
});
}
export async function createUserHandler(req: Request, _url: URL): Promise<Response> {
try {
const body = await req.text();
const data = body ? JSON.parse(body) : {};
if (!data.name || !data.email) {
return errorResponse("Name and email are required", 400);
}
return jsonResponse({
id: Math.random().toString(36).substr(2, 9),
...data
}, 201);
} catch (error) {
return errorResponse("Invalid JSON", 400);
}
}
Затем добавьте маршруты в src/index.ts:
import { getUserHandler, createUserHandler } from "./handlers/userHandler";
// ...
const router = server.getRouter();
// Добавляем новые маршруты
router.get("/api/users", getUserHandler);
router.post("/api/users", createUserHandler);
2. Создать новый Middleware
Создайте файл в src/middleware/:
src/middleware/custom.ts
import type { MiddlewareHandler } from "../middleware";
export const customHeaderMiddleware: MiddlewareHandler = async (req, url, next) => {
// Логика перед запросом
console.log(`Custom middleware: ${req.method} ${url.pathname}`);
const response = await next();
// Логика после запроса
const headers = new Headers(response.headers);
headers.set("X-Custom-Header", "My Value");
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
};
Добавьте в src/index.ts:
import { customHeaderMiddleware } from "./middleware/custom";
const middlewareChain = server.getMiddleware();
middlewareChain.use(customHeaderMiddleware);
3. Добавить React компонент страницы
Создайте компонент в src/components/pages/:
src/components/pages/AboutPage.tsx
import React from "react";
import { Layout } from "../Layout";
export function AboutPage() {
return (
<Layout title="About - Bun Server">
<div style={{ padding: "40px", background: "white", borderRadius: "12px" }}>
<h1>About Page</h1>
<p>Это страница About, созданная с помощью React компонентов!</p>
</div>
</Layout>
);
}
Добавьте обработчик в src/handlers/:
src/handlers/pageHandler.ts
export async function aboutHandler(_req: Request, _url: URL): Promise<Response> {
const html = `<!DOCTYPE html>
<html>
<head>
<title>About</title>
</head>
<body>
<h1>About Page</h1>
<p>Добро пожаловать на страницу About!</p>
</body>
</html>`;
return new Response(html, {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
}
И зарегистрируйте маршрут в src/index.ts:
router.get("/about", aboutHandler);
4. Добавить базу данных
Пример с простой in-memory базой:
src/db.ts
interface User {
id: string;
name: string;
email: string;
}
class Database {
private users: Map<string, User> = new Map();
createUser(name: string, email: string): User {
const id = Math.random().toString(36).substr(2, 9);
const user = { id, name, email };
this.users.set(id, user);
return user;
}
getUser(id: string): User | undefined {
return this.users.get(id);
}
getAllUsers(): User[] {
return Array.from(this.users.values());
}
}
export const db = new Database();
Используйте в handlers:
import { db } from "../db";
export async function createUserHandler(req: Request): Promise<Response> {
const body = await req.text();
const { name, email } = body ? JSON.parse(body) : {};
const user = db.createUser(name, email);
return jsonResponse(user, 201);
}
💡 Примеры
Пример 1: Добавить простой JSON endpoint
// src/handlers/dataHandler.ts
export async function getDataHandler(_req: Request, _url: URL): Promise<Response> {
return jsonResponse({
items: [
{ id: 1, name: "Item 1" },
{ id: 2, name: "Item 2" },
]
});
}
// src/index.ts
router.get("/api/data", getDataHandler);
Пример 2: Добавить параметры в URL
export async function getUserByIdHandler(_req: Request, url: URL): Promise<Response> {
const id = url.searchParams.get("id");
if (!id) {
return errorResponse("ID is required", 400);
}
return jsonResponse({ id, name: `User ${id}` });
}
// Использование: GET /api/users?id=123
Пример 3: POST с валидацией
export async function createItemHandler(req: Request, _url: URL): Promise<Response> {
try {
const body = await req.text();
const data = body ? JSON.parse(body) : {};
if (!data.name || data.name.trim() === "") {
return errorResponse("Name is required", 400);
}
return jsonResponse({
id: Math.random().toString(36).substr(2, 9),
name: data.name,
createdAt: new Date().toISOString()
}, 201);
} catch (error) {
return errorResponse("Invalid JSON", 400);
}
}
🔧 Конфигурация
Изменить порт сервера
В src/index.ts:
const server = new Server({ port: 8080 }); // Вместо 3001
Добавить новые HTTP методы
В src/router.ts уже реализованы методы:
get(path, handler)post(path, handler)put(path, handler)delete(path, handler)patch(path, handler)
Если нужен дополнительный метод:
export class Router {
// ... существующий код ...
public options(path: string, handler: RouteHandler): void {
this.routes.push({ method: "OPTIONS", path, handler });
}
}
📊 Логирование
По умолчанию включено логирование через loggingMiddleware:
📨 [2025-12-11T14:35:00.000Z] GET /api/hello
✅ GET /api/hello - 200 (1.09ms)
Для добавления кастомного логирования:
export const customLoggingMiddleware: MiddlewareHandler = async (req, url, next) => {
console.log(`[${new Date().toISOString()}] Custom log for ${url.pathname}`);
return next();
};
🚀 Развертывание
На Heroku
git push heroku main
На VPS
bun run build
bun run start
📝 Лучшие практики
- Структура папок: Держите обработчики и middleware в отдельных папках
- Типизация: Используйте TypeScript для всех критических частей
- Ошибки: Используйте
errorResponse()для консистентных ошибок - Middleware: Добавляйте middleware в правильном порядке (ошибки → логирование → другие)
- Validation: Всегда валидируйте входные данные
- Comments: Добавляйте комментарии к сложным логикам
📄 Лицензия
MIT