Node.js для начинающих: создаем серверное приложение за 5 шагов

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Начинающие и опытные веб-разработчики, желающие изучить Node.js
  • Разработчики, переходящие с фронтенда на бэкенд с использованием JavaScript
  • Специалисты, интересующиеся созданием серверных приложений и REST API

    Node.js — это настоящий прорыв для разработчиков, мечтающих о единой экосистеме JavaScript и на фронтенде, и на бэкенде. Освоив эту технологию, вы входите в элитный клуб универсальных разработчиков, способных создавать полноценные приложения от А до Я. Я разрабатываю на Node.js уже более 7 лет, и за это время убедился: это не просто модный инструмент, а фундаментальное изменение парадигмы серверной разработки. В этом руководстве я проведу вас от нуля до полноценного рабочего сервера через пять чётких шагов. 🚀

Мечтаете создавать серверные приложения, но не знаете, с чего начать? Обучение веб-разработке от Skypro — ваш билет в мир профессиональной разработки с Node.js. За 9 месяцев вы пройдете путь от основ до создания сложных серверных приложений с REST API, работой с базами данных и микросервисной архитектурой. Наши студенты получают реальные проекты в портфолио и уже после 5-го месяца обучения устраиваются на стажировки в IT-компании.

Что такое Node.js и почему он идеален для серверной разработки

Node.js — это среда выполнения JavaScript на стороне сервера, построенная на V8, высокопроизводительном движке JavaScript от Google Chrome. Появившись в 2009 году, Node.js совершил революцию в серверной разработке, позволив JavaScript выйти за пределы браузера.

Ключевое преимущество Node.js — его неблокирующая, асинхронная архитектура, позволяющая обрабатывать тысячи одновременных соединений без создания дополнительных потоков. Это делает Node.js исключительно эффективным для приложений с интенсивным вводом-выводом, таких как чаты, потоковые сервисы и API.

Александр Петров, технический директор Когда мы начинали разработку новой версии нашей платформы для обработки финансовых транзакций, перед нами стоял выбор: использовать проверенный временем Java-стек или рискнуть с Node.js. Многие в команде сомневались, справится ли "JavaScript на сервере" с нашими требованиями — 5000+ транзакций в секунду с минимальной задержкой. Мы решили провести эксперимент и разработали прототипы на обеих технологиях. Результаты поразили даже скептиков: Node.js не только справился с нагрузкой, но и показал на 30% меньшую задержку при обработке запросов. А скорость разработки? Мы построили MVP за 2 недели вместо планируемых 6. С тех пор вся наша инфраструктура микросервисов работает на Node.js, а время вывода новых функций сократилось втрое.

Давайте рассмотрим преимущества Node.js по сравнению с традиционными серверными технологиями:

Параметр Node.js PHP Java
Модель выполнения Асинхронная, событийная Синхронная Многопоточная
Потребление памяти Низкое Среднее Высокое
Скорость разработки Высокая Высокая Средняя
Производительность при I/O-операциях Превосходная Средняя Хорошая
Экосистема пакетов npm (1.3+ млн пакетов) Composer (~300k пакетов) Maven (~380k пакетов)

Основные преимущества Node.js для серверной разработки:

  • Единый язык — JavaScript используется как на фронтенде, так и на бэкенде, что упрощает взаимодействие команд и ускоряет разработку.
  • npm — крупнейший в мире репозиторий программных пакетов с более чем 1.3 миллионами библиотек.
  • Высокая производительность — благодаря движку V8 и асинхронной модели Node.js отлично справляется с многопользовательскими приложениями реального времени.
  • Микросервисная архитектура — Node.js идеален для создания легковесных, независимых микросервисов.
  • Активное сообщество — постоянно растущая экосистема разработчиков и инструментов.
Пошаговый план для смены профессии

Установка Node.js и подготовка среды разработки

Прежде чем погрузиться в разработку, необходимо правильно настроить рабочую среду. Процесс установки Node.js прост и не занимает много времени, независимо от вашей операционной системы. 🛠️

Способы установки Node.js:

  1. Официальный установщик — самый простой метод, доступный на nodejs.org. Рекомендую выбирать LTS (Long Term Support) версию для продакшн-разработки.
  2. Менеджеры версий — позволяют устанавливать несколько версий Node.js и легко переключаться между ними:
    • nvm (Node Version Manager) — для Linux и macOS
    • nvm-windows — для Windows
    • n — упрощенная альтернатива nvm
  3. Пакетные менеджеры ОС — apt для Ubuntu, Homebrew для macOS, Chocolatey для Windows.

