README
@egomobile/http-server
Very fast alternative HTTP server to Express, with simple routing and middleware support and which is compatible with Node.js 12 or later.
Table of contents
↑]
Install [Execute the following command from your project folder, where your
package.json
file is stored:
npm install --save @egomobile/http-server
↑]
Usage [↑]
Quick example [import createServer, { buffer, params, query } from "@egomobile/http-server";
async function main() {
const app = createServer();
// POST request for / route
// that uses the middleware buffer(), which loads the
// whole request body with a limit of 128 MB by default
// and writes the data to 'body' prop of 'request' object
// as Buffer
app.post("/", [buffer()], async (request, response) => {
const name: string = request.body!.toString("utf8");
response.write("Hello: " + name);
// no response.end() is required here
});
// parameters require a special path validator here
// s. https://github.com/lukeed/regexparam
// for more information about the string format
app.get(params("/foo/:bar/baz"), async (request, response) => {
response.write("BAR: " + request.params!.bar);
});
// parse query parameters from URL
// and write them to 'query' prop of 'request' object
app.get("/foo", [query()], async (request, response) => {
// request.query => https://nodejs.org/api/url.html#class-urlsearchparams
response.write(" BAR: " + request.query!.get("bar"));
response.write(" BAZ: " + request.query!.get("baz"));
});
await app.listen();
console.log(`Server now running on port ${app.port} ...`);
}
main().catch(console.error);
↑]
Middlewares [To enhance the functionality of your handlers, you can setup global or route specific middlewares.
For more details, have a look at the wiki page.
↑]
Controllers [The module provides tools, like decorators, functions and classes, that helps to setup routes and their behavior on a quite simple and high level.
Have a look at the wiki page for detailed information.
↑]
Error handling [import createServer from "@egomobile/http-server";
async function main() {
// ...
// custom error handler
app.setErrorHandler(async (error, request, response) => {
const errorMessage = Buffer.from("SERVER ERROR: " + String(error), "utf8");
if (!response.headersSend) {
response.writeHead(400, {
"Content-Length": String(errorMessage.length),
});
}
response.write(errorMessage);
response.end();
});
// custom 404 handler
app.setNotFoundHandler(async (request, response) => {
const notFoundMessage = Buffer.from(`${request.url} not found!`, "utf8");
if (!response.headersSend) {
response.writeHead(404, {
"Content-Length": String(notFoundMessage.length),
});
}
response.write(notFoundMessage);
response.end();
});
app.get("/", async (request, response) => {
throw new Error("Something went wrong!");
});
// ...
}
main().catch(console.error);
↑]
Pretty error pages [A nice example is, to use Youch! by Poppinss.
It prints pretty error pages in the browser:
import createServer, { prettyErrors } from "@egomobile/http-server";
import youch from "youch";
async function main() {
// ...
app.setErrorHandler(async (error, request, response) => {
const html = Buffer.from(await new youch(error, request).toHTML(), "utf8");
if (!response.headersSent) {
response.writeHead(500, {
"Content-Type": "text/html; charset=utf-8",
"Content-Length": String(html.length),
});
}
response.end(html);
});
app.get("/", async (request, response) => {
throw new Error("Oops! Something went wrong!");
});
// ...
}
main().catch(console.error);
A possible result could be:
↑]
Benchmarks [The following benchmarks were made with wrk on the following machine, running Node v16.13.2:
Machine:
- MacBook Pro (16", 2021)
- CPU: Apple M1 Max
- Memory: 64 GB
- OS: MacOS 12.1
Command: wrk -t8 -c100 -d30s http://localhost:3000/user/123
Express:
=============
Running 30s test @ http://localhost:3000/user/123
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.56ms 674.79us 14.59ms 90.47%
Req/Sec 3.39k 224.41 5.11k 75.04%
809164 requests in 30.03s, 118.84MB read
Requests/sec: 26947.30
Transfer/sec: 3.96MB
Fastify:
=============
Running 30s test @ http://localhost:3000/user/123
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.32ms 0.95ms 19.41ms 85.25%
Req/Sec 3.64k 280.76 4.87k 76.38%
869871 requests in 30.03s, 142.69MB read
Requests/sec: 28971.44
Transfer/sec: 4.75MB
Polka:
===========
Running 30s test @ http://localhost:3000/user/123
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.39ms 289.29us 13.20ms 91.15%
Req/Sec 8.66k 1.26k 10.67k 59.55%
2074873 requests in 30.10s, 259.22MB read
Requests/sec: 68930.81
Transfer/sec: 8.61MB
@egomobile/http-server:
============================
Running 30s test @ http://localhost:3000/user/123
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.05ms 220.64us 13.11ms 85.16%
Req/Sec 11.44k 1.39k 18.48k 81.16%
2737095 requests in 30.10s, 341.95MB read
Requests/sec: 90922.13
Transfer/sec: 11.36MB
Here is the test code, used recording the benchmarks.
↑]
Credits [The module makes use of:
- joi by Sideway Inc.
- js-yaml by Nodeca
- minimatch by isaacs
- mrmime by Luke Edwards
- regexparam by Luke Edwards
- Swagger UI and openapi-types
↑]
Documentation [The API documentation can be found here.
↑]
See also [- @egomobile/api-utils - Extensions for this module, helping realizing REST APIs