Installing Bun.
Table of contents
- TL;DR.
- An Introduction.
- The Big Picture.
- Prerequisites.
- Installing Bun.
- Upgrading and Uninstalling Bun.
- Quickstart1.
- Hello, Bun as a Hot Reload File.
- Hello, Bun as a Hot Reload Script.
- Installing a Package.
- TypeScript.
- DOM Types.
- Templates.
- From a GitHub Template.
- From a Local Template.
- Setup Logic for Templates.
- CLI Flags.
- Environment Variables.
- How bun create Works.
- Ecosystem.
- The Results.
- In Conclusion.
TL;DR.
Bun is a fast JavaScript runtime with TypeScript support. It is designed to be a drop-in replacement for Node.js. The Bun CLI has features like a runtime, bundler, test runner, package runner, package manager, and APIs. This post:
Covers Bun's installation,
Includes a quick start guide,
Introduces the package manager,
Describes TypeScript integration for it's APIs, and
Suggests how templates can be built, and used, for custom initialisations.
Attribution:
An Introduction.
Bun is fast because it is written in a low-level programming language called Zig. Unlike other runtimes that use Chrome V8, Bun is powered by JavaScriptCore, the same JavaScript engine that is used by Apple Safari.
The purpose of this post is to introduce the Bun JS runtime.
Bun 1.0 was released on Friday 8th September 2023 and the CLI includes:
TypeScript support,
A runtime (bun run),
A bundler (bun build),
A test runner (bun test),
A package runner (bunx),
A package manager (bun install), and
A small (but growing) collection of useful APIs.
I will touch on each of these topics as per the official documentation.
The Big Picture.
A JavaScript runtime allows JS code to run on the server. The main advantage lies in the reduction of context switching: JavaScript running on the server and in the browser means there is ONE programming language involved during development. At least, that was an intent when Node was launched in 2009.
The introduction of ECMAScript 2015, colloquially called ES6, introduced new JavaScript features like const, let, arrow functions, and other features. Further developments would threaten jQuery, although it is still a very popular library.
Each year, ECMA would issue updated JavaScript standards and each browser vendor would release new features to match. As JavaScript continued to evolve, modern development practices also changed. These transitions were due to new tools and practices that evolved, hand in hand, with the annual ECMA releases.
The current range of development tools is wide and varied. Also, it is common to see a new framework, language, library, or runtime launch each week. For instance, Bun 1.0 and the Mojo Local Installer were unveiled on the same day!!
Suffice it to say that software engineering is a continually evolving field.
Prerequisites.
A Linux-based distro (I use Ubuntu),
A remote 'bun' container on my homelab as per these instructions.
A remote connection from my workstation as per these instructions.
A hardened 'bun' container on my homelab as per these instructions.
NOTE: The purpose of using containers is to isolate any running processes from other containers and the host system.
Installing Bun.
I connect VS Code to the 'bun' container (which requires the Microsoft 'Remote - SSH' extension and the 'F1 -> Remote-SSH: Open SSH Host...' command.)
From the VS Code terminal (use
CTRL + ~
to show/hide the terminal) I install the unzip package:
sudo apt install unzip -y
- I install Bun:
curl -fsSL https://bun.sh/install | bash
- I set the source:
source $HOME/.bashrc
- I check the version:
bun -v
Upgrading and Uninstalling Bun.
- I can, if needed, upgrade Bun:
bun upgrade
- I can, if needed, uninstall Bun:
rm -rf ~/.bun
Quickstart1.
- I make a 'quickstart1' directory:
mkdir quickstart1
- I change to that directory:
cd quickstart1
- I scaffold a new project (and accept all the defaults):
bun init -y
Hello, Bun as a Hot Reload File.
- I add the following to 'index.ts' file and save the changes:
const server = Bun.serve({
port: 3000,
fetch(req) {
return new Response("Hello, from Bun.");
},
});
console.log(`Listening on http://localhost:${server.port} ...`);
- I run the 'index.ts' file from the terminal:
bun --hot run index.ts
I open a browser on my workstation and point it to 192.168.188.52:3000.
I stop the server (CTRL + C).
Hello, Bun as a Hot Reload Script.
I open the 'package.json' file.
I add the following:
"scripts": {
"start": "bun --hot run index.ts"
},
- I run the script from the terminal:
bun run start
I open a browser on my workstation and point it to 192.168.188.52:3000.
I stop the server (CTRL + C).
Installing a Package.
- I install the 'figlet' package:
bun add figlet
- FOR TYPESCRIPT USERS ONLY:
bun add -d @types/figlet
- I replace the contents of the 'index.ts' file:
import figlet from "figlet";
const server = Bun.serve({
fetch() {
const body = figlet.textSync("Hello, from Figlet Bun!");
return new Response(body);
},
port: 3000,
});
- I use the 'package.json' script command to run the server.
bun run start
I open a browser on my workstation and point it to 192.168.188.52:3000.
I stop the server (CTRL + C).
TypeScript.
- I install the TypeScript definitions for the built-in APIs:
bun add -d bun-types
- I add the following to my 'tsconfig.json' file:
{
"compilerOptions": {
"types": ["bun-types"],
}
}
NOTE: I can now reference the
Bun
global in my TypeScript files.
- I add the following to the bottom of the 'index.ts' file:
console.log(`Bun version: ` + Bun.version);
- The following 'tsconfig.json' entries are a set of recommended
compilerOptions
for a Bun project:
{
"compilerOptions": {
// add Bun type definitions
"types": ["bun-types"],
// enable latest features
"lib": ["esnext"],
"module": "esnext",
"target": "esnext",
// if TS 5.x+
"moduleResolution": "bundler",
"noEmit": true,
"allowImportingTsExtensions": true,
"moduleDetection": "force",
// if TS 4.x or earlier
// "moduleResolution": "nodenext",
"jsx": "react-jsx", // support JSX
"allowJs": true, // allow importing `.js` from `.ts`
"esModuleInterop": true, // allow default imports for CommonJS modules
// best practices
"strict": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
}
}
NOTE: If I run
bun init
in a new directory, thistsconfig.json
will be generated automatically.
- I use the 'package.json' script command to run the server.
bun run start
I open a browser on my workstation and point it to 192.168.188.52:3000.
I stop the server (CTRL + C).
DOM Types.
- If I need to add DOM types to my project, I add the following triple-slash directives at the top of any TypeScript file in my project:
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
Templates.
In VS Code, I close the 'quickstart1' directory using File > Close Folder [CTRL + K F].
In the VS Code terminal, I make a 'quickstart2' directory in the home (~) directory:
mkdir quickstart2
In VS Code, I click the blue 'Open Folder' button, select the 'quickstart2' directory, and click the blue 'OK' button.
I open the VS Code terminal with CTRL + ~.
In the terminal, I change to the 'quickstart2' directory:
cd quickstart2
- I scaffold an empty project with the interactive
bun init
command (and I use the -y flag to auto-accept the default settings):
bun init -y
- I template a new Bun project with
bun create
:
bun create <template> [<destination>]
From a GitHub Template.
- I can download the contents of a GitHub repo to disk:
bun create <user>/<repo>
- I can also use the following command:
bun create github.com/<user>/<repo>
- I can optionally specify a name for the destination folder (otherwise the repo name will be used):
bun create <user>/<repo> mydir
- I can also use the following command:
bun create github.com/<user>/<repo> mydir
From a Local Template.
My templates should live in one of the following directories:
$HOME/.bun-create/<name>
(for global templates), and<project root>/.bun-create/<name>
(for project-specific templates).
NOTE: I can customize the global template path by setting the
BUN_CREATE_DIR
environment variable.
- I navigate to
$HOME/.bun-create
:
cd $HOME/.bun-create
- I create a new directory with the desired name of my template:
mkdir foo
- I change to the new directory:
cd foo
- I create a
package.json
file:
touch ./package.json
- I open the file with Nano:
sudo nano package.json
- I add the following, save the changes, and exit Nano:
{
"name": "foo"
}
- I create a new directory:
mkdir ~/quickstart3
- I change to that directory:
cd ~/quickstart3
- I verify that Bun is correctly finding my local template:
bun create foo
Setup Logic for Templates.
I can specify pre- and post-install setup scripts in the "bun-create"
section of my local template's package.json
file:
{
"name": "@bun-examples/simplereact",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"bun-create": {
"preinstall": "echo 'Installing...'", // a single command
"postinstall": ["echo 'Done!'"], // an array of commands
"start": "bun run echo 'Hello world!'"
}
}
NOTE: After cloning a template,
bun create
will automatically remove the"bun-create"
section frompackage.json
before writing it to the destination folder.
CLI Flags.
Flag | Description |
--force | Overwrite existing files |
--no-install | Skip installing node_modules & tasks |
--no-git | Don’t initialize a git repository |
--open | Start & open in-browser after finish |
Environment Variables.
Name | Description |
GITHUB_API_DOMAIN | If you’re using a GitHub enterprise or a proxy, you can customize the GitHub domain Bun pings for downloads |
GITHUB_API_TOKEN | This lets bun create work with private repositories or if you get rate-limited |
How bun create
Works.
When you run bun create ${template} ${destination}
, here’s what happens:
IF remote template
GET
registry.npmjs.org/@bun-examples/${template}/latest
and parse itGET
registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz
Decompress & extract
${template}-${latestVersion}.tgz
into${destination}
- If there are files that would overwrite, warn and exit unless
--force
is passed
- If there are files that would overwrite, warn and exit unless
IF GitHub repo
Download the tarball from GitHub’s API
Decompress & extract into
${destination}
- If there are files that would overwrite, warn and exit unless
--force
is passed
- If there are files that would overwrite, warn and exit unless
ELSE IF local template
Open local template folder
Delete destination directory recursively
Copy files recursively using the fastest system calls available (on macOS
fcopyfile
and Linux,copy_file_range
). Do not copy or traverse intonode_modules
folder if exists (this alone makes it faster thancp
)Parse the
package.json
(again!), updatename
to be${basename(destination)}
, remove thebun-create
section from thepackage.json
and save the updatedpackage.json
to disk.IF Next.js is detected, add
bun-framework-next
to the list of dependenciesIF Create React App is detected, add the entry point in /src/index.{js,jsx,ts,tsx} to
public/index.html
IF Relay is detected, add
bun-macro-relay
so that Relay works
Auto-detect the npm client, preferring
pnpm
,yarn
(v1), and lastlynpm
Run any tasks defined in
"bun-create": { "preinstall" }
with the npm clientRun
${npmClient} install
unless--no-install
is passed OR no dependencies are in package.jsonRun any tasks defined in
"bun-create": { "preinstall" }
with the npm clientRun
git init; git add -A .; git commit -am "Initial Commit";
Rename
gitignore
to.gitignore
. NPM automatically removes.gitignore
files from appearing in packages.If there are dependencies, this runs in a separate thread concurrently while node_modules are being installed
Using libgit2 if available was tested and performed 3x slower in microbenchmarks
Ecosystem.
The following is a collection of guides for using various tools and frameworks with Bun.
App Development.
Ecosystem for Bun | Native Support and Site |
Nuxt | Yes |
Prisma | Yes |
React and JSX | Yes |
SveltKit | Yes (similar to Next and Nuxt) |
A Discord bot | Yes |
Astro | No |
Next.js | No |
Remix | No |
SolidStart | No |
Vite | No |
Server Development.
Ecosystem | Description |
Hono | Hono is a lightweight ultrafast web framework designed for the edge. |
Elysia | Elysia is a Bun-first performance-focused web framework. |
Express | Express provides a thin layer of fundamental web application features. |
StricJS | StricJS is a Bun framework for building high-performance web applications and APIs. |
MongoDB and Mongoose | MongoDB and Mongoose work out of the box with Bun. |
SSR for React | Server-Side Rendering (unlike Static Site Generation, Client-Side Rendering, and Incremental Site Rendering). |
The Results.
Bun offers a powerful and efficient alternative to Node.js, providing a range of features such as TypeScript support, a bundler, test runner, package runner, and package manager. By following the installation and quickstart guide, I can easily adopt Bun and harness its potential to improve my JavaScript projects.
In Conclusion.
Although Bun has finally released version 1.0, it has a long way to go if it wants to match the features provided by Node. Initial support for Bun appears very high but as more developers evaluate its features, shortcomings are bound to appear. The real test involves how quickly the developers can address these failings.
Until next time: Be safe, be kind, be awesome.
Subscribe to my newsletter
Read articles from Brian King directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Brian King
Brian King
Thank you for reading this post. My name is Brian and I'm a developer from New Zealand. I've been interested in computers since the early 1990s. My first language was QBASIC. (Things have changed since the days of MS-DOS.) I am the managing director of a one-man startup called Digital Core (NZ) Limited. I have accepted the "12 Startups in 12 Months" challenge so that DigitalCore will have income-generating products by April 2024. This blog will follow the "12 Startups" project during its design, development, and deployment, cover the Agile principles and the DevOps philosophy that is used by the "12 Startups" project, and delve into the world of AI, machine learning, deep learning, prompt engineering, and large language models. I hope you enjoyed this post and, if you did, I encourage you to explore some others I've written. And remember: The best technologies bring people together.