Files
lesson_2/README.md
2025-12-12 19:33:15 +03:00

480 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
![backdrop](img.jpg)
# Разрабатываем первое Web API на Node.js
Для секции "Веб-разработка: пример Fullstack"
## Введение
Урок предназачен для аудитории, имеющей базовые навыки работы с HTML/CSS, Git и SSH. Цель: освоить основы разработки Web API с использованием Node.js и фреймворка Express за ограниченное время (3 часа).
Рекомендую [очень хороший ресурс с теорией и практикой по Node.js](https://metanit.com/web/nodejs/1.1.php)
---
## 📘 **Урок 1: Создание репозитория и организации структуры проекта**
### **Цель урока**: Создать GitHub-репозиторий, организовать структуру проекта и научиться работать с ветками в Git.
### **Краткая теория**:
- **GitHub** — платформа для хостинга и совместной разработки IT-проектов с использованием системы контроля версий Git.
- **Репозиторий** — хранилище кода и истории его изменений.
- **Ветка (branch)** — изолированная копия проекта для разработки новой функциональности без влияния на основной код (часто называется `main` или `master`).
### **Практические шаги**:
1. **Создайте репозиторий на GitHub**:
* Авторизуйтесь на [GitHub](https://github.com).
* Нажмите `+` в правом верхнем углу и выберите `New repository`.
* В поле `Repository name` укажите название, отражающее тему вашего API (например, `my-first-api`).
* Оставьте репозиторий публичным (`Public`).
* **Поставьте галочку** `Add a README.md`. Это создаст начальный файл описания проекта.
* Нажмите `Create repository`.
2. **Клонируйте репозиторий и создайте учебную ветку**:
```bash
# Склонируйте репозиторий на компьютер
git clone git@github.com:<ваш-логин>/<название-репозитория>.git
cd <название-репозитория>
# Создайте и переключитесь на новую ветку 'study'
git checkout -b study
# Если в команде двое, каждый создает свою ветку (например, study-ivanov)
# git checkout -b study-фамилия
```
### **Что должно получиться**:
* Создан удаленный репозиторий на GitHub с файлом `README.md`.
* На вашем компьютере есть локальная копия проекта.
* Вы находитесь в новой ветке (`study` или `study-фамилия`), а не в основной (`main`).
---
## 📦 **Урок 2: Инициализация проекта Node.js**
- [Metanit: Файл package.json и конфигурация проекта](https://metanit.com/web/nodejs/2.7.php)
### **Цель урока**: Создать структуру папок для backend и инициализировать проект Node.js.
### **Краткая теория**:
- **Node.js** — это среда выполнения JavaScript вне браузера, которая позволяет создавать серверные приложения.
- **package.json** — это файл манифеста проекта Node.js, который содержит его описание, версию, список зависимостей и скриптов.
### **Практические шаги**:
1. **Создайте папку `backend`** внутри проекта и перейдите в неё. Можно сделать это в Git Bash:
```bash
mkdir backend
cd backend
```
2. **Инициализируйте проект Node.js**. Используйте флаг `-y`, чтобы принять значения по умолчанию.
```bash
npm init -y
# Нужно для поддержи современного синтаксиса ES Module
npm pkg set type="module";
```
### **Проверка результата**:
Убедитесь, что в папке `backend` появился файл `package.json`. В его первой строке должно быть указано `"type": "module"`.
---
## 🚀 **Урок 3: Установка фреймворка Express**
- [Metanit: Начало работы с Express](https://metanit.com/web/nodejs/4.1.php)
### **Цель урока**: Установить библиотеку Express — популярный фреймворк для создания веб-приложений и API на Node.js.
### **Краткая теория**:
- **Express** — это минималистичный и гибкий фреймворк, который значительно упрощает создание серверных приложений и маршрутов.
- **Зависимости (dependencies)** — это сторонние библиотеки (пакеты), которые использует ваш проект. Они управляются с помощью `npm` (Node Package Manager).
### **Практические шаги**:
В папке `backend` выполните команду установки:
```bash
npm install express
```
### **Что должно получиться**:
* В файле `package.json` в раздел `"dependencies"` добавится `"express"` с номером версии.
* В проекте появится папка `node_modules`, где хранятся все установленные библиотеки.
---
## 🗑️ **Урок 4: Работа с `.gitignore` и первый коммит**
[Atlassian: Файл .gitignore — игнорирование файлов в Git](https://www.atlassian.com/ru/git/tutorials/saving-changes/gitignore)
### **Цель урока**: Научиться игнорировать ненужные для репозитория файлы и зафиксировать изменения.
### **Краткая теория**:
- **`node_modules`** — это папка со всеми зависимостями проекта. Её **нельзя** добавлять в Git, так как она очень большая и её можно восстановить командой `npm install`.
- **`.gitignore`** — специальный файл, в котором перечисляются шаблоны файлов и папок, которые Git должен игнорировать.
### **Практические шаги**:
1. **Создайте файл `.gitignore`** в основной папке вашего проекта (не внутри `backend`).
2. **Добавьте в него шаблоны**. Вы можете создать базовое содержимое на сайте [gitignore.io](https://www.toptal.com/developers/gitignore). Введите `Node, Windows, VisualStudioCode` и скопируйте сгенерированный текст в ваш файл.
3. **Убедитесь, что в `.gitignore` есть строки**:
```
node_modules/
.env
```
4. **Выполните коммит**:
```bash
# Вернитесь в корень проекта (если вы в папке backend)
cd ..
# Добавьте все новые файлы в staging area
git add .
# Убедитесь, что папка node_modules не добавилась и зафиксируйте изменения с комментарием
git commit -m "feat: инициализирован проект Node.js с Express, добавлен .gitignore"
```
---
## 📄 **Урок 5: Создание базовой структуры сервера**
- [MDN: Введение в Express/Node](https://developer.mozilla.org/ru/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction)
- [Habr: Лучшие практики Node.js — советы по структуре проектов](https://habr.com/ru/articles/454476/)
### **Цель урока**: Создать основные файлы приложения и понять их роль.
### **Краткая теория**:
Серверное приложение на Express обычно разделяют на несколько файлов для лучшей организации:
- **`server.js`** — точка входа. Запускает сервер, подключает основные настройки.
- **`app.js`** — ядро приложения. Содержит настройки Express и все маршруты (routes).
### **Практические шаги**:
1. В папке `backend` создайте папку `src`.
2. Внутри `src` создайте два файла: `app.js` и `server.js`.
3. **В файл `app.js`** пока добавьте только создание экземпляра приложения:
```javascript
// backend/src/app.js
import express from 'express';
const app = express();
export default app; // Экспортируем app для использования в server.js
```
4. **В файл `server.js`** добавьте код запуска сервера:
```javascript
// backend/src/server.js
import app from './app.js';
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Сервер запущен и слушает порт ${PORT}`);
});
```
### **Что должно получиться**:
Создана модульная структура приложения. Пока при запуске сервер будет работать, но на любой запрос возвращать ошибку `404`, так как маршруты не определены.
---
## ▶️ **Урок 6: Первый запуск сервера**
### **Цель урока**: Запустить сервер и убедиться, что он работает, возвращая ожидаемую ошибку.
### **Практические шаги**:
1. Из папки `backend` выполните команду:
```bash
node src/server.js
```
2. В консоли должно появиться сообщение: `Сервер запущен и слушает порт 3000`.
3. Откройте браузер и перейдите по адресу `http://localhost:3000`.
4. **Вы должны увидеть стандартную страницу ошибки `Cannot GET /`**. Это ожидаемо, так как мы не создали обработчик для корневого маршрута (`/`).
5. Остановите сервер, нажав `Ctrl + C` в терминале.
6. **Выполните коммит**:
```bash
git add .
git commit -m "feat: создана базовая структура сервера, успешный запуск"
git push origin study # Или study-фамилия
```
---
## 🖐️ **Урок 7: Создание первого маршрута (Hello World)**
### **Цель урока**: Создать простейший обработчик GET-запроса и получить ответ от сервера.
### **Краткая теория**:
- **Маршрут (route)** — это правило, которое определяет, как сервер будет реагировать на клиентский запрос к определенному URL (конечной точке) и HTTP-методу (GET, POST и т.д.).
- **Обработчик (handler)** — функция, которая принимает запрос (`req`) и формирует ответ (`res`).
### **Практические шаги**:
1. Откройте файл `backend/src/app.js`.
2. **Добавьте обработчик для корневого маршрута (`/`) перед строкой `export default app;`**:
```javascript
// Обработчик GET-запроса на корневой URL '/'
app.get('/', (req, res) => {
// Устанавливаем заголовок ответа
res.setHeader('Content-Type', 'text/plain');
// Отправляем текстовый ответ
res.send('Hello World from my API!');
});
```
3. Запустите сервер снова (`node src/server.js`) и обновите страницу `http://localhost:3000` в браузере.
4. **Вы должны увидеть надпись `Hello World from my API!`**.
5. **Выполните коммит**.
---
## 🔢 **Урок 8: Работа с параметрами пути (Path Parameters)**
- [Expressjs: Маршрутизация в Express](https://www.expressjs.com.cn/ru/guide/routing.html)
- [Nodejsdev: Как построить REST API с помощью JS, Node.js и Express.js](https://nodejsdev.ru/guides/rest-api-design/)
### **Цель урока**: Научиться создавать динамические маршруты и извлекать данные из URL.
### **Краткая теория**:
- **Статические маршруты** — это фиксированные адреса, например, `/about` или `/contacts`. Они всегда одинаковы.
- **Динамические маршруты** — это адреса, которые могут меняться. Например, страница профиля с разными именами пользователей: `/user/anna`, `/user/max`.
- **Параметры пути (Path Parameters)** — специальные части URL, которые меняются. В Express они обозначаются двоеточием (`:`), например: `/user/:username`.
- Когда кто-то заходит на `/user/anna`, Express видит `:username` в маршруте и понимает: "вместо `:username` подставлено `anna`". Это значение сохраняется в `req.params`.
### **Примеры разных параметров**:
1. **Один параметр**:
```javascript
// Когда заходят на /hello/Максим
app.get('/hello/:name', (req, res) => {
const name = req.params.name; // = "Максим"
res.send(`Привет, ${name}!`);
});
```
2. **Несколько параметров**:
```javascript
// Когда заходят на /book/сказки/5
app.get('/book/:genre/:page', (req, res) => {
const genre = req.params.genre; // = "сказки"
const page = req.params.page; // = "5"
res.send(`Жанр: ${genre}, Страница: ${page}`);
});
```
3. **Параметр в середине пути**:
```javascript
// Когда заходят на /category/электроника/product/123
app.get('/category/:categoryName/product/:productId', (req, res) => {
const category = req.params.categoryName; // = "электроника"
const productId = req.params.productId; // = "123"
res.send(`Категория: ${category}, Товар: ${productId}`);
});
```
### **Практические шаги**:
1. В файле `app.js` добавьте маршрут:
```javascript
// Пример запроса: GET http://localhost:3000/double/7
app.get('/double/:number', (req, res) => {
const input = req.params.number; // Получаем параметр из URL
// Проверяем, является ли параметр числом
if (!isNaN(input)) {
const number = parseFloat(input);
const result = number * 2;
res.send(`Если удвоить ${number}, получится ${result}`);
} else {
res.status(400).send('Пожалуйста, укажите число. Например: /double/10');
}
});
```
2. Протестируйте:
- `http://localhost:3000/double/7` → работает
- `http://localhost:3000/double/abc` → ошибка
---
## ➕➖✖️➗ **Урок 9: Практическое задание — Текстовый API-калькулятор**
### **Цель задания**: Создать простой калькулятор, который принимает числа в URL и возвращает текстовый результат.
### **Задание**:
Создайте 4 маршрута для основных математических операций. Формат ответа — обычный текст.
**Маршруты и примеры**:
1. **Сложение**: `GET /api/calc/sum/:a/:b`
- Пример: `GET /api/calc/sum/5/3`
- Ответ: `5 + 3 = 8`
2. **Вычитание**: `GET /api/calc/subtract/:a/:b`
- Пример: `GET /api/calc/subtract/10/4`
- Ответ: `10 - 4 = 6`
3. **Умножение**: `GET /api/calc/multiply/:a/:b`
- Пример: `GET /api/calc/multiply/7/2`
- Ответ: `7 * 2 = 14`
4. **Деление**: `GET /api/calc/divide/:a/:b`
- Пример: `GET /api/calc/divide/15/3`
- Ответ: `15 / 3 = 5`
### **Как это сделать**:
1. Для каждого маршрута получите параметры из `req.params.a` и `req.params.b`
2. Преобразуйте их в числа: `const num1 = parseFloat(req.params.a)`
3. Выполните операцию
4. Верните ответ в формате: ``res.send(`${num1} + ${num2} = ${result}`)``
### **Важно**:
- Пока не нужно проверять, корректные ли данные прислал пользователь
- Используйте шаблонные строки (обратные кавычки) для формирования ответа
---
## ⚠️ **Урок 10: Базовые проверки в калькуляторе**
### **Цель урока**: Добавить минимальную проверку входных данных.
### **Задание**:
Доработайте калькулятор из урока 9, добавив две простые проверки:
1. **Проверка, что оба параметра — числа**
2. **Для деления — проверка, что второй параметр не ноль**
### **Как это сделать**:
**Шаг 1: Проверка на число**
В начале каждого обработчика добавьте:
```javascript
const a = parseFloat(req.params.a);
const b = parseFloat(req.params.b);
// Проверяем, являются ли оба параметра числами
if (isNaN(a) || isNaN(b)) {
return res.status(400).send('Ошибка: оба параметра должны быть числами');
}
```
**Шаг 2: Проверка деления на ноль**
Только в обработчике деления добавьте после проверки на числа:
```javascript
// Проверка деления на ноль
if (b === 0) {
return res.status(400).send('Ошибка: на ноль делить нельзя');
}
```
### **Что должно происходить**:
- `/api/calc/sum/5/3` → `5 + 3 = 8` (работает)
- `/api/calc/sum/5/abc` → `Ошибка: оба параметра должны быть числами` (ошибка 400)
- `/api/calc/divide/10/0` → `Ошибка: на ноль делить нельзя` (ошибка 400)
---
## 📊 **Урок 11: Введение в формат JSON**
- [MDN: Работа с JSON](https://developer.mozilla.org/ru/docs/Learn_web_development/Core/Scripting/JSON)
- [Microsoft Learn: Создание API, получающего JSON](https://learn.microsoft.com/ru-ru/shows/beginners-series-to-nodejs/how-to-create-an-api-that-receive-json-with-nodejs-and-express-20-of-26)
### **Цель урока**: Понять, что такое JSON и научиться отправлять данные в этом формате.
### **Краткая теория**:
- **JSON (JavaScript Object Notation)** — популярный формат для обмена данными;
- у JSON есть строгий набор правил:
- Все ключи в двойных кавычках
- Значения: строки (в кавычках), числа, true/false, null, массивы [], объекты {}
- **Пример JSON**:
```json
{
"имя": "Иван",
"возраст": 16,
"хобби": ["программирование", "шахматы"],
"студент": true
}
```
### **Практическое задание**:
1. Создайте маршрут `GET /api/user/:id`
2. Если `:id` — число, верните JSON с информацией о пользователе:
```javascript
app.get('/api/user/:id', (req, res) => {
const id = req.params.id;
if (!isNaN(id)) {
// Формируем вручную объект пользователя.
// Пока что просто притворимся, что мы загрузили его из базы данных.
const userData = {
userId: Number(id),
name: "Алексей Петров",
email: "alexey@example.com",
registered: true
};
// Отправляем как JSON
res.json(userData);
} else {
// Если id не число - ошибка в JSON формате
res.status(400).json({
error: "ID должен быть числом",
example: "/api/user/123"
});
}
});
```
3. Протестируйте в браузере:
- `http://localhost:3000/api/user/123` → увидите красиво оформленный JSON
- `http://localhost:3000/api/user/abc` → JSON с ошибкой
### Обратите внимание
В примере мы возвращаем фальшивые данные. С настоящими динамическими данными (списки/объекты БД) мы поработаем позже.
---
## 🔄 **Урок 12: Калькулятор с JSON-ответами**
### **Цель урока**: Переделать текстовый калькулятор для возврата JSON.
### **Задание**:
Измените все 4 операции калькулятора так, чтобы они возвращали данные в JSON-формате.
### **Формат успешного ответа**:
```json
{
"success": true,
"operation": "sum",
"arguments": {
"a": 5,
"b": 3
},
"result": 8
}
```
### **Формат ответа с ошибкой**:
```json
{
"success": false,
"error": {
"code": "DIVISION_BY_ZERO",
"message": "Делитель не может быть равен нулю"
}
}
```
### **Как изменить код**:
1. **Заменяем `res.send()` на `res.json()`**
2. **Для успешного ответа** формируем объект:
```javascript
const response = {
success: true,
operation: "sum", // или "subtract", "multiply", "divide"
arguments: {
a: a,
b: b
},
result: a + b // здесь ваша операция
};
res.json(response);
```
3. **Для ошибок**:
```javascript
res.status(400).json({
success: false,
error: {
code: "INVALID_NUMBERS", // или "DIVISION_BY_ZERO"
message: "Оба параметра должны быть числами"
}
});
```
### **Примеры кодов ошибок**:
- `"INVALID_NUMBERS"` — когда параметры не числа
- `"DIVISION_BY_ZERO"` — когда делим на ноль