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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jnode/server

v2.2.3

Published

Simple web server package for Node.js.

Readme

@jnode/server

Simple web server package for Node.js.

Installation

npm i @jnode/server

Quick start

Import

const { createServer, routerConstructors: r, handlerConstructors: h } = require('@jnode/server');

Start a simple Hello, world. server

const server = createServer(
  // use text handler to respond with text
  h.Text('Hello, world.')
);

// listen to port 8080
server.listen(8080);

Start a complex file and API server

let requestCount = 0;

const server = createServer(
  // use path router to route the request
  r.Path(
    null, // nothing should be here because a basic path is `/`
    {
      // a request counter api
      // `@` makes sure the path ends here
      '@GET /api/request-count': h.JSON({ count: requestCount }),
      // a static file service
      // using `r.Function(route)` to count requests before continuing
      'GET /files': r.Function(() => {
        requestCount++;

        // return a folder handler; it will send files for remaining path segments
        return h.Folder('./static-files/');
      })
    }
  )
);

// listen to port 8080
server.listen(8080);

How it works?

Our world-leading router-handler framework brings you a simple, fast, and extensible development experience.

Here's what @jnode/server (shortened as JNS) will do:

  1. Receive a request.
  2. Use routers to route for a handler.
  3. Use a handler to complete the request.

Pretty simple, isn't it?

Further, a router has a method .route(env, ctx) which returns either another router (to continue routing) or a handler (which has a method .handle(ctx, env) that will be executed to complete the request).

Also, we provide some powerful built-in routers and handlers so you can start building your own web server now! (Learn more in the reference). And you can find more routers or handlers on npm with the query @jnode/server-<name> (built by the JNode team) or jns-<name> (built by the community).


Reference

server.createServer(router[, options])

We will check if options.key and options.cert exist to decide whether to enable TLS or not. Note that most browsers reject insecure HTTP/2.

Class: server.Server

new server.Server(router[, options])

More details in server.createServer().

Static method: Server.route(router[, env, ctx, options])

Please note that this function will modify env.i to count routing steps for safety.

server.listen()

Starts the server listening for connections. This method is identical to server.listen() from net.Server.

server.close(...args)

Closes the server. This method is identical to server.close() from net.Server.

server.throw(...args)

Emits an error event 'e' with the provided arguments. Useful for custom error handling and logging.

Event: 'e'

Emitted when an error occurs during routing or handling. The event includes the error object, env, and ctx.

Event: 'warn'

Emitted when a non-critical warning occurs, such as an invalid request URL.

Class: server.Router

This class is a base interface that defines the structure of a router; it does not contain built-in logic. Any object with method .route(env, ctx) will be viewed as a router.

router.route(env, ctx)

  • env <Object>

    • path <string[]> The request path split by '/' and excluding the first segment (which is always an empty string since the path always starts with '/'). E.G. '/a/b' will become [ 'a', 'b' ], and /a/ will become [ 'a', '' ].
    • pathPointer <number> The path index pointer. Use it in your own router if needed to ensure capability with PathRouter.
    • host <string[]> The request host split by . and reversed. E.G. 'example.com' will become [ 'com', 'example' ].
    • hostPointer <number> The host index pointer. Use it in your own router if needed so they could be compatible with the core HostRouter.
    • codeHandlers <Object> Keys are HTTP status codes, and values are handlers-extended.
    • i <number> How many routers the request has gone through; do not change it in your own routers because the main loop increments it.
  • ctx <Object>

  • Returns: <Promise> | <router> | handler-extended Fulfills with a router or handler-extended. Promise is not strictly required; synchronous functions are also acceptable.

The env object contains metadata mainly for the routing process, while the ctx object is mainly for the handling process and used to store custom properties in most cases.

Here is an example custom router:

const { h: handlerConstructors } = require('@jnode/server');

// meow router, always responds with the text 'Meow!' if the request path contains any 'meow'
// (ignore why we need this)
class MeowRouter {
  constructor(next) {
    this.next = next;
  }

  // this makes the class a router
  route(env, ctx) {
    if (env.path.includes('meow')) {
      return h.Text('Meow!');
    }
    return this.next;
  }
}

As for exporting, you could make a factory function like what we done in official routers:

// exporting
module.exports = {
  MeowRouter, // original class
  routerConstructors: {
    Meow: (...args) => new MeowRouter(...args)
  }
};

