Initial commit: Bun web server with middleware, routing, and comprehensive documentation
This commit is contained in:
510
README.md
Normal file
510
README.md
Normal file
@ -0,0 +1,510 @@
|
||||
# 🚀 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
|
||||
Reference in New Issue
Block a user