Skip to content

losfair/blueboat

Repository files navigation

Blueboat

CI

Blueboat is an all-in-one, multi-tenant serverless JavaScript runtime. See the site for a brief introduction on features.

A simple Blueboat application looks like:

Router.get("/", req => new Response("hello world"));

Router.get("/example", req => {
  return fetch("https://example.com");
});

Router.get("/yaml", req => {
  const res = TextUtil.Yaml.stringify({
    hello: "world",
  });
  return new Response(res);
});

Quickstart (single-tenant mode)

This pulls, builds, and runs the hello-world example.

Prerequisites: cargo, npm, git, docker

cargo install boatctl
git clone https://github.com/losfair/blueboat-examples
cd blueboat-examples/hello-world
npm i && boat pack -o build.json
docker run --rm -d -p 127.0.0.1:3001:3001 \
  -v "$PWD:/app" \
  --entrypoint /usr/bin/blueboat_server \
  -e RUST_LOG=info \
  -e SMRAPP_BLUEBOAT_DISABLE_SECCOMP=1 \
  ghcr.io/losfair/blueboat:v0.3.1-alpha.5 \
  -l "0.0.0.0:3001" \
  --single-tenant "/app/build.json"
curl http://localhost:3001 # "hello world"

The JavaScript API

Web API compatibility

Blueboat prefers to keep compatibility with the Web API when doing so is reasonable.

  • Things like fetch, Request, Response and URL are built-in.

No local resources

Blueboat is a “distributed-system-native” runtime and prioritizes frictionless scalability over single-node performance. Local resources are abstracted out and replaced with their equivalents in a distributed system:

  • Files → Key-value store
  • Sockets → Event stream
  • Single-file databases → Key-value store, App.mysql and App.postgresql object families
  • FFI → WebAssembly

Read and write files

// Don't: The filesystem is a local resource
import { writeFileSync } from "fs"
writeFileSync("hello.txt", "Hello from Node")

// Do: Use the key-value store
const ns = new KV.Namespace("files")
await ns.set("hello.txt", "Hello from Blueboat")

Stream data to client

// Don't: Sockets are a local resource
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ port: 8080 });
wss.on("connection", (ws) => {
  ws.on("message", (data) => {
    console.log("received: %s", data);
  });
  ws.send("something");
});

// Do: Use the PubSub API
Router.post("/send_to_group", async req => {
  const { groupId, message } = await req.json();
  await App.pubsub.myChannel.publish(groupId, message)
  return new Response("ok");
});

Use SQL databases

// Don't: SQLite databases are stored on the local filesystem
import { DB } from "https://deno.land/x/sqlite/mod.ts";
const db = new DB("test.db");
const people = db.query("SELECT name FROM people");

// Do: Use `App.mysql` or `App.postgresql`
const people = App.mysql.myDatabase.exec("SELECT name FROM people", {}, "s");

Developing on Blueboat

You can use your favorite JS/TS bundler to build your project for Blueboat. webpack works, and other bundlers like esbuild and bun should work too.

You can package and run your apps with single-tenant mode as described in the Quickstart section, or deploy it on a multi-tenant service like MagicBoat.

TypeScript type definitions

Pull in the blueboat-types package in your TypeScript project, and add it to your tsconfig.json:

{
  "compilerOptions": {
    "types": [
      "blueboat-types"
    ]
  }
}

API documentation

While there isn't a lot of documentation on Blueboat's API yet, the global object declared in jsland can be seen as the source-of-truth of the public API.

Meanwhile, refer to the tracking issue for a high-level overview of the API.

Deploying Blueboat

There are two options for deploying Blueboat apps.

Single-tenant

This mode works well for local development and private self-hosting. The steps are described in quickstart.

Multi-tenant

This is a fully optimized mode for multi-tenant operation. See the guide to deploy a multi-tenant environment yourself, or request access to our hosted environment, MagicBoat.

Frameworks

Simple web backends with JSON API and template-based rendering can be built without requiring any third-party frameworks. If you have more complex needs like server-side rendering React or just prefer a different style of router API, third-party frameworks are also available.

Flareact

Flareact is an edge-rendered React framework built for Cloudflare Workers. Since Blueboat and Workers both implement a large (and mostly overlapping) subset of the Web API, Flareact also works on Blueboat with some modifications in the entry script.

See the source code of my blog as an example of using Flareact with Blueboat.

Contributing

Build locally

Clone the repository, and run ./build.sh.

Build process internals

Normally the build process is handled automatically by build.sh. Here are some internals, in case you need it.

Blueboat is built in three stages: jsland build, rust prebuild and rust final build. The jsland build stage bundles /jsland/; the rust prebuild stage generates a blueboat_mkimage binary that is used to generate JSLAND_SNAPSHOT from /jsland/; and the rust final build stage generates the final blueboat_server binary.

Please refer to the CI script for a reproducible set of steps.

License

Apache-2.0