Клиент и сервер
Клиент-серверная модель — архитектура, где клиент (браузер, приложение) отправляет запросы, а сервер обрабатывает их и возвращает ответы.
Зачем нужно
Понимание клиент-серверной архитектуры — фундамент веб-разработки. Это определяет, где выполняется код, где хранятся данные, как строится безопасность и как масштабируется приложение.
Где используется
- Все веб-сайты и веб-приложения
- Мобильные приложения с бэкендом
- Микросервисная архитектура
- Облачные сервисы
Предпосылки
Клиент (Frontend)
Клиент — это то, что видит и с чем взаимодействует пользователь.
Технологии фронтенда
HTML → Структура (скелет страницы)
CSS → Оформление (цвета, шрифты, расположение)
JS → Поведение (интерактивность, логика)
// Пример: клиент отправляет данные формы на сервер
const form = document.querySelector('#login-form');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const formData = {
email: document.querySelector('#email').value,
password: document.querySelector('#password').value,
};
// Клиент отправляет запрос серверу
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
const result = await response.json();
if (result.success) {
window.location.href = '/dashboard';
} else {
alert(result.error);
}
});
Что делает клиент
- Рендерит интерфейс (HTML/CSS)
- Обрабатывает пользовательский ввод
- Отправляет запросы серверу
- Отображает данные от сервера
- Валидация на стороне клиента (UX, не безопасность!)
Сервер (Backend)
Сервер — программа, которая слушает входящие запросы и обрабатывает их.
Технологии бэкенда
| Язык/платформа | Фреймворк |
|---|---|
| JavaScript (Node.js) | Express, Fastify, Nest.js |
| Python | Django, Flask, FastAPI |
| Java | Spring Boot |
| Go | net/http, Gin |
| PHP | Laravel |
// Пример: простой сервер на Node.js + Express
const express = require('express');
const app = express;
app.use(express.json());
// Хранилище (в реальности — база данных)
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' },
];
// GET — получить всех пользователей
app.get('/api/users', (req, res) => {
res.json(users);
});
// GET — получить одного пользователя
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === Number(req.params.id));
if (!user) {
return res.status(404).json({ error: 'Пользователь не найден' });
}
res.json(user);
});
// POST — создать пользователя
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
const newUser = { id: users.length + 1, name, email };
users.push(newUser);
res.status(201).json(newUser);
});
app.listen(3000, () => {
console.log('Сервер запущен на http://localhost:3000');
});
Что делает сервер
- Обрабатывает бизнес-логику
- Работает с базой данных (CRUD)
- Аутентификация и авторизация
- Валидация данных (безопасность!)
- Отправка ответов клиенту
API (Application Programming Interface)
API — контракт между клиентом и сервером: какие запросы принимаются и что возвращается.
REST API
GET /api/users → список пользователей
GET /api/users/42 → пользователь с id=42
POST /api/users → создать пользователя (данные в body)
PUT /api/users/42 → обновить пользователя 42
DELETE /api/users/42 → удалить пользователя 42
// Полный CRUD на клиенте
const API = 'http://localhost:3000/api';
// Create
async function createUser(data) {
const res = await fetch(`${API}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
return res.json();
}
// Read
async function getUsers() {
const res = await fetch(`${API}/users`);
return res.json();
}
// Update
async function updateUser(id, data) {
const res = await fetch(`${API}/users/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
return res.json();
}
// Delete
async function deleteUser(id) {
const res = await fetch(`${API}/users/${id}`, {
method: 'DELETE',
});
return res.json();
}
Базы данных
Сервер хранит данные в базах данных (не в переменных!).
| Тип | Примеры | Данные | Когда |
|---|---|---|---|
| SQL (реляционные) | PostgreSQL, MySQL | Таблицы, строгая схема | Структурированные данные, связи |
| NoSQL (документные) | MongoDB, Firebase | JSON-документы, гибкая схема | Прототипы, разнородные данные |
| Key-Value | Redis, Memcached | Ключ → значение | Кэш, сессии |
// Пример с базой данных (псевдокод)
// SQL (PostgreSQL через pg)
const result = await db.query(
'SELECT * FROM users WHERE id = $1',
[42]
);
// NoSQL (MongoDB через mongoose)
const user = await User.findById(42);
// Key-Value (Redis)
await redis.set('session:abc123', JSON.stringify(userData));
const session = JSON.parse(await redis.get('session:abc123'));
Full-Stack: клиент + сервер + БД
┌─────────────┐ HTTP ┌─────────────┐ SQL/Query ┌──────────┐
│ Клиент │ ◄──────────► │ Сервер │ ◄──────────────► │ БД │
│ (Браузер) │ JSON │ (Node.js) │ │ (Postgres)│
│ │ │ │ │ │
│ HTML/CSS │ │ Express │ │ users │
│ JavaScript │ │ API routes │ │ posts │
│ React/Vue │ │ Auth logic │ │ orders │
└─────────────┘ └─────────────┘ └──────────┘
Поток данных: пользователь регистрируется
1. Пользователь заполняет форму (Клиент)
2. JS валидирует email формат (Клиент — UX)
3. POST /api/register { email, password } (Клиент → Сервер)
4. Сервер валидирует данные (Сервер — безопасность!)
5. Сервер хэширует пароль (bcrypt)
6. INSERT INTO users (...) (Сервер → БД)
7. БД возвращает id нового пользователя (БД → Сервер)
8. Сервер создаёт JWT-токен
9. 201 Created { token: "..." } (Сервер → Клиент)
10. Клиент сохраняет токен (localStorage / cookie)
11. Клиент перенаправляет на dashboard
Разница: где выполняется код
| Аспект | Клиент | Сервер |
|---|---|---|
| Где работает | Браузер пользователя | Удалённый сервер |
| Язык | HTML, CSS, JavaScript | Любой (JS, Python, Go...) |
| Доступ к данным | Только через API | Прямой доступ к БД |
| Безопасность | Код виден пользователю | Код скрыт |
| Ресурсы | Устройство пользователя | Серверное железо |
Золотое правило: никогда не доверяйте данным от клиента. Всегда валидируйте на сервере.
// ПЛОХО: валидация только на клиенте
// Пользователь может обойти через DevTools
if (password.length >= 8) {
fetch('/api/register', { body: JSON.stringify({ password }) });
}
// ХОРОШО: валидация на клиенте (UX) + на сервере (безопасность)
// Клиент:
if (password.length < 8) {
showError('Пароль должен быть не менее 8 символов');
return;
}
fetch('/api/register', { body: JSON.stringify({ password }) });
// Сервер:
app.post('/api/register', (req, res) => {
if (!req.body.password || req.body.password.length < 8) {
return res.status(400).json({ error: 'Пароль слишком короткий' });
}
// ... создание пользователя
});
Частые ошибки
- Хранить секреты на клиенте. API-ключи, пароли — только на сервере. Код клиента виден всем через DevTools
- Доверять данным от клиента. Валидация на клиенте — для UX, на сервере — для безопасности
- Путать SSR и CSR. Server-Side Rendering (SSR) — HTML генерируется на сервере; Client-Side Rendering (CSR) — HTML генерируется в браузере
- Не обрабатывать ошибки сети. Сервер может быть недоступен, запрос может таймаутнуть
Практика
- Создайте простой Express-сервер с одним GET-эндпоинтом
- Сделайте fetch-запрос к
jsonplaceholder.typicode.comи отобразите данные на странице - Откройте DevTools → Network, выполните действие на любом сайте и изучите запросы
Связанные темы
Ресурсы
- Express.js — официальная документация
- MDN — Fetch API
- REST API Tutorial (restfulapi.net)
- The Full Stack Developer (Chris Northwood)