477 lines
24 KiB
Markdown
477 lines
24 KiB
Markdown

|
||
|
||
# Разрабатываем первое 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`** и перейдите в неё:
|
||
```bash
|
||
mkdir backend
|
||
cd backend
|
||
```
|
||
2. **Инициализируйте проект Node.js**. Используйте флаг `-y`, чтобы принять значения по умолчанию, и `--type=module` для поддержки современного синтаксиса ES-модулей:
|
||
```bash
|
||
npm init -y --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 .
|
||
|
||
# Зафиксируйте изменения с комментарием
|
||
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"` — когда делим на ноль
|