uWS Server
@miiajs/uws-server provides a high-performance HTTP server adapter using uWebSockets.js - a C++ networking library that is significantly faster than Node.js's built-in http module.
Installation
bun add @miiajs/uws-server uWebSockets.js@uNetworking/uWebSockets.js#v20.68.0
npm install @miiajs/uws-server uNetworking/uWebSockets.js#v20.68.0
pnpm add @miiajs/uws-server uNetworking/uWebSockets.js#v20.68.0
yarn add @miiajs/uws-server uNetworking/uWebSockets.js#v20.68.0
uWebSockets.js is installed directly from GitHub - the package is not published to npm.
Usage
import { Miia } from '@miiajs/core'
import { serve } from '@miiajs/uws-server'
const app = new Miia().register(AppModule)
await app.listen(3000, serve)
With custom hostname:
await app.listen(3000, '0.0.0.0', serve)
How it works
The adapter converts uWebSockets.js request/response objects to the Web Standard Request/Response API that MiiaJS expects. Since uWS HttpRequest is only valid synchronously, all metadata (method, URL, headers) is captured before any async gap.
Performance modes
Optimized mode (default)
Minimizes allocations on the hot path:
- Lazy Request Proxy - lightweight object instead of
new Request(). Method, URL, headers, and body are resolved only on first access. - Lightweight Headers - linear scan over header pairs instead of constructing a
Headersobject. uWS provides lowercase keys natively, so lookups are direct string comparisons. - Body Buffering - small POST bodies (Content-Length ≤
bufferThreshold) are buffered and parsed directly, bypassingReadableStreamandRequestcreation. Large or chunked bodies fall back to streaming. - LightResponse Cache - simple responses (string, null, Uint8Array) store a
[status, body, headers]tuple without creating a realResponseobject. - Corked Writes - batches header + body writes into a single syscall via
res.cork(). - Sync Fast Path - synchronous handlers bypass
Promiseallocation entirely.
Native mode
const server = await serve({
fetch: app.fetch,
port: 3000,
mode: 'native',
})
Full Web API compliance with standard Request and Response objects. Use when strict spec conformance is needed.
Standalone usage
import { serve } from '@miiajs/uws-server'
const server = await serve({
fetch: (req) => new Response('Hello, World!'),
port: 8080,
})
await server.close()
Options
interface ServeOptions {
fetch: (req: Request) => Response | Promise<Response> // Required
port?: number // Default: 3000
hostname?: string // Default: '0.0.0.0'
mode?: 'optimized' | 'native' // Default: 'optimized'
bufferThreshold?: number // Default: 102400 (100KB)
maxBodySize?: number | false // Default: 1048576 (1MB)
}
The bufferThreshold controls the body buffering optimization in optimized mode. POST/PUT/PATCH bodies with a known Content-Length up to this size are buffered in memory for fast json()/text() access. Bodies without Content-Length or larger than the threshold use ReadableStream for streaming.
The maxBodySize caps request bodies. A request whose declared Content-Length exceeds the cap gets an immediate 413 JSON response - the handler never runs. Chunked bodies (no Content-Length) are capped in-stream: the body stream rejects with an Error named 'PayloadTooLargeError', which @miiajs/core maps to a 413 automatically. Non-Miia consumers should catch this error by name. Pass false to disable the cap.
When used via app.listen(port, serve), Miia passes its computed ceiling (max of maxBodySize and all @BodyLimit values) automatically. To override it for the adapter, wrap the call:
await app.listen(3000, (info) => serve({ ...info, maxBodySize: 5 * 1024 * 1024 }))
When to use
Choose @miiajs/uws-server when:
- Maximum throughput is critical
- You're running on Node.js and need higher performance than the built-in
httpmodule - You're comfortable with native binary dependencies
Choose @miiajs/node-server when:
- You prefer zero native dependencies
- You need broader platform compatibility
- Performance is sufficient with the Node.js adapter
On Bun and Deno, no adapter is needed - app.listen() auto-detects the runtime.
Testing
@miiajs/uws-server is exercised directly via serve(). Tests must run under Node.js - the native uWS binary does not load on Bun or Deno.
// node --experimental-strip-types --no-warnings --test
import { afterEach, describe, it } from 'node:test'
import assert from 'node:assert/strict'
import { serve } from '@miiajs/uws-server'
let server: { close(): Promise<void> }
afterEach(async () => {
if (server) await server.close()
})
it('handles GET requests', async () => {
server = await serve({
port: 19234,
fetch: () => Response.json({ ok: true }),
})
const res = await fetch('http://localhost:19234/')
assert.equal(res.status, 200)
assert.deepEqual(await res.json(), { ok: true })
})
For applications that wire the adapter through app.listen(port, host, serve), prefer the TestApp pattern from Testing - it runs handlers without binding a port and works under any runtime.