После установки проверьте версии Node.js и npm (менеджер пакетов, устанавливается автоматически с Node.js):

node -v
npm -v

Теперь настроим базовую структуру проекта:

  1. Создайте новую директорию для вашего проекта и перейдите в неё:
mkdir my-node-server
cd my-node-server

  1. Инициализируйте проект с npm:
npm init -y

Этот команда создаст файл package.json с базовой конфигурацией проекта. Флаг -y принимает значения по умолчанию, но впоследствии вы можете отредактировать этот файл.

Рекомендую также установить несколько полезных инструментов для разработки:

npm install nodemon --save-dev

Nodemon автоматически перезапускает сервер при изменении файлов, что ускоряет процесс разработки.

Обновите раздел "scripts" в вашем package.json:

json
Скопировать код
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
}

Теперь для запуска сервера в режиме разработки достаточно выполнить:

npm run dev

Популярные редакторы кода для разработки на Node.js:

Редактор Преимущества Рекомендуемые расширения
Visual Studio Code Бесплатный, отличная интеграция с Node.js, встроенный терминал ESLint, Prettier, Node.js Extension Pack
WebStorm Платный, мощная среда для JavaScript, встроенный отладчик Встроенная поддержка Node.js
Sublime Text Быстрый, легковесный, настраиваемый Babel, JavaScript Completions, Node.js
Atom Бесплатный, настраиваемый, модульный atom-ternjs, language-babel, node-debugger

Создание первого HTTP-сервера на Node.js с примерами кода

Теперь, когда у нас настроена среда разработки, давайте создадим базовый HTTP-сервер с использованием встроенного модуля http. Это фундаментальный шаг в понимании работы Node.js. 🌐

Создайте файл index.js в корне проекта и добавьте следующий код:

JS
Скопировать код
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Привет, мир! Мой первый Node.js сервер работает!\n');
});

server.listen(port, hostname, () => {
console.log(`Сервер запущен по адресу http://${hostname}:${port}/`);
});

Разберём этот код по шагам:

  1. Мы импортируем встроенный модуль http с помощью функции require()
  2. Указываем хост (127.0.0.1 или localhost) и порт (3000) для нашего сервера
  3. Создаём сервер с помощью метода createServer(), который принимает функцию-обработчик запросов
  4. Обработчик запросов получает два объекта: request (req) и response (res)
  5. Устанавливаем статус ответа 200 (успешный), указываем тип контента и отправляем текстовое сообщение
  6. Запускаем сервер на указанном порту и выводим сообщение в консоль

Запустите сервер:

node index.js

Или с помощью nodemon, если вы его установили:

npm run dev

Теперь откройте браузер и перейдите по адресу http://127.0.0.1:3000/. Вы должны увидеть сообщение "Привет, мир! Мой первый Node.js сервер работает!".

Давайте усложним наш сервер, добавив обработку разных URL и методов HTTP:

JS
Скопировать код
const http = require('http');
const url = require('url');

const hostname = '127.0.0.1';
const port = 3000;

const users = [
{ id: 1, name: 'Иван', email: 'ivan@example.com' },
{ id: 2, name: 'Мария', email: 'maria@example.com' },
];

const server = http.createServer((req, res) => {
// Парсинг URL и получение пути
const parsedUrl = url.parse(req.url, true);
const path = parsedUrl.pathname;
const trimmedPath = path.replace(/^\/+|\/+$/g, '');

// Получение метода запроса
const method = req.method.toLowerCase();

// Базовая маршрутизация
if (trimmedPath === '' && method === 'get') {
// Главная страница
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Добро пожаловать на наш сервер!\n');
} else if (trimmedPath === 'users' && method === 'get') {
// Получение списка пользователей
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(JSON.stringify(users));
} else if (trimmedPath.startsWith('users/') && method === 'get') {
// Получение конкретного пользователя по ID
const userId = parseInt(trimmedPath.split('/')[1]);
const user = users.find(u => u.id === userId);

if (user) {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(JSON.stringify(user));
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(JSON.stringify({ error: 'Пользователь не найден' }));
}
} else {
// Маршрут не найден
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('404 – Страница не найдена\n');
}
});

server.listen(port, hostname, () => {
console.log(`Сервер запущен по адресу http://${hostname}:${port}/`);
});

Теперь наш сервер поддерживает несколько маршрутов:

  • GET / – возвращает приветственное сообщение
  • GET /users – возвращает список всех пользователей в формате JSON
  • GET /users/1 – возвращает информацию о пользователе с ID 1

Попробуйте обратиться к этим маршрутам через браузер или с помощью инструмента вроде Postman, чтобы увидеть, как работает маршрутизация.

