⚡️ A fast, minimalist web framework for the Bun JavaScript runtime

Overview

🥟 Bao.js

npm npm npm

Logo

A fast, minimalist web framework for the Bun JavaScript runtime.

⚡️ Bao.js is 3.7x faster than Express.js and has similar syntax for an easy transition.

Table of Contents

Background

Bun was released as a fast, modern JavaScript runtime. One of the many improvements over Node.js was the 2.5x increase in HTTP request throughput when compared to the Node.js http module (reference).

Bao.js uses Bun's built in Bun.serve module to serve routes and uses a radix tree for finding those routes resulting in exceptionally low latency response times. Bao is loosely syntactically modeled after Express.js and Koa.js with a handful of changes and upgrades to help with speed and improve the developer experience.

Bao works by creating a Context object (ctx) for each request and passing that through middleware and the destination route. This Context object also has various shortcut methods to make life easier such as by having standard response types (e.g. ctx.sendJson({ "hello": "world" })). When a route or middleware is finished, it should return the Context object to pass it along the chain until it is sent to the user.

The code is well documented and uses TypeScript, but more complete documentation will be added here in the future. It is not recommended to use this in production yet as both Bun and Bao.js are in beta.

Install

Although this package is distributed via NPM, is only compatible with Bun (not Node.js) as it uses native Bun libraries.

You must first install Bun and use it to run your server.

🧑‍💻 To install Bao.js, in your project directory, run bun add baojs

Usage

You can import Bao by using

import Bao from "baojs";

const app = new Bao();

To create a GET route, run

app.get("/", (ctx) => {
  return ctx.sendText("OK");
});

Then to get Bao to listen for requests, run

app.listen();

This will start a web server on the default port 3000 listening on all interfaces (0.0.0.0). The port can be modified in the listen() options

app.listen({ port: 8080 });

Examples

Hello World

Run bun index.ts

// index.ts
import Bao from "baojs";

const app = new Bao();

app.get("/", (ctx) => {
  return ctx.sendText("Hello World!");
});

app.listen();

Read request body

Run bun index.ts

// index.ts
import Bao from "baojs";

const app = new Bao();

app.post("/pretty", async (ctx) => {
  const json = await ctx.req.json();
  return ctx.sendPrettyJson(json);
});

app.listen();

Named parameters

Run bun index.ts

// index.ts
import Bao from "baojs";

const app = new Bao();

app.get("/user/:user", (ctx) => {
  const user = await getUser(ctx.params.user);
  return ctx.sendJson(user);
});

app.get("/user/:user/:post/data", (ctx) => {
  const post = await getPost(ctx.params.post);
  return ctx.sendJson({ post: post, byUser: user });
});

app.listen();

Wildcards

Wildcards are different to named parameters as wildcards must be at the end of paths as they will catch everything.

The following would be produced from the example below

  • GET /posts/123 => /123
  • GET /posts/123/abc => /123/abc

Run bun index.ts

// index.ts
import Bao from "baojs";

const app = new Bao();

app.get("/posts/*post", (ctx) => {
  return ctx.sendText(ctx.params.post);
});

app.listen();

Custom error handler

Run bun index.ts

// index.ts
import Bao from "baojs";

const app = new Bao();

app.get("/", (ctx) => {
  return ctx.sendText("Hello World!");
});

// A perpetually broken POST route
app.post("/broken", (ctx) => {
  throw "An intentional error has occurred in POST /broken";
  return ctx.sendText("I will never run...");
});

// Custom error handler
app.errorHandler = (error: Error) => {
  logErrorToLoggingService(error);
  return new Response("Oh no! An error has occurred...");
};

// Custom 404 not found handler
app.notFoundHandler = () => {
  return new Response("Route not found...");
};

app.listen();

Middleware

Middleware is split into middleware that runs before the routes, and middleware that runs after them. This helps to contribute to the performance of Bao.js.

// index.ts
import Bao from "baojs";

const app = new Bao();

