Skip to content
griban.dev
← назад_до_блогу
nodejs

Розробка сучасних REST API на Node.js 23 та Express 5.0

Ruslan Griban7 хв читання
поділитися:

Вступ

Ландшафт бекенд-розробки зазнав тектонічних зрушень за останні два роки. Протягом майже десятиліття Express 4.x залишався галузевим стандартом, змушуючи розробників покладатися на сторонні бібліотеки для базових завдань, таких як обробка асинхронних помилок або виконання HTTP-запитів. Однак із появою Express 5.0 та Node.js 22/23 LTS екосистема перетворилася на більш оптимізоване, продуктивне та безпечне середовище.

Створення REST API у 2025 році — це вже не просто маршрутизація; це безпека типів, дизайн на основі контрактів (contract-first design) та використання нативних можливостей середовища виконання, які раніше були недоступні. Цей посібник досліджує, як створювати професійні REST API, використовуючи найновіші функції Node.js та Express, проходячи шлях від базового налаштування до просунутих архітектурних патернів.

Сучасний фундамент: Node.js 22 та Express 5.0

Перш ніж писати перший рядок коду, важливо зрозуміти особливості сучасного середовища. Node.js 22 представив функції, які значно зменшують потребу у великій кількості залежностей.

Перехід на ES-модулі (ESM)

CommonJS (require) фактично став застарілим форматом у сучасній екосистемі Node.js. Встановивши "type": "module" у вашому package.json, ви отримуєте доступ до top-level await та кращого статичного аналізу для інструментів збірки.

{
  "name": "modern-express-api",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "express": "^5.0.0",
    "zod": "^3.23.0"
  }
}

Express 5.0: нативна підтримка асинхронності

Найважливішим оновленням в Express 5.0 є нативна обробка Promises. В Express 4 необроблене відхилення (rejection) в async маршруті призводило до зависання запиту або завершення процесу з помилкою, якщо воно не було обгорнуте в блок try-catch або допоміжну функцію на кшталт express-async-handler. Express 5.0 автоматично перехоплює ці помилки та передає їх у ваш глобальний middleware для обробки помилок.

Нативні Fetch та WebSockets

У Node.js 22 API fetch став стабільним. Це означає, що ваше REST API тепер може взаємодіяти з іншими мікросервісами без додаткових витрат на axios або node-fetch. Крім того, включення node:ws забезпечує нативний шлях для додавання можливостей реального часу до ваших RESTful-ендпоінтів.

Архітектурна досконалість: трирівневий патерн

Поширеною помилкою в розробці на Express є синдром «товстого контролера» (Fat Controller), коли вся бізнес-логіка, запити до бази даних та валідація знаходяться всередині обробника маршруту. Для створення масштабованого API ми впроваджуємо модульну трирівневу архітектуру.

1. Шар контролерів (Controller Layer)

Єдиний обов'язок контролера — обробка HTTP-інтерфейсу. Він розбирає запит, викликає відповідний сервіс і повертає відформатовану відповідь. Він ніколи не повинен взаємодіяти з базою даних безпосередньо.

2. Шар сервісів (Service Layer)

Це серце вашого додатка. Шар сервісів містить основну бізнес-логіку. Якщо вам потрібно розрахувати знижку, надіслати електронний лист або перевірити права користувача, це відбувається саме тут. Цей шар не залежить від фреймворку, що полегшує його тестування або перенесення на інший фреймворк у майбутньому.

3. Шар доступу до даних (DAL)

DAL взаємодіє з вашою базою даних. Використовуючи ORM, як-от Prisma, або ODM, як-от Mongoose, цей шар абстрагує запити. Ізолюючи доступ до даних, ви можете перейти з PostgreSQL на MongoDB з мінімальним впливом на вашу бізнес-логіку.

Діаграма, що показує потік HTTP-запиту через три рівні: шар контролерів, шар сервісів та шар доступу до даних, що веде до бази даних.

Реалізація CRUD з типізацією та валідацією