Михаил Соколов, ведущий разработчик Помню свой первый опыт с Node.js в 2015 году. Я был фронтенд-разработчиком, работавшим исключительно с jQuery и Bootstrap, когда мой руководитель поставил задачу реализовать небольшой сервис для сбора аналитики с сайтов наших клиентов. В то время у меня был выбор: либо изучить PHP (что казалось логичным для серверной части), либо рискнуть с Node.js, о котором я только начал слышать. Решил дать Node.js шанс — ведь я уже знал JavaScript. Первые 2-3 дня я испытывал настоящий когнитивный диссонанс. Асинхронные функции и колбэки вместо привычного последовательного кода сбивали с толку. Но момент прозрения наступил, когда я создал свой первый HTTP-сервер — точно такой же, как в примере выше — и увидел, как легко можно обрабатывать запросы и возвращать данные. Спустя неделю я смог запустить полноценный API, способный обрабатывать до 1000 запросов в секунду с минимальной нагрузкой на сервер. Для сравнения: по оценкам нашего DevOps-инженера, аналогичное PHP-решение потребовало бы в 2-3 раза больше серверных ресурсов. С тех пор я не оглядывался назад — Node.js стал моим основным инструментом для серверной разработки.

Разработка серверного приложения с фреймворком Express.js

Хотя встроенный модуль http позволяет создавать серверы, для сложных приложений он становится неудобным. Express.js — это минималистичный и гибкий фреймворк, существенно упрощающий разработку веб-приложений на Node.js. 🛠️

Установим Express:

npm install express --save

Теперь создадим новый файл app.js и реализуем тот же функционал, что и ранее, но с использованием Express:

JS
Скопировать код
const express = require('express');
const app = express();
const port = 3000;

// Middleware для парсинга JSON
app.use(express.json());

// Данные
const users = [
{ id: 1, name: 'Иван', email: 'ivan@example.com' },
{ id: 2, name: 'Мария', email: 'maria@example.com' },
];

// Маршруты
app.get('/', (req, res) => {
res.send('Добро пожаловать на наш сервер!');
});

app.get('/users', (req, res) => {
res.json(users);
});

app.get('/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const user = users.find(u => u.id === userId);

if (user) {
res.json(user);
} else {
res.status(404).json({ error: 'Пользователь не найден' });
}
});

// Создание нового пользователя
app.post('/users', (req, res) => {
const { name, email } = req.body;

// Проверка обязательных полей
if (!name || !email) {
return res.status(400).json({ error: 'Требуются поля name и email' });
}

// Создание нового пользователя
const newUser = {
id: users.length + 1,
name,
email
};

users.push(newUser);
res.status(201).json(newUser);
});

// Обработка несуществующих маршрутов
app.use((req, res) => {
res.status(404).send('404 – Страница не найдена');
});

// Запуск сервера
app.listen(port, () => {
console.log(`Сервер Express запущен на порту ${port}`);
});

Обратите внимание, как значительно сократился и стал понятнее код. Express предоставляет удобный API для маршрутизации, middleware и обработки запросов.

Основные концепции Express.js:

  • Маршрутизация – Express позволяет определять обработчики для различных HTTP методов и URL путей.
  • Middleware – функции, имеющие доступ к объектам запроса, ответа и следующей функции в цикле запрос-ответ.
  • Шаблонизаторы – Express может использовать различные шаблонизаторы (Pug, EJS, Handlebars) для генерации HTML.
  • Статические файлы – Express упрощает обслуживание статических файлов (CSS, JavaScript, изображения).

Давайте улучшим наше приложение, организовав его более структурировано:

  1. Создайте папку routes и файл userRoutes.js внутри неё:
JS
Скопировать код
// routes/userRoutes.js
const express = require('express');
const router = express.Router();

// Данные (в реальном приложении это была бы база данных)
const users = [
{ id: 1, name: 'Иван', email: 'ivan@example.com' },
{ id: 2, name: 'Мария', email: 'maria@example.com' },
];

// Получение всех пользователей
router.get('/', (req, res) => {
res.json(users);
});

// Получение пользователя по ID
router.get('/:id', (req, res) => {
const userId = parseInt(req.params.id);
const user = users.find(u => u.id === userId);

if (user) {
res.json(user);
} else {
res.status(404).json({ error: 'Пользователь не найден' });
}
});

// Создание нового пользователя
router.post('/', (req, res) => {
const { name, email } = req.body;

if (!name || !email) {
return res.status(400).json({ error: 'Требуются поля name и email' });
}

const newUser = {
id: users.length + 1,
name,
email
};

users.push(newUser);
res.status(201).json(newUser);
});

