Explore Bun.js: The all-in-one JavaScript runtime

Bun is an ambitious project that is quickly emerging as a drop-in replacement and faster alternative to Node.js. Here's a look at how Bun works and how to use it for your server-side JavaScript applications.

digital transformation /finger tap causes waves of interconnected digital ripples

Bun.js is an all-in-one JavaScript toolkit whose lighthearted moniker belies its transformative potential. Bun draws together several important themes in server-side JavaScript, resulting in a single, high-performance tool. It is a runtime like Node or Deno, a package manager like NPM or pnpm, and a build tool like webpack or Vite. It has rapidly evolved from a one-person side project to a compelling alternative to standard approaches.

Bun vs. Node.js

Bun.js is fundamentally a server-side JavaScript runtime like Node. Atop this is incorporated a package manager and a bundler/transpiler. The runtime itself is the current focus of development and is the most fully realized part of the project. The package manager is the next most developed, and the bundler is the most alpha-stage at the moment.

Bun creator Jarred Sumner told me, “We want to make JavaScript faster to run and simpler to write. An important part of that is ecosystem compatibility. Bun is designed as a drop-in Node.js replacement. People shouldn’t have to rewrite their code to use Bun.”

Bun is built from the ground up with the WebKit/Safari JavaScriptCore engine (instead of V8 like Node). It is not a fork of Node. The libraries are built in C and Zig, and it explicitly avoids any Node or NPM dependencies, thereby minimizing JavaScript in its stack. These decisions are all in service of maximizing performance. Rewriting the universe of JavaScript-implemented APIs like network and disk IO in a lower level language nets huge performance gains. It’s also a monumental undertaking.

Bun aims to cover the entire Node/NPM API, and it is rapidly moving towards that aim. Here’s the Bun project roadmap, where you can get a sense of the scope of the project and how quickly it is moving. Additionally, there’s a list of planned features yet to be implemented. You’ll notice that Bun includes edge-oriented features and much more. It is really an entire JavaScript ecosystem built on top of a runtime engine.

Bun is in active development, and its developers acknowledge that “it is experimental software.”  Currently, the focus is on expanding and stabilizing the JavaScript runtime. The project is currently in 0.5.5 release.

Now that we know what Bus is intended for and have a sense of where it is in its growth trajectory, let’s get our hands on Bun!

Install and run a Bun React application

You can install Bun as a native package on any operating system. You can also install it as a global NPM package. It might feel a little odd to install an NPM replacement with NPM, but it sure does make the installation easy.

Listing 1. Install Bun with NPM


$ npm install -g bun
$ bun -v
0.5.5

The bun command is now available on your command line. Let’s use Bun to create a new React app. This is the same as entering: npx create-react-app my-app. If you are familiar with using npx (which runs on NPM/Node) then you will immediately notice how fast using Bun works. Run the command in Listing 2 to start a new project using the create-react-app libraries.

Listing 2. Run create-react-app


$ bun create react ./bun-react
[package.json] Detected React - added "react-refresh"
$ bun install // This happens automatically
[12.00ms] bun install
$ bun bun ./src/index.jsx // this happens automatically
[720.00ms] bun create react

Note that you don't enter the second two commands; they happen automatically as part of the initial command. You may be surprised to observe that the entire command took less than a second including installing the Node modules. 

You can now cd into the bun-react/ directory and start the development server with bun dev. You can then visit the application at localhost:3000, where you’ll see a welcome screen like the one shown in Figure 1.

Bun.js application screen IDG

Figure 1. The Bun React application welcome screen

If you glance at the package.json file, you’ll see it is the same as it would otherwise be, with nothing specific to Bun added. Bun is doing just what NPM would do, but faster.

For an unscientific quick check, I rm -rf’d the /mode_modules directory and reinstalled the dependencies with bun install (four milliseconds) versus npm install (two seconds). 

Bun for edge and serverless deployments

What you have just seen is Bun doing the work of a package manager as well as a script runner. When you did bun dev, you were doing the equivalent of npm run dev. The nicety with Bun is again the speed, but that speed also has implications for other areas. Bun is fast at running the task because it is fast at starting up. Most of the time required to run a task with Node/NPM is spent in starting the Node process itself. 