// Runs before the routes
app.before((ctx) => {
  const user = getUser(ctx.headers.get("Authorization"));
  if (user === null) return ctx.sendEmpty({ status: 403 }).forceSend();
  ctx.extra["auth"] = user;
  return ctx;
});

app.get("/", (ctx) => {
  return ctx.sendText(`Hello ${ctx.extra.user.displayName}!`);
});

// Runs after the routes
app.after((ctx) => {
  ctx.res.headers.append("version", "1.2.3");
  return ctx;
});

app.listen();

The .forceSend() method tells Bao to not pass the Context object to anything else but instead send it straight to the user. This is useful in cases like this where we don't want unauthenticated users to be able to access our routes and so we just reject their request before it can make it to the route handler.

Benchmarks

Benchmarks were conducted using wrk with the results shown below.

Bao.js

$ wrk -t12 -c 500 -d10s http://localhost:3000/
Running 10s test @ http://localhost:3000/
  12 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    15.38ms    1.47ms  39.19ms   76.59%
    Req/Sec     2.67k   195.60     2.90k    82.33%
  318588 requests in 10.01s, 24.31MB read
  Socket errors: connect 0, read 667, write 0, timeout 0
Requests/sec:  31821.34
Transfer/sec:      2.43MB
import Bao from "baojs";

const app = new Bao();

app.get("/", (ctx) => {
  return ctx.sendText("OK");
});

app.listen();

Express.js

Bao.js can handle 3.7x more requests per second, with an equal 3.7x reduction in latency per request when compared to Express.js.

$ wrk -t12 -c 500 -d10s http://localhost:5000/
Running 10s test @ http://localhost:5000/
  12 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    56.34ms   13.42ms 246.38ms   90.62%
    Req/Sec   729.26    124.31     0.88k    86.42%
  87160 requests in 10.01s, 18.95MB read
  Socket errors: connect 0, read 928, write 0, timeout 0
Requests/sec:   8705.70
Transfer/sec:      1.89MB
const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("OK");
});

app.listen(5000);

Koa.js

Bao.js can handle 1.2x more requests per second, with an equal 1.2x reduction in latency per request when compared to the modern Koa.js.

$ wrk -t12 -c 500 -d10s http://localhost:1234/
Running 10s test @ http://localhost:1234/
  12 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    18.12ms    2.47ms  65.03ms   91.12%
    Req/Sec     2.26k   280.16     4.53k    90.46%
  271623 requests in 10.11s, 42.74MB read
  Socket errors: connect 0, read 649, write 0, timeout 0
Requests/sec:  26877.94
Transfer/sec:      4.23MB
const Koa = require("koa");
const app = new Koa();

app.use((ctx) => {
  ctx.body = "OK";
});

app.listen(1234);

Fastify

Bao.js is about equal to Fastify in both throughput and latency.

$ wrk -t12 -c 500 -d10s http://localhost:5000/
Running 10s test @ http://localhost:5000/
  12 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    15.32ms    1.90ms  60.53ms   78.74%
    Req/Sec     2.68k   274.95     3.25k    72.08%
  319946 requests in 10.01s, 50.65MB read
  Socket errors: connect 0, read 681, write 0, timeout 0
Requests/sec:  31974.36
Transfer/sec:      5.06MB
const fastify = require("fastify");
const app = fastify({ logger: false });

app.get("/", () => "OK");

app.listen({ port: 5000 });

Contribute

PRs are welcome! If you're looking for something to do, maybe take a look at the Issues?

If updating the README, please stick to the standard-readme specification.

License

MIT

