@contextjs/webserver
TypeScript icon, indicating that this package has built-in type declarations

25.1.0 • Public • Published

@contextjs/webserver

Tests npm License

High-performance, TypeScript-first HTTP/HTTPS server built directly on raw TCP sockets for maximum throughput, and zero runtime dependencies. Supports HTTP/2 with automatic HTTP/1.1 fallback, pooled contexts for minimal GC, and a robust middleware pipeline.

Designed to integrate seamlessly into the ContextJS ecosystem or run standalone.

Table of Contents

  1. Installation
  2. Features
  3. Benchmarks
  4. Quick Start
  5. Basic Middleware Example
  6. Streaming a File
  7. Configuration Reference
  8. API Reference
  9. Events
  10. Exceptions

Installation

npm i @contextjs/webserver

Features

  • Socket-based core leveraging raw TCP sockets for lowest-level performance
  • HTTP and HTTPS support with PEM-encoded certificates
  • Middleware pipeline for modular request handling
  • Context pooling for reusing HttpContext instances
  • Configurable header limits and idle socket cleanup
  • Lifecycle events (info, warning, error) for observability
  • TypeScript declarations with full JSDoc support
  • Zero dependencies for maximum performance and minimal footprint

Benchmarks

Below are the results of our WebServer benchmark suite, which compares throughput and latency across four different Node.js–based HTTP servers. Each test is run on GitHub Actions' provided "ubuntu-latest" runner. The benchmarks target a minimal server that responds with a 200 OK status and a short body of "OK" They use 500 concurrent connections, a pipelining factor of 1, and run for 10 seconds, with a warmup phase, and results averaged over three runs.

Summary

Server Req/sec Latency (ms) Throughput (MB/s) Errors
ContextJS 15492.53 31.97 2.76 0.00
Node.js Raw HTTP 14793.33 33.51 2.64 0.00
Fastify 13884.27 35.80 2.48 0.00
Express 8942.94 55.93 2.14 0.00

Column descriptions:

  • Req/sec — Average number of HTTP requests served per second.
  • Latency (ms) — Median (50th percentile) response time in milliseconds.
  • Throughput (MB/s) — Average data transferred per second.
  • Errors — Total connection-level failures (e.g. resets, refusals).

Extended Metrics

Server Connections Pipelining Duration (s) Latency Stdev (ms) Requests Stdev Throughput Stdev (MB/s) Total Requests
ContextJS 500 1 10.07 102.32 258.50 0.05 464750
Node.js Raw HTTP 500 1 10.08 109.54 198.34 0.04 443750
Fastify 500 1 10.10 121.67 174.27 0.03 416500
Express 500 1 10.15 237.23 191.14 0.05 268250

Extended column descriptions:

  • Connections — Number of simultaneous TCP connections opened.
  • Pipelining — Number of requests pipelined per connection.
  • Duration (s) — Total benchmark runtime in seconds.
  • Latency Stdev (ms) — Standard deviation of response latency.
  • Requests Stdev — Standard deviation of the requests/sec across samples.
  • Throughput Stdev (MB/s) — Standard deviation of throughput.
  • Total Requests — Sum of all successful 2xx responses across all iterations.

Quick Start

Standalone Usage

import { HttpContext, HttpWebServerOptions, WebServer, WebServerOptions } from "@contextjs/webserver";

const options = new WebServerOptions(
    undefined,
    new HttpWebServerOptions(true, "0.0.0.0", 3000, 60_000)
);

const server = new WebServer(options);

// Simple middleware
server.useMiddleware({
    name: "hello-world",
    async onRequest(context: HttpContext) {
        await context.response
            .setHeader("Content-Type", "text/plain")
            .sendAsync("Hello, ContextJS!");
    }
});

server.startAsync()

setTimeout(async () => {
    await server.stopAsync();
    console.log("Server stopped after 60 seconds");
}, 60000);

Application Integration

If you’re using the ContextJS Application from @contextjs/system, you can wire up the server directly:

import "@contextjs/webserver"; // module augmentation
import { Application } from "@contextjs/system";
import { HttpContext } from "@contextjs/webserver";

const app = new Application();

app.useWebServer(options => {
    options.http.port = 8080;
    options.onEvent = e => console.log(`[WebServer:${e.type}]`, e.detail);
});

app.webServer.useMiddleware({
    name: "logger",
    version: "1.0.0",
    onRequest(context: HttpContext) {
        console.log(`${context.request.method} ${context.request.path}`);
        context.response
            .setHeader("Content-Type", "text/plain")
            .setHeader("X-ContextJS", "Hello World")
            .sendAsync("Hello, ContextJS!");
    }
});

await app.runAsync();

Streaming a File

app.webServer.useMiddleware({
    name: "static-file",
    version: "1.0.0",
    async onRequest(context: HttpContext, next: () => Promise<void>) {
        if (context.request.path.startsWith("/assets/")) {
            const filePath = path.join(__dirname, context.request.path);
            return await context.response
                .setHeader("Content-Type", "application/octet-stream")
                .streamAsync(createReadStream(filePath));
        }
        await next();
    }
});

Uploading and Saving a File from the Request Body

import { HttpContext } from "@contextjs/webserver";
import { Controller, IActionResult, Ok, Post } from "@contextjs/webserver-middleware-controllers";
import fs from "node:fs";
import path from "node:path";
import { pipeline } from "node:stream/promises";

@Controller()
export class HomeController {

    @Post("index")
    public async indexPost(context: HttpContext): Promise<IActionResult> {
        // Choose upload directory and file name
        const uploadDir = path.join(process.cwd(), "uploads");
        const fileName = "uploaded-file.png";
        const uploadPath = path.join(uploadDir, fileName);

        // Ensure upload directory exists
        await fs.promises.mkdir(uploadDir, { recursive: true });

        // Stream the request body directly to disk (no memory buffering)
        const writeStream = fs.createWriteStream(uploadPath);
        await pipeline(context.request.body, writeStream);

        return Ok("File uploaded successfully!");
    }
}

Configuration Reference

WebServerOptions

Property Type Description
general GeneralWebServerOptions Header limits, pool size, idle socket timeout
http HttpWebServerOptions HTTP binding: port, host, keep-alive
https HttpsWebServerOptions HTTPS binding: port, host, keep-alive, SSL certificate
onEvent (event: WebServerEvent) Callback for info / warning / error events

GeneralWebServerOptions

  • maximumHeaderSize: number — max header bytes (default: 32 * 1024)
  • httpContextPoolSize: number — pre-allocate contexts (default: 1024)
  • idleSocketsTimeout: number — ms before closing idle sockets (default: 5000)

HttpWebServerOptions

  • enabled: boolean — enable HTTP (default: true)
  • host?: string — bind address (default: "localhost")
  • port: number — port number (default: 80)
  • keepAliveTimeout: number — ms for connection keep-alive (default: 5000)

HttpsWebServerOptions

  • enabled: boolean — enable HTTPS (default: false)
  • host?: string — bind address (default: "localhost")
  • port: number — port number (default: 443)
  • certificate: { key: string; cert: string } — PEM key & cert
  • keepAliveTimeout: number — ms for connection keep-alive (default: 5000)

API Reference

For detailed API documentation, please refer to the API Reference.

Events

The server emits runtime events via the onEvent callback:

  • info — general progress messages
  • warning — recoverable issues (e.g. idle socket timeout)
  • error — fatal or unexpected errors

Package Sidebar

Install

npm i @contextjs/webserver

Weekly Downloads

9

Version

25.1.0

License

MIT

Unpacked Size

113 kB

Total Files

39

Last publish

Collaborators

  • ~contextjs