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 🙏

© 2024 – Pkg Stats / Ryan Hefner

ultimate-express

v1.1.4

Published

The Ultimate Express. Fastest http server with full Express compatibility, based on uWebSockets.

Downloads

865

Readme

µExpress / Ultimate Express

The Ultimate Express. Fastest http server with full Express compatibility, based on µWebSockets.

This library is a very fast re-implementation of Express.js 4. It is designed to be a drop-in replacement for Express.js, with the same API and functionality, while being much faster. It is not a fork of Express.js.
To make sure µExpress matches behavior of Express in all cases, we run all tests with Express first, and then with µExpress and compare results to make sure they match.

npm install ultimate-express -> replace express with ultimate-express -> done*

Node.js >= 16.0.0 npm

Difference from similar projects

Similar projects based on uWebSockets:

  • express on Bun - since Bun uses uWS for its HTTP module, Express is about 2.5 times faster than on Node.js with 25k req/sec instead of 10k req/sec normally, but still slower than µExpress at 60k req/sec because it doesn't do uWS-specific optimizations.
  • hyper-express - while having a similar API to Express, it's very far from being a drop-in replacement, and implements most of the functionality differently. This creates a lot of random quirks and issues, making the switch quite difficult. Built in middlewares are also very different.
  • uwebsockets-express - this library is closer to being a drop-in replacement, but misses a lot of APIs, depends on Express by calling it's methods under the hood and doesn't try to optimize routing by using native uWS router.

Performance

Tested using wrk (-d 60 -t 1 -c 200). Etag was disabled in both Express and µExpress. Tested on Ubuntu 22.04, Node.js 20.17.0, AMD Ryzen 5 3600, 64GB RAM.

| Test | Express req/sec | µExpress req/sec | Express throughput | µExpress throughput | µExpress speedup | | --------------------------------------------- | --------------- | ---------------- | ------------------ | ------------------- | ---------------- | | routing/simple-routes (/) | 10.90k | 70.10k | 2.04 MB/sec | 11.57 MB/sec | 6.43X | | routing/lot-of-routes (/999) | 4.66k | 51.58k | 0.85 MB/sec | 8.07 MB/sec | 11.07X | | routing/some-middlewares (/90) | 10.18k | 66.97k | 1.81 MB/sec | 10.42 MB/sec | 6.58X | | routers/nested-routers (/abccc/nested/ddd) | 10.25k | 50.98k | 1.83 MB/sec | 7.98 MB/sec | 4.97X | | middlewares/express-static (/static/index.js) | 7.52k | 31.08k | 6.92 MB/sec | 26.48 MB/sec | 4.13X | | engines/ejs (/test) | 5.92k | 41.64k | 2.40 MB/sec | 16.55 MB/sec | 7.03X | | middlewares/body-urlencoded (/abc) | 7.90k | 29.90k | 1.64 MB/sec | 5.36 MB/sec | 3.78X |

Also tested on a real-world application with templates, static files and dynamic pages with data from database, and showed 1.5-4X speedup in requests per second depending on the page.

Differences from Express

In a lot of cases, you can just replace require("express") with require("ultimate-express") and everything works the same. But there are some differences:

  • case sensitive routing is enabled by default.
  • Depending on how you send response, Content-Length header may be overwritten or not sent at all:
    • on simple responses with res.send(), res.json(), etc. it's set automatically (any value you set with res.set() is overwritten)
    • on streaming responses (piping, res.sendFile()) it's not sent because uWS uses chunked transfer encoding instead
    • on responses without body, it is sent (useful for HEAD requests)
  • request body is only read for POST, PUT and PATCH requests by default. You can add additional methods by setting body methods to array with uppercased methods.
  • For HTTPS, instead of doing this:
const https = require("https");
const express = require("express");

const app = express();

https.createServer({
    key: fs.readFileSync('path/to/key.pem'),
    cert: fs.readFileSync('path/to/cert.pem')
}, app).listen(3000, () => {
    console.log('Server is running on port 3000');
});

You have to pass uwsOptions to the express() constructor:

const express = require("u-express");

const app = express({
    uwsOptions: {
        // https://unetworking.github.io/uWebSockets.js/generated/interfaces/AppOptions.html
        key_file_name: 'path/to/key.pem',
        cert_file_name: 'path/to/cert.pem'
    }
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});
  • This also applies to non-SSL HTTP too. Do not create http server manually, use app.listen() instead.

Performance tips

  1. µExpress tries to optimize routing as much as possible, but it's only possible if:
  • case sensitive routing is enabled (it is by default, unlike in normal Express).
  • only string paths without regex characters like *, +, (), {}, :param, etc. can be optimized.
  • only 1-level deep routers can be optimized.

Optimized routes can be up to 10 times faster than normal routes, as they're using native uWS router and have pre-calculated path.

  1. Do not use external serve-static module. Instead use built-in express.static() middleware, which is optimized for uExpress.

  2. Do not set body methods to read body of requests with GET method or other methods that don't need a body. Reading body makes server about 10k req/sec slower.

  3. By default, µExpress creates 1 (or 0 if your CPU has only 1 core) child thread to improve performance of reading files. You can change this number by setting threads to a different number in express(), or set to 0 to disable thread pool (express({ threads: 0 })). Threads are shared between all express() instances, with largest threads number being used. Using more threads will not necessarily improve performance. Sometimes not using threads at all is faster, please test both options.