Comments
  • Middleware usage

    Middleware usage

    Exciting new project you have here. I'm experimenting with bun.sh atm and are trying to use baojs to replace express with bao. I have some dependencies in my node_modules ie ui5-tooling that runs an express server. I'm trying to run some express middlewares through bao and have hacked the ui5 cli a bit to use bao instead. I understand that bao doesn't use app.use, but I need to specify a specific mountpath for my middleware. Is this something that is currently supported? The issue is here

    opened by uxkjaer 4
  • Add Websocket support (Bun v0.2.1)

    Add Websocket support (Bun v0.2.1)

    Bun recently released support for Websockets. It would be nice to expose this functionality at the minimum. Maybe later it can be expanded on with some Bao.js objects, as the base API still makes a lot of boilerplate to parse messages into a common format.

    Implementation is based on docs: https://github.com/oven-sh/bun/releases/tag/bun-v0.2.1

    opened by kylejreed 1
  • Creating a CI/CD Website and Documentation System for BaoJS

    Creating a CI/CD Website and Documentation System for BaoJS

    I'm working on another bun project and I found that you can get more contributors with a website. I built a really simple website and documentation generation tool if you want to add it to your project. It's under the MIT License- I'm just sending it around the bun community so it gains some more traction. If you want to check it out, it's right here. It's all integrated with Github Actions so all you have to do is add the code to GitHub actions and then you're all set. If you don't want to, just let me know and close this issue- good luck with your project!

    opened by William-McGonagle 0
  • [Snyk] Upgrade bun-types from 0.2.2 to 0.3.0

    [Snyk] Upgrade bun-types from 0.2.2 to 0.3.0

    Snyk has created this PR to upgrade bun-types from 0.2.2 to 0.3.0.

    :information_source: Keep your dependencies up-to-date. This makes it easier to fix existing vulnerabilities and to more quickly identify and fix newly disclosed vulnerabilities when they affect your project.


    • The recommended version is 1 version ahead of your current version.
    • The recommended version was released 22 days ago, on 2022-12-07.
    Release notes
    Package name: bun-types
    • 0.3.0 - 2022-12-07
    • 0.2.2 - 2022-10-27
    from bun-types GitHub release notes

    Note: You are seeing this because you or someone else with access to this repository has authorized Snyk to open upgrade PRs.

    For more information:

    🧐 View latest project report

    🛠 Adjust upgrade PR settings

    🔕 Ignore this dependency or unsubscribe from future upgrade PRs

    opened by snyk-bot 0
  • Add a changelog and push git tags

    Add a changelog and push git tags

    I noticed there's no changelog and no git tags. This makes it really hard to know what changed between versions, and if there are breaking changes. Since I have a package that can be used as a middleware, it makes it harder for me to see if I need to make changes to it when updates release.

    I would suggest creating a changelog using the Keep a Changelog format. This is what I am using for my own package. Also, by pushing the git tags, it would make it easier to compare the commits between versions.

    opened by jakobbouchard 0
  • Multiple middleware per route

    Multiple middleware per route

    For some APIs, its nice to have the ability to run some pre/post operations on a per-route or per-group basis. For example, you want some public routes, and some protected routes requiring a JWT token. Maybe this can currently be achieved in the Bao.before(), but the DX around it could be improved.

    One solution is to allow things like Bao.get() to accept multiple IHandler's.

    get(...handlers: IHandler[]) {}
    

    Another step up from that would be to implement a multi-router support like Express or Koa does

    const app = new Bao();
    const HelloRouter = new Router('/hello');
    HelloRouter.before(logger());
    HelloRouter.get(protect(), ctx => ctx.sendText('world'));
    
    app.use(HelloRouter); // or app.use('/hello', HelloRouter) if preferred DX
    
    opened by kylejreed 0
  • Add redirect support

    Add redirect support

    Closes #12

    Introduces a Context.redirect(url, alt, options) which sets up the proper headers for performing a redirect.

    From Koa, passing back as the url will attempt to use the Referrer header, and fallback to the alt parameter (ex. Context.redirect('back', '/index.html'))

    opened by kylejreed 0
  • Add an easy way to redirect to another site

    Add an easy way to redirect to another site

    Maybe a function like Context.redirect(newUrl) to redirect the user to a different page could be added. I think it could be very usefull for many projects.

    opened by Phil-hacker 1
Owner
Matt Reid
Matt Reid
Fast, Bun-powered, and Bun-only(for now) Web API framework with full Typescript support.

Zarf Fast, Bun-powered, and Bun-only(for now) Web API framework with full Typescript support. Quickstart Starting with Zarf is as simple as instantiat