If you even want to publish your routers or handlers on npm (why not?), feel free to name your package as jns-<name> so other developers will know it's for @jnode/server! E.G. jns-meow.

Class: server.Handler

This class is a base interface that defines the structure of a handler; it does not contain built-in logic. Any object with method .handle(ctx, env) will be viewed as a handler.

handler.handle(ctx, env)

Handlers can throw a numeric error code to call codeHandlers, e.g., throw 404.

Here is an example custom handler:

// random int handler, returns a random integer
class RandomIntHandler {
  constructor(from, to, options) {
    this.from = from;
    this.to = to;
    this.options = options;
  }

  // this makes the class a handler
  handle(ctx, env) {
    const num = String(Math.floor(Math.random() * (this.to - this.from + 1)) + this.from);

    const headers = this.options.headers ?? {};
    headers['Content-Type'] = headers['Content-Type'] ?? 'text/plain';
    headers['Content-Length'] = Buffer.byteLength(num, 'utf8');

    ctx.res.writeHead(this.options.statusCode ?? 200, headers);
    ctx.res.end(num);
  }
}

handler-extended

handler-extended includes a standard handler and the following types:

server.mimeTypes

A mapping of file extensions (.<ext>) to their corresponding MIME types.

Built-in routers

We provide two methods to use them:

  1. new based, use it via new server.<Name>Router(...).
  2. Factory functions, use it via server.routerConstructors.<Name>(...).

Router: PathRouter(end, map)

  • end router | handler-extended Used when the path resolver came to the end (env.pathPointer >= env.path.length).
  • map <Object>
    • /<path_segment>[/<path_segment>...] router | handler-extended A simple path segment routing. E.G., '/meow': 'Meow!' (ignoring later segments, '/meow/something' will also have the same result as '/meow'), '/cats': r.Path(null, { '/meow': 'Meow!! Meow!!' }) or '/cats/meow': 'Meow!! Meow!!'.
    • @/<path_segment>[/<path_segment>...] router | handler-extended Used when the path resolver ends here; equals to '/<path_segment>': r.Path(<value>). E.G., '@/meow': 'Meow!' (only works for path '/meow' but not '/meow/something').
    • <METHOD>/<path_segment>[/<path_segment>...] router | handler-extended Used when the method matches; equals to '/<path_segment>': r.Method({ '<METHOD>': <value> }). E.G., 'GET/meow': 'Meow!' (only works for HTTP method 'GET').
    • @<METHOD>/<path_segment>[/<path_segment>...] router | handler-extended Used when both the path resolver ends and the method matches; equals to '/<path_segment>': r.Path(r.Method({ '<METHOD>': <value> })). E.G., '@GET/meow': 'Meow!' (only works for path '/meow' but not '/meow/something' and request method is 'GET').
    • * router | handler-extended Any path segment. E.G. '*': h.Text('Meow? Nothing here!', { statusCode: 404 }).
    • /%:<path_parameter_name> router | handler-extended Match any segment (if exists) and save the segment to ctx.params by <path_parameter_name>. Do the similar thing as PathArgRouter.

PathRouter is probably the most important router; almost every server needs it!

Please note that when defining only /%:arg/b and /a/c, requesting /a/b WILL NOT return the value of /%:arg/b. Instead, it will return 404. This limitation is in place to improve performance, and we believe in designing a great API. If you still need this functionality, you can build your own router.

By the way, if you’re looking for a universal matching character, use '/%:' instead of '/*' (this will match to '*' in a literal sense).

How it works?

This section delves into the intricacies of how PathRouter functions. If you’re not interested in this topic, feel free to skip it.

Everything begins with the constructor. When we receive your map, we parse it into an internal structure. Here's an example:

// the map you passed in
map = {
  '/a': 'A!',
  '/a/b': 'B!',
  '@/a/b': 'C!',
  'GET/a': 'D!',
  '@GET/a/b': 'E!',
  '@GET/%:arg/c': 'F!',
  '*': 'G!'
};

// we will parse into
parsed = {
  '/a': {
    '*': { // note that the * here doesn't count for path resolver
      // object of methods
      '*': 'A!',
      'GET': 'D!'
    },
    '/b': {
      '*': {
        // object of methods
        '*': 'B!'
      },
      '@': {
        '*': 'C!',
        'GET': 'E!'
      }
    }
  },
  ':': {
    '/c': {
      '@': {
        'GET': 'F!',
        '::GET': [ 'arg' ]
      }
    }
  },
  '*': 'G!'
};

