uWebSockets.js

Найшвидший WebSocket & HTTP сервер для Node.js
01 / 14

Що таке uWebSockets.js?

🧬

C++ ядро

Бібліотека µWebSockets написана на C/C++ і обгорнута тонким JavaScript-шаром через N-API

🌐

HTTP + WebSocket

Один сервер обслуговує HTTP/1.1 та WebSocket з'єднання без додаткових залежностей

🔒

TLS з коробки

Вбудована підтримка SSL/TLS через BoringSSL для захищених з'єднань

📦

Прекомпільований

Поставляється як готовий npm-пакет з бінарниками — без node-gyp та зовнішніх залежностей

02 / 14
{ } Приклад коду

HTTP та URL-параметри

http-params.js
import { App } from 'uWebSockets.js';

App()
  // Route-параметри через :param
  .get('/api/users/:id', (res, req) => {
    const id = req.getParameter(0);  // перший :param
    res.end(`User ID: ${id}`);
  })

  // Кілька параметрів у маршруті
  .get('/api/:version/items/:itemId', (res, req) => {
    const version = req.getParameter(0); // "v1"
    const itemId  = req.getParameter(1); // "42"
    res.end(`${version} → item ${itemId}`);
  })

  // Query-параметри — парсимо вручну
  .get('/search', (res, req) => {
    const query = req.getQuery();       // "q=hello&page=2"
    const params = new URLSearchParams(query);
    const q    = params.get('q');      // "hello"
    const page = params.get('page');   // "2"
    res.end(`Search: ${q}, page ${page}`);
  })

  .listen(3000, (token) => {
    if (token) console.log('Server on :3000');
  });
Route-параметри отримуються через req.getParameter(index) за порядковим номером, а не за іменем. Query-string доступний через req.getQuery() — парсити потрібно самостійно.
03 / 14
{ } Приклад коду

cork() та обмеження end()

cork-and-end.js
App().get('/api/data', (res, req) => {

  // ✅ cork() — групує всі записи в один syscall
  // Без нього кожен writeHeader/write — окремий виклик
  res.cork(() => {
    res.writeStatus('200 OK');
    res.writeHeader('Content-Type', 'application/json');
    res.writeHeader('X-Custom', 'value');
    res.end(JSON.stringify({ ok: true }));
  });

  // ❌ ПОМИЛКА — end() вже викликано вище!
  // res.end('second call'); // → CRASH / UB

  // ❌ ПОМИЛКА — writeHeader після end()
  // res.writeHeader('X-Late', 'too late');
});

// ⚠️ Async + cork — правильний патерн
App().get('/api/async', (res, req) => {
  // Зберігаємо дані з req ДО async
  const url = req.getUrl();
  let aborted = false;
  res.onAborted(() => { aborted = true; });

  fetchData(url).then(data => {
    if (aborted) return;
    res.cork(() => {
      res.writeHeader('Content-Type', 'application/json');
      res.end(JSON.stringify(data));
    });
  });
});
cork() об'єднує writeStatus + writeHeader + end в один системний виклик — суттєвий приріст швидкості. end() можна викликати лише ОДИН раз — повторний виклик призведе до краша або undefined behavior.
04 / 14
{ } Приклад коду

WebSocket та Pub/Sub

websocket-pubsub.js
import { App } from 'uWebSockets.js';

App().ws('/*', {
  maxPayloadLength: 16 * 1024,
  idleTimeout: 120,

  open: (ws) => {
    // Підписуємо клієнта на канал
    ws.subscribe('chat/general');
    console.log('Connected & subscribed');
  },

  message: (ws, message, isBinary) => {
    // Pub: розсилаємо всім підписникам
    ws.publish('chat/general', message, isBinary);
  },

  drain: (ws) => {
    // Backpressure: буфер звільнився
    console.log('Buffered: ' + ws.getBufferedAmount());
  },

  close: (ws, code, msg) => {
    // Відписка від каналів — автоматична
    console.log('Disconnected');
  }
}).listen(9001, (token) => {
  if (token) console.log('WS on :9001');
});
Вбудований Pub/Sub без Redis чи брокерів. Подія drain контролює backpressure. При close відписка від каналів відбувається автоматично.
05 / 14

🚀 Бенчмарк продуктивності

uWebSockets.js
~121 000 req/s
Bun.serve
~110 000 req/s
Fastify
~24 000 req/s
Express.js
~7 000 req/s
* Hello World, single-thread, Intel i9-9940X · Джерело: GitHub oven-sh/bun#8643, uWebSockets.js#1097
06 / 14
✦ Перевага

Екстремальна швидкість

Мільйони повідомлень на секунду

Нативний C++ код обробляє WebSocket-фрейми без накладних витрат JavaScript event loop. Це на порядок швидше за будь-яку чисто JS-реалізацію.