У 2025 році довіра до вхідних даних від клієнта є критичним ризиком безпеки. Ми використовуємо Zod для валідації під час виконання та TypeScript для безпеки на етапі компіляції.

Визначення схеми

Zod дозволяє визначити схему, яка валідує req.body і одночасно генерує тип TypeScript.

import { z } from 'zod';
 
export const CreateUserSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
  role: z.enum(['USER', 'ADMIN']).default('USER'),
});
 
type CreateUserDto = z.infer<typeof CreateUserSchema>;

Обробник маршрутів Express 5.0

Зверніть увагу, наскільки чистим стає маршрут, коли ми використовуємо нативну обробку промісів Express 5.0 та централізований middleware для валідації.

import express from 'express';
import { userService } from '../services/user.service.js';
import { validate } from '../middleware/validate.js';
import { CreateUserSchema } from '../schemas/user.schema.js';
 
const router = express.Router();
 
router.post('/', validate(CreateUserSchema), async (req, res) => {
  // Логіка виконується лише у разі успішної валідації
  const newUser = await userService.createUser(req.body);
  res.status(201).json(newUser);
});
 
export default router;

Централізована обробка помилок

Замість розкиданих викликів res.status(500), ми використовуємо глобальний обробник помилок. Express 5.0 робить його потужнішим, автоматично захоплюючи помилки, викинуті з асинхронних функцій.

// middleware/errorHandler.js
export const errorHandler = (err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  const message = err.message || 'Internal Server Error';
 
  console.error(`[Error] ${req.method} ${req.url}:`, err);
 
  res.status(statusCode).json({
    status: 'error',
    code: err.code || 'INTERNAL_ERROR',
    message,
    ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
  });
};

Просунуті техніки: безпека та продуктивність

API, готове до продакшену, потребує більше, ніж просто операції CRUD. Воно вимагає надійного підходу до безпеки та розуміння event loop у Node.js.

Модель дозволів Node.js

Одним із найцікавіших доповнень у Node.js 22 є експериментальна модель дозволів. Тепер ви можете обмежити доступ вашого API до середовища. Наприклад: node --experimental-permission --allow-fs-read=/tmp/ --allow-net=api.stripe.com server.js Це гарантує, що навіть якщо залежність буде скомпрометована, зловмисник не зможе прочитати ваш файл /etc/passwd або надіслати дані на шкідливий домен.

Обробка важких обчислень

Node.js є однопотоковим. Якщо вашому API потрібно обробляти великі зображення або генерувати складні PDF-файли, це заблокує event loop, перешкоджаючи обробці інших запитів.

  • Рішення: Використовуйте Worker Threads для завдань, обмежених потужністю процесора (CPU-bound), або передавайте їх фоновому воркеру, наприклад BullMQ, використовуючи Redis. Це дозволить вашому REST API залишатися чуйним.

Заголовки безпеки та санітизація

Завжди використовуйте Helmet.js для встановлення безпечних HTTP-заголовків. Він захищає від поширених вразливостей, таких як Cross-Site Scripting (XSS) та клікджекінг, за замовчуванням.

import helmet from 'helmet';
const app = express();
app.use(helmet()); // Встановлює 15+ заголовків безпеки

Концептуальна ілюстрація захисного щита, що охороняє логотип Node.js, з іконками, що представляють HTTP-заголовки, валідацію даних та модель дозволів Node.js.

Реальний сценарій: реалізація складного фільтрування

Сучасні API часто мають підтримувати складні запити. Замість того, щоб писати кастомну логіку для кожного маршруту, ми можемо створити універсальну утиліту «Query Features».

class APIFeatures {
  constructor(query, queryString) {
    this.query = query; // Запит Prisma або Mongoose
    this.queryString = queryString; // req.query
  }
 
  filter() {
    const queryObj = { ...this.queryString };
    const excludedFields = ['page', 'sort', 'limit', 'fields'];
    excludedFields.forEach(el => delete queryObj[el]);
 
    // Просунуте фільтрування (наприклад, price[gte]=500)
    let queryStr = JSON.stringify(queryObj);
    queryStr = queryStr.replace(/\b(gte|gt|lte|lt)\b/g, match => `$${match}`);
 
    this.query = this.query.find(JSON.parse(queryStr));
    return this;
  }
 