This format allows for fast and flexible path routing while maintaining simplicity for developers. However, as mentioned earlier, some specialized path matching will not be supported.

Router: HostRouter(end, map)

  • end router | handler-extended Used when the host resolver came to the end (env.hostPointer >= env.host.length).
  • map <Object>
    • .<host_segment>[.<host_segment>...] router | handler-extended A simple host segment routing (reversed). E.G., .example.com will match example.com, .localhost will match localhost.
    • @.<host_segment>[.<host_segment>...] router | handler-extended Used when the host resolver ends here. E.G., @.example.com (only works for exactly example.com but not sub.example.com).
    • * router | handler-extended Any host segment.
    • .%:<host_parameter_name> router | handler-extended Match any segment (if exists) and save the segment to ctx.params by <host_parameter_name>.

Router: MethodRouter(methodMap)

Router: FunctionRouter(fn, ext)

  • fn <Function> A function with signature (env, ctx, ext) => router | handler-extended.
  • ext [<any>] Passed to func.

A simple router that allows you to implement custom routing logic.

Router: PathArgRouter(paramName, next)

  • paramName <string> The parameter name to save the current path segment.
  • next router | handler-extended The next router or handler to call after collecting the parameter.

Collects a path segment and saves it to ctx.params[paramName], then advances the path pointer and continues routing.

Router: HostArgRouter(paramName, next)

  • paramName <string> The parameter name to save the current host segment.
  • next router | handler-extended The next router or handler to call after collecting the parameter.

Collects a host segment and saves it to ctx.params[paramName], then advances the host pointer and continues routing.

Router: SetCodeRouter(codeHandlers, next)

  • codeHandlers <Object>
    • <STATUS_CODE> handler-extended A handler to respond when the specified HTTP status code is thrown. E.G., 404: h.Text('Not found!', { statusCode: 404 }), 500: h.JSON({ error: 'Internal server error' }, { statusCode: 500 }).
  • next router | handler-extended The next router or handler to call after setting up the code handlers.

Sets custom handlers for specific HTTP status codes, which will be used when handlers throw numeric error codes. The code handlers set by this router are merged with existing ones in env.codeHandlers.

Built-in handlers

We provide two methods to use them:

  1. new based, use it via new server.<Name>Handler(...).
  2. Factory functions, use it via server.handlerConstructors.<Name>(...).

Handler: DataHandler(data[, options]) (alias: TextHandler)

Sends string, buffer, or stream data as response body. Automatically sets appropriate Content-Type and Content-Length headers.

Handler: FileHandler(file[, options])

  • file <string> Path to the file to serve.
  • options <Object>
    • statusCode <number> HTTP status code. Default: 200.
    • headers <Object> Additional headers to send.
    • cache <boolean> | <number> Enable caching with ETag and Last-Modified headers. If true, uses only conditional caching. If number, also sets Cache-Control: max-age=<value>. Default: false.
    • disableRange <boolean> Disable HTTP Range requests. Also automatically disabled when statusCode is not 200. Default: false.
    • disableHead <boolean> Disable HEAD request support. Default: false.
    • highWaterMark <number> Stream high water mark in bytes. Default: 65536.

Serves a single file with support for HTTP Range requests, caching headers, and ETag validation. Throws 404 if file not found or is not a regular file; throws 416 if range is invalid. Supports 304 Not Modified responses for conditional requests.

Handler: FolderHandler(folder[, options])

Serves files from a folder based on remaining path segments. Automatically resolves paths and prevents directory traversal attacks. Internally uses FileHandler.

Handler: JSONHandler(obj[, options])

  • obj <Object> The object to serialize and send as JSON.
  • options <Object>
    • statusCode <number> HTTP status code. Default: 200.
    • headers <Object> Additional headers to send.

Sends a JavaScript object serialized as JSON with Content-Type: application/json; charset=utf-8.

Handler: RedirectHandler(location[, options])

  • location <string> The redirect target URL.
  • options <Object>
    • statusCode <number> HTTP status code. Default: 307.
    • base <string> Base URL for relative redirects. If set, the redirect location will be constructed as base + remaining path.
    • headers <Object> Additional headers to send.

Redirects the request to a specified location. Supports both absolute URLs and dynamic redirects based on remaining path segments.

Handler: FunctionHandler(func, ext)

  • func <Function> A function with signature (ctx, env, ext) => void | Promise<void>.
  • ext [<any>] Passed to func.

Allows you to implement custom request handling logic directly within a function.