Мінімальне споживання пам'яті

Кожне з'єднання споживає в рази менше RAM порівняно з ws чи socket.io. 100K одночасних з'єднань — не проблема.

Низька затримка (latency)

Відсутність проміжних абстракцій між мережевим сокетом і вашим кодом дає мікросекундні затримки.

07 / 14
✦ Перевага

Продумана архітектура

Backpressure handling

Вбудований механізм контролю зворотного тиску дозволяє відстежувати стан буфера клієнта й уникати перевантаження.

Вбудований Pub/Sub

Нативна система підписок на теми (topics) для WebSocket — масштабне розсилання без зовнішніх брокерів повідомлень.

Zero-copy де можливо

Мінімізація копіювання даних між C++ та JS шарами — ще один фактор продуктивності.

08 / 14
✦ Перевага

Зручні можливості

HTTP + WebSocket в одному процесі

Не потрібно піднімати окремий сервер для WebSocket — маршрутизація, апгрейд і обробка в одному місці.

Без node-gyp

Прекомпільовані бінарники для Linux, macOS, Windows. npm install — і працює, без компіляції.

Chunked transfer & streaming

Підтримка потокової відправки відповідей для великих файлів або Server-Sent Events.

09 / 14
✗ Недолік

Несумісний API

Не працює з Express/Koa/Fastify

API повністю відрізняється від стандартного http.Server Node.js. Жоден популярний фреймворк не підключиться без адаптера.

HttpRequest живе лише в колбеку

Об'єкт запиту не можна зберегти або передати в async-ланцюжок — потрібно копіювати заголовки й параметри одразу. Це ламає звичні патерни.

Крива навчання

Звичні патерни Node.js (middleware, pipe, async/await на req/res) не працюють. Потрібно перебудовувати мислення під колбек-стиль uWS.

end() лише один раз — інакше краш

Повторний виклик res.end() призводить до undefined behavior або сегфолту. В Express це безпечно ігнорується — тут ні. Потрібно ретельно контролювати потік відповіді.

Обов'язковий cork() для продуктивності

Без cork() кожен writeHeader/write — окремий системний виклик. Це неочевидний патерн, якого немає в жодному іншому Node.js фреймворку, і без нього втрачається значна частина переваг швидкості.

10 / 14
✗ Недолік

Ризики та екосистема

Один основний мейнтейнер

Проєкт здебільшого веде Alex Hultman. Були випадки видалення репозиторіїв і зміни ліцензій — bus factor = 1.

Мала екосистема

Мінімум плагінів, middleware, прикладів і туторіалів. Документація існує, але значно поступається Express чи Fastify.

Складне відлагодження

C++ ядро означає неінформативні стектрейси при краші. Стандартні Node.js профайлери бачать лише JS-частину.

11 / 14
✗ Недолік

Docker та версії Node.js

🐳 Потрібен повний Node.js — не Alpine

uWebSockets.js використовує нативні C++ бінарники, скомпільовані під glibc. Alpine Linux використовує musl, тому node:alpine образи не підходять. Потрібен повний node:20 (~350 MB) або node:slim (~70 MB з обмеженнями), що суттєво збільшує розмір Docker-іміджа.

📦 Великий Docker-імідж у продакшені

Типовий alpine-образ для Node.js сервісу ~50 MB. З uWS потрібен debian-based образ: ~180–350 MB. Для мікросервісної архітектури з десятками контейнерів це відчутне навантаження на registry та деплой.

🕐 Відставання від нових версій Node.js

Оскільки бінарники прекомпільовані під конкретні версії Node.js N-API, підтримка нових мажорних релізів (Node 22, 23+) може з'являтися із затримкою в тижні або місяці. Це блокує оновлення Node.js в проєкті.

⚠️ Прив'язка до конкретних версій

Часто потрібно фіксувати точну версію Node.js у Dockerfile та CI/CD, бо оновлення рантайму може зламати сумісність з uWS бінарником. Це додаткове навантаження на DevOps.

12 / 14

Де використовувати?

🎮

Ігрові сервери

Real-time мультиплеєр з мінімальною затримкою

💬

Чат-системи

Сотні тисяч одночасних з'єднань

📈

Фінансові біржі

Потокова передача котирувань з мікросекундною затримкою

📡

IoT платформи

Масове підключення пристроїв з низьким споживанням ресурсів

13 / 14

Висновок

uWebSockets.js — ідеальний вибір, коли продуктивність є головним пріоритетом. Але врахуйте обмеження з Docker, версіями Node.js та відсутністю екосистеми middleware. Для типових задач — Fastify або Express будуть прагматичнішим вибором.

github.com/uNetworking/uWebSockets.js → 14 / 14