  paginate() {
    const page = this.queryString.page * 1 || 1;
    const limit = this.queryString.limit * 1 || 100;
    const skip = (page - 1) * limit;
    this.query = this.query.skip(skip).limit(limit);
    return this;
  }
}

Це дозволяє обробляти запити на кшталт GET /api/products?price[gte]=100&page=2&limit=20 всього кількома рядками коду у вашому шарі сервісів.

Необхідні інструменти для 2025 року

Інструмент Призначення Чому він необхідний
Prisma ORM Забезпечує повну безпеку типів для вашої схеми бази даних.
PM2 Керування процесами Обробляє кластеризацію для використання всіх ядер процесора та забезпечує перезапуск без простоїв.
Swagger UI Документація Автоматично генерує інтерактивну документацію API зі специфікації OpenAPI 3.1.
Winston Логування Структуроване JSON-логування необхідне для сучасних інструментів моніторингу, таких як Datadog.

Часті запитання

Як створити RESTful API на Node.js та Express з нуля?

Щоб створити API з нуля, ініціалізуйте проект Node.js за допомогою npm init, встановіть Express і створіть вхідний файл. Потім визначте маршрути за допомогою app.get(), app.post() тощо, і використовуйте middleware для парсингу JSON та обробки помилок.

Яка різниця між Node.js та Express.js у розробці API?

Node.js — це середовище виконання JavaScript, яке дозволяє запускати код на сервері, тоді як Express.js — це мінімалістичний вебфреймворк, побудований поверх Node.js. Node.js надає основні мережеві можливості, тоді як Express спрощує маршрутизацію, інтеграцію middleware та обробку запитів.

Як обробляти автентифікацію та авторизацію в Node.js REST API?

Автентифікація зазвичай обробляється за допомогою JSON Web Tokens (JWT) або сесійних кук через middleware, наприклад Passport.js або власну логіку. Авторизація реалізується шляхом перевірки ролі або дозволів користувача (вилучених з токена) на відповідність вимогам конкретного маршруту.

Які найкращі практики структурування проекту Node.js Express?

Найкращою практикою є використання багаторівневої архітектури, розділення коду на папки для контролерів (Controllers), сервісів (Services), моделей (DAL) та middleware. Такий розподіл обов'язків гарантує, що бізнес-логіка ізольована від транспортного рівня HTTP, що полегшує тестування та підтримку коду.

Як підключити Node.js REST API до бази даних MongoDB?

Ви підключаєтеся до MongoDB за допомогою офіційного драйвера MongoDB або ODM, наприклад Mongoose. Ви встановлюєте рядок підключення у змінних оточення і використовуєте патерн Singleton, щоб переконатися, що з'єднання з базою даних спільно використовується в шарі сервісів вашого додатка.

Висновок

Розробка REST API на Node.js та Express перетворилася на зрілу та складну дисципліну. Реліз Express 5.0 став поворотним моментом, де асинхронні патерни нарешті стали повноцінними елементами фреймворку, значно зменшуючи кількість шаблонного та схильного до помилок коду. Поєднуючи це з нативними функціями Node.js 22 — такими як модель дозволів та fetch API — і строгою трирівневою архітектурою, розробники можуть створювати системи, які є не лише продуктивними, але й стійкими та безпечними.

Рухаючись вперед, надавайте пріоритет підходу contract-first з OpenAPI та валідації під час виконання за допомогою Zod. Ці інструменти гарантують, що в міру зростання вашого API воно залишатиметься надійним контрактом для вашого фронтенду та мобільних клієнтів. «Шлях Express» у 2025 році полягає в тому, щоб робити більше з меншими зусиллями: менше залежностей, більше нативних функцій та чистіший, безпечний щодо типів код.

rocket_launch

Ready to start your project?

Let's discuss how I can help bring your ideas to life with modern web technologies and AI.

Get in Touch