Бібліотека µWebSockets написана на C/C++ і обгорнута тонким JavaScript-шаром через N-API
Один сервер обслуговує HTTP/1.1 та WebSocket з'єднання без додаткових залежностей
Вбудована підтримка SSL/TLS через BoringSSL для захищених з'єднань
Поставляється як готовий npm-пакет з бінарниками — без node-gyp та зовнішніх залежностей
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'); });
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)); }); }); });
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'); });
Нативний C++ код обробляє WebSocket-фрейми без накладних витрат JavaScript event loop. Це на порядок швидше за будь-яку чисто JS-реалізацію.
Кожне з'єднання споживає в рази менше RAM порівняно з ws чи socket.io. 100K одночасних з'єднань — не проблема.
Відсутність проміжних абстракцій між мережевим сокетом і вашим кодом дає мікросекундні затримки.
Вбудований механізм контролю зворотного тиску дозволяє відстежувати стан буфера клієнта й уникати перевантаження.
Нативна система підписок на теми (topics) для WebSocket — масштабне розсилання без зовнішніх брокерів повідомлень.
Мінімізація копіювання даних між C++ та JS шарами — ще один фактор продуктивності.
Не потрібно піднімати окремий сервер для WebSocket — маршрутизація, апгрейд і обробка в одному місці.
Прекомпільовані бінарники для Linux, macOS, Windows. npm install — і працює, без компіляції.
Підтримка потокової відправки відповідей для великих файлів або Server-Sent Events.
API повністю відрізняється від стандартного http.Server Node.js. Жоден популярний фреймворк не підключиться без адаптера.
Об'єкт запиту не можна зберегти або передати в async-ланцюжок — потрібно копіювати заголовки й параметри одразу. Це ламає звичні патерни.
Звичні патерни Node.js (middleware, pipe, async/await на req/res) не працюють. Потрібно перебудовувати мислення під колбек-стиль uWS.
Повторний виклик res.end() призводить до undefined behavior або сегфолту. В Express це безпечно ігнорується — тут ні. Потрібно ретельно контролювати потік відповіді.
Без cork() кожен writeHeader/write — окремий системний виклик. Це неочевидний патерн, якого немає в жодному іншому Node.js фреймворку, і без нього втрачається значна частина переваг швидкості.
Проєкт здебільшого веде Alex Hultman. Були випадки видалення репозиторіїв і зміни ліцензій — bus factor = 1.
Мінімум плагінів, middleware, прикладів і туторіалів. Документація існує, але значно поступається Express чи Fastify.
C++ ядро означає неінформативні стектрейси при краші. Стандартні Node.js профайлери бачать лише JS-частину.
uWebSockets.js використовує нативні C++ бінарники, скомпільовані під glibc. Alpine Linux використовує musl, тому node:alpine образи не підходять. Потрібен повний node:20 (~350 MB) або node:slim (~70 MB з обмеженнями), що суттєво збільшує розмір Docker-іміджа.
Типовий alpine-образ для Node.js сервісу ~50 MB. З uWS потрібен debian-based образ: ~180–350 MB. Для мікросервісної архітектури з десятками контейнерів це відчутне навантаження на registry та деплой.
Оскільки бінарники прекомпільовані під конкретні версії Node.js N-API, підтримка нових мажорних релізів (Node 22, 23+) може з'являтися із затримкою в тижні або місяці. Це блокує оновлення Node.js в проєкті.
Часто потрібно фіксувати точну версію Node.js у Dockerfile та CI/CD, бо оновлення рантайму може зламати сумісність з uWS бінарником. Це додаткове навантаження на DevOps.
Real-time мультиплеєр з мінімальною затримкою
Сотні тисяч одночасних з'єднань
Потокова передача котирувань з мікросекундною затримкою
Масове підключення пристроїв з низьким споживанням ресурсів
uWebSockets.js — ідеальний вибір, коли продуктивність є головним пріоритетом. Але врахуйте обмеження з Docker, версіями Node.js та відсутністю екосистеми middleware. Для типових задач — Fastify або Express будуть прагматичнішим вибором.
github.com/uNetworking/uWebSockets.js → 14 / 14