beta

uWS Server

uWebSockets.js HTTP server adapter for maximum throughput.

@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

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 Headers object. uWS provides lowercase keys natively, so lookups are direct string comparisons.
  • Body Buffering - small POST bodies (Content-Length ≤ bufferThreshold) are buffered and parsed directly, bypassing ReadableStream and Request creation. Large or chunked bodies fall back to streaming.
  • LightResponse Cache - simple responses (string, null, Uint8Array) store a [status, body, headers] tuple without creating a real Response object.
  • Corked Writes - batches header + body writes into a single syscall via res.cork().
  • Sync Fast Path - synchronous handlers bypass Promise allocation 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 http module
  • 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.