Files
bun-server-test/README.md

14 KiB
Raw Blame History

🚀 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

📝 Лучшие практики

  1. Структура папок: Держите обработчики и middleware в отдельных папках
  2. Типизация: Используйте TypeScript для всех критических частей
  3. Ошибки: Используйте errorResponse() для консистентных ошибок
  4. Middleware: Добавляйте middleware в правильном порядке (ошибки → логирование → другие)
  5. Validation: Всегда валидируйте входные данные
  6. Comments: Добавляйте комментарии к сложным логикам

📄 Лицензия

MIT