511 lines
14 KiB
Markdown
511 lines
14 KiB
Markdown
# 🚀 Bun Web Server
|
||
|
||
Современный, быстрый и масштабируемый веб-сервер на основе **Bun** с поддержкой middleware, маршрутизации и SSR.
|
||
|
||
## 📋 Оглавление
|
||
|
||
- [Быстрый старт](#быстрый-старт)
|
||
- [Структура проекта](#структура-проекта)
|
||
- [API Endpoints](#api-endpoints)
|
||
- [Middleware](#middleware)
|
||
- [Как расширять](#как-расширять)
|
||
- [Примеры](#примеры)
|
||
|
||
## 🚀 Быстрый старт
|
||
|
||
### Установка зависимостей
|
||
```bash
|
||
bun install
|
||
```
|
||
|
||
### Разработка (с hot reload)
|
||
```bash
|
||
bun run dev
|
||
```
|
||
|
||
Сервер запустится на `http://localhost:3000`
|
||
|
||
### Запуск в продакшене
|
||
```bash
|
||
bun run start
|
||
```
|
||
|
||
### Сборка для продакшена
|
||
```bash
|
||
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 с приветствием.
|
||
|
||
**Ответ:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"message": "Hello from Bun API! 👋",
|
||
"timestamp": "2025-12-11T14:35:00.000Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Статус сервера
|
||
```
|
||
GET /api/status
|
||
```
|
||
Возвращает информацию о состоянии сервера.
|
||
|
||
**Ответ:**
|
||
```json
|
||
{
|
||
"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:**
|
||
```json
|
||
{
|
||
"message": "Hello from Bun!",
|
||
"timestamp": "2025-12-11T14:35:00.000Z"
|
||
}
|
||
```
|
||
|
||
**Ответ:**
|
||
```json
|
||
{
|
||
"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:
|
||
```typescript
|
||
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**
|
||
```typescript
|
||
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`:
|
||
|
||
```typescript
|
||
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**
|
||
```typescript
|
||
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`:
|
||
|
||
```typescript
|
||
import { customHeaderMiddleware } from "./middleware/custom";
|
||
|
||
const middlewareChain = server.getMiddleware();
|
||
middlewareChain.use(customHeaderMiddleware);
|
||
```
|
||
|
||
### 3. Добавить React компонент страницы
|
||
|
||
Создайте компонент в `src/components/pages/`:
|
||
|
||
**src/components/pages/AboutPage.tsx**
|
||
```typescript
|
||
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**
|
||
```typescript
|
||
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`:
|
||
|
||
```typescript
|
||
router.get("/about", aboutHandler);
|
||
```
|
||
|
||
### 4. Добавить базу данных
|
||
|
||
Пример с простой in-memory базой:
|
||
|
||
**src/db.ts**
|
||
```typescript
|
||
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:
|
||
|
||
```typescript
|
||
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
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```typescript
|
||
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 с валидацией
|
||
|
||
```typescript
|
||
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`:
|
||
```typescript
|
||
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)`
|
||
|
||
Если нужен дополнительный метод:
|
||
|
||
```typescript
|
||
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)
|
||
```
|
||
|
||
Для добавления кастомного логирования:
|
||
|
||
```typescript
|
||
export const customLoggingMiddleware: MiddlewareHandler = async (req, url, next) => {
|
||
console.log(`[${new Date().toISOString()}] Custom log for ${url.pathname}`);
|
||
return next();
|
||
};
|
||
```
|
||
|
||
## 🚀 Развертывание
|
||
|
||
### На Heroku
|
||
```bash
|
||
git push heroku main
|
||
```
|
||
|
||
### На VPS
|
||
```bash
|
||
bun run build
|
||
bun run start
|
||
```
|
||
|
||
## 📝 Лучшие практики
|
||
|
||
1. **Структура папок**: Держите обработчики и middleware в отдельных папках
|
||
2. **Типизация**: Используйте TypeScript для всех критических частей
|
||
3. **Ошибки**: Используйте `errorResponse()` для консистентных ошибок
|
||
4. **Middleware**: Добавляйте middleware в правильном порядке (ошибки → логирование → другие)
|
||
5. **Validation**: Всегда валидируйте входные данные
|
||
6. **Comments**: Добавляйте комментарии к сложным логикам
|
||
|
||
## 📄 Лицензия
|
||
|
||
MIT
|