Клиент и сервер

Клиент-серверная модель — архитектура, где клиент (браузер, приложение) отправляет запросы, а сервер обрабатывает их и возвращает ответы.

Зачем нужно

Понимание клиент-серверной архитектуры — фундамент веб-разработки. Это определяет, где выполняется код, где хранятся данные, как строится безопасность и как масштабируется приложение.

Где используется

  • Все веб-сайты и веб-приложения
  • Мобильные приложения с бэкендом
  • Микросервисная архитектура
  • Облачные сервисы

Предпосылки

Клиент (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: 'Пароль слишком короткий' });
  }
  // ... создание пользователя
});

Частые ошибки

  1. Хранить секреты на клиенте. API-ключи, пароли — только на сервере. Код клиента виден всем через DevTools
  2. Доверять данным от клиента. Валидация на клиенте — для UX, на сервере — для безопасности
  3. Путать SSR и CSR. Server-Side Rendering (SSR) — HTML генерируется на сервере; Client-Side Rendering (CSR) — HTML генерируется в браузере
  4. Не обрабатывать ошибки сети. Сервер может быть недоступен, запрос может таймаутнуть

Практика

  1. Создайте простой Express-сервер с одним GET-эндпоинтом
  2. Сделайте fetch-запрос к jsonplaceholder.typicode.com и отобразите данные на странице
  3. Откройте DevTools → Network, выполните действие на любом сайте и изучите запросы

Связанные темы

Ресурсы

  • Express.js — официальная документация
  • MDN — Fetch API
  • REST API Tutorial (restfulapi.net)
  • The Full Stack Developer (Chris Northwood)