npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@contextjs/webserver

v25.1.0

Published

A superfast, lightweight, extensible HTTP/HTTPS web server, designed with clean object-oriented principles and zero runtime dependencies.

Readme

@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