// Обновление пользователя
router.put('/:id', (req, res) => {
const userId = parseInt(req.params.id);
const userIndex = users.findIndex(u => u.id === userId);

if (userIndex === -1) {
return res.status(404).json({ error: 'Пользователь не найден' });
}

const { name, email } = req.body;

if (!name && !email) {
return res.status(400).json({ error: 'Требуется хотя бы одно поле для обновления' });
}

users[userIndex] = {
...users[userIndex],
...(name && { name }),
...(email && { email })
};

res.json(users[userIndex]);
});

// Удаление пользователя
router.delete('/:id', (req, res) => {
const userId = parseInt(req.params.id);
const userIndex = users.findIndex(u => u.id === userId);

if (userIndex === -1) {
return res.status(404).json({ error: 'Пользователь не найден' });
}

const deletedUser = users[userIndex];
users.splice(userIndex, 1);

res.json(deletedUser);
});

module.exports = router;

  1. Обновите app.js, используя модульный подход:
JS
Скопировать код
const express = require('express');
const userRoutes = require('./routes/userRoutes');

const app = express();
const port = 3000;

// Middleware
app.use(express.json());

// Логгирование запросов
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} – ${req.method} ${req.path}`);
next();
});

// Маршруты
app.get('/', (req, res) => {
res.send('Добро пожаловать на наш API сервер!');
});

// Подключение маршрутов пользователей
app.use('/users', userRoutes);

// Обработка ошибок
app.use((req, res) => {
res.status(404).json({ error: 'Маршрут не найден' });
});

// Запуск сервера
app.listen(port, () => {
console.log(`Сервер Express запущен на порту ${port}`);
});

Теперь мы создали полноценный REST API с CRUD операциями для ресурса "пользователи". Сравните эти два подхода:

Аспект Нативный http модуль Express.js
Маршрутизация Ручная реализация через условные операторы Встроенная система маршрутизации (app.get, app.post и т.д.)
Middleware Требуется ручная реализация Встроенная поддержка через app.use()
Обработка ошибок Ручное управление статусами и ответами Централизованные обработчики ошибок
Модульность Сложно реализовать Поддержка роутеров и модульной структуры
Статические файлы Требует ручной реализации express.static()

Асинхронное программирование и работа с базами данных

Асинхронное программирование — одна из самых мощных особенностей Node.js, позволяющая обрабатывать множество операций одновременно без блокировки основного потока выполнения. Это особенно важно при работе с базами данных и внешними API. 📊

Исторически в Node.js использовались колбэки для асинхронных операций, но современный код использует промисы и async/await для большей читаемости и лучшего управления ошибками.

Давайте рассмотрим, как подключиться к базе данных MongoDB с использованием популярной библиотеки mongoose:

  1. Установите необходимые пакеты:
npm install mongoose dotenv

  1. Создайте файл .env в корне проекта для хранения конфиденциальных данных:
MONGODB_URI=mongodb://localhost:27017/myapp
PORT=3000

  1. Создайте папку models и файл User.js:
JS
Скопировать код
// models/User.js
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true
},
createdAt: {
type: Date,
default: Date.now
}
});

module.exports = mongoose.model('User', userSchema);

  1. Обновите userRoutes.js для работы с моделью MongoDB:
JS
Скопировать код
// routes/userRoutes.js
const express = require('express');
const User = require('../models/User');
const router = express.Router();

// Получение всех пользователей
router.get('/', async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (err) {
res.status(500).json({ error: err.message });
}
});

// Получение пользователя по ID
router.get('/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({ error: 'Пользователь не найден' });
}
res.json(user);
} catch (err) {
res.status(500).json({ error: err.message });
}
});

// Создание нового пользователя
router.post('/', async (req, res) => {
try {
const { name, email } = req.body;

// Проверяем, существует ли пользователь с таким email
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ error: 'Пользователь с таким email уже существует' });
}

// Создаем нового пользователя
const newUser = new User({ name, email });
await newUser.save();

res.status(201).json(newUser);
} catch (err) {
res.status(400).json({ error: err.message });
}
});

// Обновление пользователя
router.put('/:id', async (req, res) => {
try {
const { name, email } = req.body;

const updatedUser = await User.findByIdAndUpdate(
req.params.id, 
{ name, email },
{ new: true, runValidators: true }
);

if (!updatedUser) {
return res.status(404).json({ error: 'Пользователь не найден' });
}

res.json(updatedUser);
} catch (err) {
res.status(400).json({ error: err.message });
}
});

// Удаление пользователя
router.delete('/:id', async (req, res) => {
try {
const deletedUser = await User.findByIdAndDelete(req.params.id);

if (!deletedUser) {
return res.status(404).json({ error: 'Пользователь не найден' });
}

res.json({ message: 'Пользователь успешно удален' });
} catch (err) {
res.status(500).json({ error: err.message });
}
});

module.exports = router;

  1. Обновите app.js для подключения к MongoDB:
JS
Скопировать код
// app.js
const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const userRoutes = require('./routes/userRoutes');

// Загрузка переменных окружения
dotenv.config();

const app = express();
const port = process.env.PORT || 3000;

// Middleware
app.use(express.json());

// Подключение к MongoDB
mongoose.connect(process.env.MONGODB_URI)
.then(() => console.log('Успешное подключение к MongoDB'))
.catch(err => {
console.error('Ошибка подключения к MongoDB', err);
process.exit(1);
});

// Маршруты
app.get('/', (req, res) => {
res.send('Добро пожаловать на наш API сервер!');
});

app.use('/users', userRoutes);

// Обработка ошибок
app.use((req, res) => {
res.status(404).json({ error: 'Маршрут не найден' });
});

// Запуск сервера
app.listen(port, () => {
console.log(`Сервер запущен на порту ${port}`);
});

Вы можете заметить, что мы используем async/await для обработки асинхронных операций с базой данных. Это делает код более понятным и упрощает обработку ошибок.

Рассмотрим основные подходы к асинхронному программированию в Node.js:

  • Колбэки – традиционный подход с передачей функций обратного вызова:
JS
Скопировать код
fs.readFile('file.txt', (err, data) => {
if (err) throw err;
console.log(data);
});

  • Промисы – более современный подход, позволяющий цепочку вызовов:
JS
Скопировать код
fs.promises.readFile('file.txt')
.then(data => console.log(data))
.catch(err => console.error(err));

  • Async/Await – синтаксический сахар над промисами, делает код похожим на синхронный:
JS
Скопировать код
async function readFile() {
try {
const data = await fs.promises.readFile('file.txt');
console.log(data);
} catch (err) {
console.error(err);
}
}

При работе с асинхронным кодом важно помнить о правильной обработке ошибок. В Node.js необработанные исключения могут привести к аварийному завершению всего приложения.

Для работы с реляционными базами данных, такими как MySQL или PostgreSQL, можно использовать Sequelize или TypeORM – ORM-библиотеки, предоставляющие удобный API для взаимодействия с базами данных.

Для повышения производительности приложения можно использовать кэширование с помощью Redis:

JS
Скопировать код
// Пример кэширования с Redis
const express = require('express');
const redis = require('redis');
const util = require('util');
const User = require('./models/User');

const app = express();
const client = redis.createClient();

// Промисификация методов Redis
const getAsync = util.promisify(client.get).bind(client);
const setAsync = util.promisify(client.set).bind(client);

// Middleware для кэширования
const cacheMiddleware = async (req, res, next) => {
const userId = req.params.id;

try {
// Проверяем, есть ли данные в кэше
const cachedUser = await getAsync(`user:${userId}`);

if (cachedUser) {
// Если есть, возвращаем их
res.json(JSON.parse(cachedUser));
} else {
// Если нет, передаем управление следующему middleware
next();
}
} catch (err) {
next();
}
};

// Маршрут с использованием кэширования
app.get('/users/:id', cacheMiddleware, async (req, res) => {
try {
const user = await User.findById(req.params.id);

if (!user) {
return res.status(404).json({ error: 'Пользователь не найден' });
}

// Сохраняем результат в кэше на 60 секунд
await setAsync(`user:${req.params.id}`, JSON.stringify(user), 'EX', 60);

res.json(user);
} catch (err) {
res.status(500).json({ error: err.message });
}
});

Node.js открывает новый мир серверной разработки для JavaScript-программистов. От простых HTTP-серверов до сложных API с базами данных — Node.js позволяет создать практически любое веб-приложение. Ключевое преимущество — не нужно переключаться между языками программирования. Тот же JavaScript, который вы используете на фронтенде, теперь работает и на бэкенде. Многие компании, от стартапов до гигантов вроде Netflix и Walmart, уже перешли на Node.js, получив впечатляющие результаты. Если у вас есть опыт в JavaScript, Node.js — ваш логичный следующий шаг. Сообщество активно развивает технологию, а экосистема npm содержит готовые решения практически для любой задачи. Начните с простого сервера, постепенно добавляйте новые возможности, и вскоре вы освоите эту мощную платформу.

Загрузка...