Compatibility

In general, basically all features and options are supported. Use Express 4.x documentation for API reference.

✅ - Full support (all features and options are supported)
🚧 - Partial support (some options are not supported)
❌ - Not supported

express

  • ✅ express()
  • ✅ express.Router()
  • ✅ express.json()
  • ✅ express.urlencoded()
  • ✅ express.static()
    • Additionally you can pass options.ifModifiedSince to support If-Modified-Since header (this header is not supported in normal Express, but is supported in µExpress)
  • ✅ express.text()
  • ✅ express.raw()
  • 🚧 express.request (this is not a constructor but a prototype for replacing methods)
  • 🚧 express.response (this is not a constructor but a prototype for replacing methods)

Application

  • ✅ app.listen()
  • ✅ app.METHOD() (app.get, app.post, etc.)
  • ✅ app.route()
  • ✅ app.all()
  • ✅ app.use()
  • ✅ app.mountpath
  • ✅ app.set()
  • ✅ app.get()
  • ✅ app.enable()
  • ✅ app.disable()
  • ✅ app.enabled()
  • ✅ app.disabled()
  • ✅ app.path()
  • ✅ app.param(name, callback)
  • ✅ app.param(callback)
  • ✅ app.engine()
  • ✅ app.render()
  • ✅ app.locals
  • ✅ app.settings
  • ✅ app.engines
  • ✅ app.on("mount")
  • ✅ HEAD method

Application settings

  • ✅ case sensitive routing
  • ✅ env
  • ✅ etag
  • ✅ jsonp callback name
  • ✅ json escape
  • ✅ json replacer
  • ✅ json spaces
  • ✅ query parser
  • ✅ strict routing
  • ✅ subdomain offset
  • ✅ trust proxy
  • ✅ views
  • ✅ view cache
  • ✅ view engine
  • ✅ x-powered-by

Request

  • ✅ implements Readable stream
  • ✅ req.app
  • ✅ req.baseUrl
  • ✅ req.body
  • ✅ req.cookies
  • ✅ req.fresh
  • ✅ req.hostname
  • ✅ req.headers
  • ✅ req.headersDistinct
  • ✅ req.rawHeaders
  • ✅ req.ip
  • ✅ req.ips
  • ✅ req.method
  • ✅ req.url
  • ✅ req.originalUrl
  • ✅ req.params
  • ✅ req.path
  • ✅ req.protocol
  • ✅ req.query
  • ✅ req.res
  • ✅ req.secure
  • ✅ req.signedCookies
  • ✅ req.stale
  • ✅ req.subdomains
  • ✅ req.xhr
  • 🚧 req.route (route implementation is different from Express)
  • 🚧 req.connection, req.socket (only encrypted, remoteAddress, localPort and remotePort are supported)
  • ✅ req.accepts()
  • ✅ req.acceptsCharsets()
  • ✅ req.acceptsEncodings()
  • ✅ req.acceptsLanguages()
  • ✅ req.get()
  • ✅ req.is()
  • ✅ req.param()
  • ✅ req.range()

Response

  • ✅ implements Writable stream
  • ✅ res.app
  • ✅ res.headersSent
  • ✅ res.req
  • ✅ res.locals
  • ✅ res.append()
  • ✅ res.attachment()
  • ✅ res.cookie()
  • ✅ res.clearCookie()
  • ✅ res.download()
  • ✅ res.end()
  • ✅ res.format()
  • ✅ res.getHeader(), res.get()
  • ✅ res.json()
  • ✅ res.jsonp()
  • ✅ res.links()
  • ✅ res.location()
  • ✅ res.redirect()
  • ✅ res.render()
  • ✅ res.send()
  • ✅ res.sendFile()
    • ✅ options.maxAge
    • ✅ options.root
    • ✅ options.lastModified
    • ✅ options.headers
    • ✅ options.dotfiles
    • ✅ options.acceptRanges
    • ✅ options.cacheControl
    • ✅ options.immutable
    • ✅ Range header
    • ✅ Setting ETag header
    • ✅ If-Match header
    • ✅ If-Modified-Since header (with options.ifModifiedSince option)
    • ✅ If-Unmodified-Since header
    • ✅ If-Range header
  • ✅ res.sendStatus()
  • ✅ res.header(), res.setHeader(), res.set()
  • ✅ res.status()
  • ✅ res.type()
  • ✅ res.vary()
  • ✅ res.removeHeader()
  • ✅ res.write()
  • ✅ res.writeHead()

Router

  • ✅ router.all()
  • ✅ router.METHOD() (router.get, router.post, etc.)
  • ✅ router.route()
  • ✅ router.use()
  • ✅ router.param(name, callback)
  • ✅ router.param(callback)
  • ✅ options.caseSensitive
  • ✅ options.strict
  • ✅ options.mergeParams

Tested middlewares

Most of the middlewares that are compatible with Express are compatible with µExpress. Here's list of middlewares that we test for compatibility:

Middlewares that are confirmed to not work:

Tested view engines

Any Express view engine should work. Here's list of engines we include in our test suite: