htts

Expressive, easy to use, zero-dependency http server written in typescript

Usage no npm install needed!

<script type="module">
  import htts from 'https://cdn.skypack.dev/htts';
</script>

README

HTTS

Expressive, easy to use, zero-dependency http server written in typescript

Htts takes inspiration from the fetch API. It is simple and expressive, it has a very small footprint and it is also very performant. Take a look at the example repo.

Warnings

Htts relies heavily on the typescript compiler. If you make typescript happy, you make Htts happy. Keep in mind that if you are using Htts with plain javascript, it will not make any runtime type-checking. We recommend using it on typescript-only projects with strictNullChecks set to true inside your tsconfig.json file.

Installation

$ npm install htts

Usage

import { HttpServer, HttpStatus, Response } from "htts";

const htts = new HttpServer();

htts.post("/products", async (request) => {
  // You should validate this with your library of choice
  const body = await request.json();
  const product = await prisma.product.create(body);
  return Response.json(product, HttpStatus.Created);
});

const { port } = await htts.bind(4000);
console.log(`Ready on port http://localhost:${port}`);

With Next.js

Why would you want to use htts with Next.js?

  • No more if method === 'POST' statements
  • Expressive and familiar syntax
  • Catches HttpError and replies accordingly
  • Lightweight wrapper
// pages/api/posts.ts

import { post } from "htts/next";
import { Response, HttpStatus } from "htts";
import { PageConfig } from "next";

// This is important since htts does the parsing by itself.
// If you forget this and try to read the body, a `400` error will be thrown.
export const config: PageConfig = {
  api: {
    bodyParser: false,
  },
};

export default post(async (request) => {
  const body = await request.json();
  return Response.json(body, HttpStatus.Created);
});

Dynamic and Query Params

Htts normalizes both query and dynamic params into a single URLSearchParams instance. note: dynamic params override query string params.

// incoming url: /products/5?limit=10
htts.get("/products/:id", (request) => {
  const { url } = request;
  console.log(url.pathname); // /products/5
  console.log(url.searchParams); // URLSearchParams { 'id' => '5', 'limit' => '10' }
});

CORS

Htts comes with cors support out of the box. It is a fair simple implementation (for now) taken mostly from @koa/cors and expressjs/cors.

const htts = new HttpServer({ cors: true });

// or
const htts = new HttpServer({
  cors: { origin: "https://example.com", allowedHeaders: ["x-token"] },
});

Uploads

Htts has multipart/form-data support through multiparty.

const htts = new HttpServer();

htts.post("/upload", async (request) => {
  const { fields, files } = await request.formData();

  // Work your magic

  return Response.status(HttpStatus.Created);
});

Sockets

Htts is built around Node's core http module. It exposes the underlying server so it can be used alongside websocket libraries, such as Socket.IO or ws.

Socket.IO

import { HttpServer } from "htts";
import { Server as SocketServer } from "socket.io";

const htts = new HttpServer();
const io = new SocketServer(http.server, {
  /* other options */
});

io.on("connection", (socket) => {
  // work your magic
});

await http.bind(4000);

WS

import { HttpServer } from "htts";
import { WebSocket } from "ws";

const htts = new HttpServer();

// Create a WebSocket server completely detached from the HTTP server.
const wss = new WebSocket.Server({ clientTracking: false, noServer: true });

htts.server.on("upgrade", (request, socket, head) => {
  // Nice place to do some authentication

  wss.handleUpgrade(request, socket, head, (socket) => {
    wss.emit("connection", socket, request);
  });
});

wss.on("connection", (socket, request) => {
  // Work your magic...
});

await htts.bind(4000);

Benchmarks

Command

bombardier -c 126 -n 1000000 http://localhost:4000/example

Code

import { HttpServer, Response } from "htts";

const htts = new HttpServer();

htts.get("/example", () => Response.text("Hello world"));

htts.bind(4000);

Output

Statistics        Avg      Stdev        Max
  Reqs/sec     26228.48    1717.36   28772.79
  Latency        4.69ms   197.48us    19.60ms
  HTTP codes:
    1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     5.73MB/s

Here's the ouput for the express equivalent.

Statistics        Avg      Stdev        Max
  Reqs/sec      8880.65     839.30    9964.55
  Latency       14.19ms     0.93ms    69.20ms
  HTTP codes:
    1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     2.60MB/s