# Node.js співбесіда: маршрут підготовки і приклади запитань

> Що реально перевіряють на backend-співбесіді з Node.js: event loop, middleware, робота з базою, типові запитання з відповідями та live coding приклад REST-endpoint з валідацією.

- Автор: Юра Скиба (https://cookiesoftware.io)
- Опубліковано: 2026-09-24
- Категорія: Backend і System Design
- Canonical: https://cookiesoftware.io/blog/nodejs-backend-interview

---

**Коротка відповідь.** Node.js-співбесіда майже завжди складається з трьох шарів: як працює сам Node (event loop, асинхронність), як ти будуєш HTTP-сервіси (маршрути, middleware, обробка помилок) і як працюєш із даними (база, транзакції, кеш). Готуйся до кожного шару окремо: спочатку впевнене пояснення, потім робочий код. Нижче – маршрут і типові запитання з відповідями.

## Що реально перевіряють

Інтерв'юер на backend-позицію хоче зрозуміти три речі:

1. **Чи розумієш ти модель виконання Node.js.** Один потік, event loop, неблокуючий I/O. Без цього решта відповідей – завчені слова.
2. **Чи вмієш ти будувати надійні API.** Валідація вхідних даних, помилки, статуси, структура коду.
3. **Чи розумієш ти дані.** Коли транзакція обов'язкова, чому N+1 запитів – проблема, що кешувати.

## Шар 1 – модель виконання

Мінімум, який треба пояснювати без запинки:

- **Event loop.** Node виконує JavaScript в одному потоці; довгі операції (мережа, диск) віддаються системі, а колбеки повертаються в чергу. Тому сервер обробляє тисячі з'єднань без потоку на кожне.
- **Блокування.** Синхронний код (`JSON.parse` величезного файла, важкий цикл) зупиняє ВЕСЬ сервер, а не один запит. Це найчастіша пастка в запитаннях «чому сервіс завис».
- **Microtasks vs macrotasks.** `Promise.then` виконується раніше за `setTimeout(fn, 0)`. Загальна механіка та сама, що в браузері – детальний розбір є в статті про [Event Loop у JavaScript](/blog/event-loop-javascript).
- **Streams – на рівні ідеї.** Читати файл на 2 ГБ через `fs.readFile` означає покласти 2 ГБ у пам'ять; stream обробляє дані шматками. Достатньо вміти пояснити навіщо і навести приклад pipe з файла у відповідь HTTP.
- **Модулі.** Різниця CommonJS (`require`) і ESM (`import`), і чому в сучасних проєктах ESM. Першоджерело: [офіційні гайди Node.js](https://nodejs.org/en/learn).

## Шар 2 – HTTP-сервіси

### Middleware в Express

Middleware – це функції, через які проходить запит до того, як дійде до обробника. Логування, auth, парсинг тіла – усе це ланцюжок:

```ts
import express from 'express';

const app = express();

app.use(express.json());

// Простий middleware авторизації
app.use((req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  next();
});
```

На співбесіді питають не «що таке middleware», а «в якому порядку вони виконуються» (у порядку реєстрації) і «що станеться, якщо забути `next()`» (запит зависне).

### Nest на рівні концепцій

Якщо вакансія на Nest, треба розуміти dependency injection: клас оголошує залежності в конструкторі, фреймворк їх створює і підставляє. Це спрощує тестування – замість реального сервісу підставляєш mock:

```ts
@Injectable()
export class OrdersService {
  constructor(private readonly repository: OrdersRepository) {}

  findByUser(userId: string) {
    return this.repository.findMany({ userId });
  }
}
```

Достатньо пояснити навіщо DI (слабка зв'язаність, тестованість), не переказуючи документацію.

## Шар 3 – дані

- **Транзакції.** Коли дві операції мають виконатися разом або не виконатися взагалі (списати кошти + створити замовлення), потрібна транзакція. Вміти навести приклад, де без неї дані розсипаються.
- **N+1.** Отримати 100 замовлень одним запитом, а потім по одному запиту на кожного покупця – це 101 запит. Рішення: JOIN або batch-завантаження.
- **Вибір бази.** Мати аргументовану позицію, а не «Mongo, бо я його знаю». Розгорнутий розбір: [SQL vs NoSQL: як пояснити вибір на співбесіді](/blog/sql-vs-nosql).
- **Кеш.** Коли варто, а коли ні: [Redis і кешування](/blog/redis-koly-potriben-kesh).

## Типові запитання з відповідями

**Чому Node.js однопотоковий, але обробляє багато запитів одночасно?**
JavaScript виконується в одному потоці, але I/O-операції делегуються системі через libuv. Поки база відповідає на запит A, Node обробляє запит B. Паралельний тут I/O, а не JavaScript.

**Що станеться, якщо в обробнику виконати важке синхронне обчислення?**
Заблокується event loop: усі інші запити чекатимуть. Рішення: worker threads, черга задач або винесення обчислення в окремий сервіс.

**Чим `process.nextTick` відрізняється від `setImmediate`?**
`nextTick` виконується до наступної фази event loop (раніше за проміси), `setImmediate` – в окремій фазі після I/O. Практичне правило: обидва рідко потрібні в прикладному коді, і вміння сказати це вголос теж цінується.

**Як обробляти помилки в асинхронному коді?**
`try/catch` навколо `await`, централізований error-middleware в Express, обов'язковий обробник `unhandledRejection` на рівні процесу. Помилка без обробки в промісі може покласти процес.

**Навіщо змінні середовища, а не конфіг у коді?**
Секрети не потрапляють у git, конфігурація змінюється між середовищами без перезбирання. Питання-маркер: перевіряють гігієну, а не знання.

**Що таке ідемпотентність і чому вона важлива для API?**
Повторний виклик дає той самий результат. PUT ідемпотентний, POST зазвичай ні. Важливо для retry: клієнт може безпечно повторити запит після таймаута.

**Як би ти зробив rate limiting?**
На рівні ідеї: лічильник запитів на ключ (IP/користувач) з вікном часу, зберігання в Redis, відповідь 429 при перевищенні. Плюс згадати, що на практиці часто це робить API gateway.

## Live coding: endpoint з валідацією і помилками

Типова задача рівня Junior/Middle: «зроби POST /orders». Сильне рішення відрізняється не фічами, а охайністю країв:

```ts
import express from 'express';

const app = express();
app.use(express.json());

type CreateOrderBody = {
  productId: string;
  quantity: number;
};

function validateOrder(body: unknown): CreateOrderBody | null {
  if (typeof body !== 'object' || body === null) return null;
  const { productId, quantity } = body as Record<string, unknown>;
  if (typeof productId !== 'string' || productId.length === 0) return null;
  if (typeof quantity !== 'number' || !Number.isInteger(quantity) || quantity < 1) {
    return null;
  }
  return { productId, quantity };
}

app.post('/orders', async (req, res, next) => {
  try {
    const order = validateOrder(req.body);
    if (!order) {
      return res.status(400).json({ error: 'productId і quantity (ціле ≥ 1) обов’язкові' });
    }
    const created = await ordersService.create(order);
    res.status(201).json(created);
  } catch (error) {
    next(error);
  }
});

// Централізований обробник помилок – останнім
app.use((error: Error, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
  console.error(error);
  res.status(500).json({ error: 'Internal server error' });
});
```

Коментуй уголос: чому 400 vs 500, чому валідація до сервісу, що б ти покрив тестами (валідатор і обидві гілки обробника).

## Чекліст за рівнями

**Junior:**

- пояснити event loop і навести приклад блокування;
- зробити CRUD-endpoint з валідацією і статусами;
- знати різницю GET/POST/PUT/DELETE і коди 200/201/400/401/404/500;
- базовий SQL: SELECT з JOIN, INSERT.

**Middle:**

- усе вище + транзакції і рівні ізоляції на рівні розуміння;
- аргументований вибір бази і кешу;
- структура застосунку: шари, DI, тестованість;
- retry, ідемпотентність, rate limiting, graceful shutdown.

## Типові помилки кандидатів

1. **Завчений event loop без застосування.** Кандидат переказує фази циклу, але не може відповісти, чому конкретний endpoint «підвішує» сервер. Тренуй пояснення на прикладах, а не на схемах.
2. **`async` без обробки помилок.** Код на співбесіді без жодного `try/catch` і без error-middleware читається як «у production це впаде». Обробка помилок – не бонус, а базова частина рішення.
3. **Статуси навмання.** 200 на створення, 500 на невалідне тіло. Матриця «яка ситуація – який код» вчиться за вечір і сильно впливає на враження.
4. **«Я просто використовую ORM».** ORM – нормально, але за ним треба бачити SQL: що таке індекс, чому запит у циклі – проблема, коли транзакція обов'язкова.
5. **Мовчазне кодування.** Backend-задачі повні неозвучених рішень (валідація, помилки, межі). Якщо не коментуєш хід думок, інтерв'юер бачить лише фінальний код, а оцінює він мислення.

## Практичне завдання

Візьми свій pet-проєкт або зроби новий endpoint за прикладом вище і доведи його до «співбесідного» стану: валідація, коректні статуси, централізовані помилки, один тест на happy path і один на невалідний вхід. Потім поясни рішення вголос за 5 хвилин, ніби поруч сидить інтерв'юер. Ця вправа дає більше, ніж ще один перегляд списку питань.

Перед реальною співбесідою пройди хоча б одну пробну: формат сильно відрізняється від самостійного розв'язування. Як це працює – у статті про [mock interview](/blog/mock-interview-yak-prokhodyt).