The fact that Bun is quick to start is an important characteristic in edge and serverless deployments, where the ideal is "scale to zero."  That means you want a system that can spawn nodes to meet demand. Then, when that demand tapers off, it should cheaply drop nodes. A big hurdle to such scalability is the speed at which nodes are able to spin up. So, the ability to rapidly start and execute scripts means Bun is well-suited for edge and serverless computing environments.

Bun for Next, Svelte, and Vue

Bun can do something similar with a Next application, starting with the command: bun create next ./app. To get a list of all the currently available create commands, type bun create. You’ll notice there’s about a dozen supported templates in Bun .0.5.5.

To handle use cases where a built-in loader is not available, Bun.js includes pluggable loaders. This allows for handling files for Svelte or Vue, like .svelte or .vue. You can learn more about Bun's custom loaders here

There is an experimental SvelteKit adapter to run SvelteKit in Bun. This is very much in development, since the Bun API itself is rapidly evolving and SvelteKit depends on those APIs. (The SvelteKit template obtained with bun create doesn’t seem to be working at the moment.)

Bun transpiling and modules

One of Bun’s ambitions is to replace the transpilation phase of building. This is complex because it deals with so many different technologies, from CSS to JSX. These are technologies that are subject to change, and thus to complications like tree shaking and minification.

Bun also has to deal with JavaScript module resolution, which the Bun documentation acknowledges is complex. The complexity is overwhelming to even think about, which is what prevents most people from attempting something like Bun. Rewriting what is already pretty good (Node/NPM) with something even better is a lofty goal.

I’ll refer you again to the Bun roadmap, which includes items related to transpiling, bundling, and module resolution.

Bun as a server

Bun can run WASM binaries on the server. It can also can handle HTTP requests with a built-in API that is similar to a serverless environment. Let’s take a quick look. If we create a file called server.ts and add the code in Listing 3, we can then run it with Bun.

Listing 3. Use Bun as a simple server


export default {
  port: 3000,
  fetch(request: Request) {
    return new Response("Hello InfoWorld");
  }
};

To run the echo server, type bun server.ts. If you browse to localhost:3000, you’ll see the greeting. This works because if Bun finds a default export object with a fetch method, it assumes it’s a server. It wraps this in the Bun.serve API. You can see a simple use of this API in Listing 4. Where appropriate, the APIs found in Bun follow web standards, so the request and response objects are the familiar standards (i.e., Request). Listing 4 uses the Request object to grab the fetched URL and output it.

Listing 4. Using the Bun.serve API


Bun.serve({
  fetch(req) {
    return new Response("You visited: " + JSON.stringify(req.url));
  },
});

Note that Bun's Node APIs (NAPI) are not complete enough to yet run Express; however, there are a number of similar projects for Bun itself. One example is BunRest.

A new approach to server-side JavaScript

The Bun roadmap includes many open to-do's, which provides a sense of the scope and ambition of this project. Bun really looks to become a one-stop shop to perform most server-side JavaScript tasks, including everything from spawning processes to hosting an embedded SQLite instance to running native function with Bun FFI (foreign function interface). 

Instead of the workflow being: I need to do some JavaScript work, so let me go get the runtime and the package manager and download the specific tools to stand up a working environment, followed by the tools for the task at hand, it becomes: let's install bunks and get the tools required for the actual work.

It is also interesting that Bun uses Zig under the hood. Zig is not only a programming language but a package manager and build tool all in one. This is a sensible trend, because in the past we had the goal of creating a working general-purpose language. That was a big enough goal in itself, and then all the required support for development and production was bolted on afterward. 

Today, most of us understand that a language will need those things, especially a package manager and a build tool. It makes sense that designers are building them into the language from the ground up. From the 10,000-foot view, it looks like we are seeing a new generation of programming tools that are bootstrapping tools and utilities at a higher level based on past learnings. Now is a very interesting time to be building software.

Want to keep learning about Bun? Start with this list of interesting projects in the Bun.js ecosystem.

Copyright © 2023 IDG Communications, Inc.