Getting Started With Bun

Getting Started With Bun

Bun is a new JavaScript runtime that is redefining the developer experience and setting some pretty high standards when it comes to performance. As of Feb-24, I’ve not yet had the opportunity to deploy a "serious" project with it, but everything I’ve seen so far is really exciting!

The project homepage does a great "sales" job. So, if you’re not familiar with bun, then I highly recommend you give it a read and come back to this article where I’ll go over some of the great stuff I’ve encountered so far.

Bun is So Damn Fast!

The benchmarks quoted on the website really match with my own experience. I’ve been building three separate projects with bun: a React SPA frontend, a backend API, and an ORM library. Across all three of them I’m constantly amazed at how fast things are. What stands out immediately is how package management feels instant and tests compile as the speed of light. Heck, even the install (single binary) just finished so quickly. I waited longer to source by shell than I did to install/setup bun!

Just Write Your Code, No Setup Needed

Imagine making a new directory, creating a .ts file, and just having everything work. Bun's approach to TypeScript and its compilation/bundling process is nothing short of a game-changer. TypeScript is a first-class citizen and is smoothly integrated with runtime. There’s no need to install and configure transpilation or build steps, or introduce any other toolchain clutter.

Powerful Testing Tools

You can also just create a *.test.ts file and start writing your unit tests! Bun comes with a highly performant test runner and testing utilities are part of the standard library. Just write your test and run it with bun test. You don’t need a testing library. Again, the toolchain clutter is reduced further. Here’s a minimal sample:

import { test, expect } from 'bun:test';
import { add } from './add';

test('adds two numbers together', () => {
    const result = add(2, 3);
    expect(result).toBe(5);
});

Want to watch for changes? Intuitively, just add --watch. Need snapshot tests? DOM testing? It’s all supported. Want to migrate from Jest? The syntax and APIs are so similar, you’ll have an easy time.

In an existing project I saw a 2x improvement in testing speed (~420 test cases) by just switching over to bun. This took less than a few hours to migrate as I only really needed to come up with a solution for the missing expect.assertions(number) API.

SQLite Built-in

Bun comes with a native, high-performant driver for SQLite3. The API is extremely simple to use and you can spin-up a quick a in-memory database with two lines of code:

import { Database } from 'bun:sqlite';

const db = new Database(':memory:');
const query = db.query('SELECT 1234 as result;');
query.get();

Given the simplicity and millisecond-level performance, I imagine we will be seeing some cool frameworks and DX tools come out. I recently published furnace-sql, which explores some fun ways to use this. In short, this library provides a cool way to interact with csv and parquet files by writing SQL. Sometimes, I just want to crunch some numbers (based on relation data) and discard everything when I’m done. The simplicity of bun’s API made it possible to make something like this in an afternoon!

Unexpected, Wonderful Utilities

One way to achieve speed is to sacrifice utility. For example, AWS published LLRT recently – a JavaScript runtime optimized for low-latency execution in Lambda. But they really sacrificed a lot to get it so fast.

In contract, Bun is not missing any features and in fact comes with some great, unexpected gems. For example, script writers will love the import.meta utilities that let’s them easily check the current directly or file name.

Then there’s the Bun interface, which comes with all sorts of fun methods. For example, you can easily setup DEFLATE compression:

const data = Buffer.from('Hello, World!');
const compressed = Bun.deflateSync('Hello, World!'); // => Uint8Array
const decompressed = Bun.inflateSync(compressed); // => Uint8Array

Or setup a HTTP or WebSocket server:

const httpServer = Bun.serve({
  port: 3000,
  fetch(request) {
    return new Response('Hello, World!');
  },
});

const wsServer = Bun.serve({
  websocket: {
    async message(ws, message) {
      ws.send(`Echo: ${message}`);
    },
  },
});

Or write incrementally to a file – for those cases when performance matters – via a simple API:

const file = Bun.file('/path/to/file.txt');
const writer = file.writer();

writer.write('1234');
writer.write('5678');
writer.write('9010');

writer.flush();

Anyway, I hate how abruptly this post ends, but there’s no witty punch-line. Bun is simply awesome and I really encourage you to give it a shot and explore how it can deliver value for your project.