Zarf Framework 65 Dec 28, 2022
🦆 lightning fast duckdb bindings for bun runtime

@evan/duckdb lightning fast duckdb bindings for bun runtime Install bun add @evan/duckdb Features ?? batteries included ?? jit optimized bindings ?? 4

evan 29 Oct 20, 2022
TypeScript type definitions for Bun's JavaScript runtime APIs

Bun TypeScript type definitions These are the type definitions for Bun's JavaScript runtime APIs. Installation Install the bun-types npm package: # ya

Oven 73 Dec 16, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Fast, and friendly Bun web framework

?? KingWorld Fast, and friendly Bun web framework. ⚡️ Faster than Express.js by 8.5x on M1 Max Named after my favorite VTuber (Shirakami Fubuki) and c

SaltyAom 114 Jan 4, 2023
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Fast Differentiable Tensor Library in JavaScript and TypeScript with Bun + Flashlight

A fast differentiable tensor library for research in TypeScript and JavaScript. Built with bun + flashlight. ⚠️ This is experimental software! ⚠️ Usag

Meta Research 917 Jan 7, 2023
A zero-dependency, strongly-typed web framework for Bun, Node and Cloudflare workers

nbit A simple, declarative, type-safe way to build web services and REST APIs for Bun, Node and Cloudflare Workers. Examples See some quick examples b

Simon Sturmer 16 Sep 16, 2022
Tiny and expressive web framework for Bun.js

Bagel Bagel is a tiny and expressive web framework for Bun.js for building web APIs. Inspired by Express.js and Koa.js. Here we treat Typescript as fi

KaKeng Loh 34 Nov 25, 2022
A next-gen web framework made on top of bun

Eviate JS (WIP) Next-generation web framework to build powerful apps Features Simple: No more req or res. It's all ctx (context) and plain objects! Fa

eviatejs 17 Oct 30, 2022
A minimal routing library designed to sit on top of Bun's fast HTTP server.

siopao A minimal routing library designed to sit on top of Bun's fast HTTP server. Based on Radix Tree. Sio=Hot Pao=Bun Installation bun add siopao Us

Robert Soriano 69 Nov 8, 2022
A blazingly fast Bun.js filesystem router, with an unpleasantly smooth experience!

Oily A blazingly fast Bun.js filesystem router, with an unpleasantly smooth experience! Installation · Usage · Examples · Discord Installation Once yo

Aries 22 Dec 19, 2022
⚡️A minimalistic and sweet router for blazing fast bun

Melonpan is a simple and minimalistic web-router designed to work with Bun, keeping performance in mind. ?? Why Melonpan? no/minimal learning curve De

Hemanth Krishna 66 Jan 6, 2023
Cross-runtime JavaScript framework

Primate, a cross-runtime framework Primate is a full-stack cross-runtime Javascript framework (Node.js and Deno). It relieves you of dealing with repe

null 8 Nov 7, 2022
⚗️Nitro provides a powerful toolchain and a runtime framework from the UnJS ecosystem to build and deploy any JavaScript server, anywhere

⚗️Nitro provides a powerful toolchain and a runtime framework from the UnJS ecosystem to build and deploy any JavaScript server, anywhere

unjs 1.3k Jan 5, 2023
For web frameworks on Node, on Deno, and on Bun.

Web Framework Bench For web frameworks on Node, on Deno, and on Bun. Fast is not everything, but fast is everything. Motivation There are some benchma

Yusuke Wada 8 Sep 7, 2022
A minimalist web app for the daily morning and night athkar.

Morning & Night Athkar | أذكار الصباح والمساء Local Development Recommended: Use nvm to manage npm dependencies and versions. This app uses node versi

Mohammed Amarnah 4 Dec 2, 2022
Minimalist Web XR Location Based Markers for A-Frame 1.3.0

LBAR.js “I understand how the engines work now. It came to me in a dream. The engines don't move the ship at all. The ship stays where it is and the e

Media Engineering Institute 9 Dec 3, 2022