はじめてのBun
Bun は JavaScript のランタイムです。Node.js の代替のランタイムで JavaScript をブラウザ上ではなくサーバ上で実行したい場合に利用することができます。ランタイムだけの機能ではなくパッケージマネージャー、バンドラー、テストランナーの機能も備えています。つまり Bun をインストールしただけでパッケージをインストールし、ビルドを行い、テストを実行することができます。その上高速に動作します。さらに Bun をインストールした直後から追加のライブラリをインストールすることなく TypeScript でコードを記述し実行することができます。Bun には開発に必要となるツールがすべて含まれているのが Node.js との大きな違いです。
これまで Bun を利用したことのない人を対象にどのような機能を持っているのか簡単なコードを利用して説明を行っています。一部 Node.js における設定について説明を行なっている箇所がありますが Node.js の箇所はスキップ可能で Bun を利用するために Node.js の知識は必須ではありません。
目次
インストール
Bun を mac OS でインストールする方法には curl, npm, Homebrew などがありますが本文書では curl を利用してインストールを行います。
ユーザのホームディレクトリで”curl -fsSL https://bun.sh/install | bash”を実行します。実行が完了すると実行したホームディレクトリの下の”~/.bun/bin”に bun, bunx ファイルが作成されます。
% curl -fsSL https://bun.sh/install | bash
######################################################################## 100.0%
bun was installed successfully to ~/.bun/bin/bun
Added "~/.bun/bin" to $PATH in "~/.zshrc"
To get started, run:
exec /bin/zsh
bun --help
インストール後はインストールメッセージに表示されている通り”exec /bin/zsh”を実行します。
% exec /bin/zsh
実行後は”~/.bun/bin”以下にある bun ファイルへのパスが設定されているため bun コマンドを実行することができます。Bun のインストールはこれで完了です。
% bun help
Bun: a fast JavaScript runtime, package manager, bundler and test runner. (1.0.7)
run ./my-script.ts Run JavaScript with Bun, a package.json script, or a bin
test Run unit tests with Bun
x bun-repl Install and execute a package bin (bunx)
repl Start a REPL session with Bun
init Start an empty Bun project from a blank template
create next-app Create a new project from a template (bun c)
install Install dependencies for a package.json (bun i)
add zod Add a dependency to package.json (bun a)
remove browserify Remove a dependency from package.json (bun rm)
update tailwindcss Update outdated dependencies
link Link an npm package globally
unlink Globally unlink an npm package
pm More commands for managing packages
build ./a.ts ./b.jsx Bundle TypeScript & JavaScript into a single file
upgrade Get the latest version of Bun
bun --help Show all supported flags and commands
Learn more about Bun: https://bun.sh/docs
Join our Discord community: https://bun.sh/discord
バージョンアップ
Bun のバージョンアップを行いたい場合は bun upgrade コマンドで行うことができます。
% bun upgrade
Congrats! You're already on the latest version of bun (which is v1.0.7)
プロジェクトの作成
Bun の動作確認を行うためにプロジェクトを作成します。任意の場所にプロジェクトディレクトリを作成します。ここでは bun-test という名前にしています。任意の名前をつけてください。
% mkdir bun-test
作成した bun-test ディレクトリに移動して bun init コマンドを実行します。package 名と entry point のファイル名を聞かれますがそのまま Enter キーを押します。
% cd bun-test
% bun init
bun init helps you get started with a minimal project and tries to guess sensible defaults. Press ^C anytime to quit
package name (bun-test):
entry point (index.ts):
Done! A package.json file was saved in the current directory.
+ index.ts
+ .gitignore
+ tsconfig.json (for editor auto-complete)
+ README.md
To get started, run:
bun run index.ts
bun init コマンドを実行後のディレクトリには node_modules ディレクトリ以外に.gitignore, index.ts, package.json, README.md, tsconfig.json ファイルが作成されます。
はじめての Bun の動作確認
index.ts ファイルには下記のコードが記述されています。
console.log("Hello via Bun!");
デフォルトでは拡張子を見て分かる通り TypeScript ファイルとなっていますが index.js に変更して JavaScript としてコードを記述することも可能です。
bun run コマンドで index.ts ファイルを指定することで実行することができます。
% bun run index.ts
Hello via Bun!
冒頭で説明した通り、Bun では追加設定なしで TypeScript が利用できるか確認するために index.ts ファイルのコードを変更します。greeting 関数の引数に型を設定しています。
const greeting = (message: string) => {
console.log(message);
};
greeting('Hello!');
実行するとターミナルに”Hello!”が表示されます。TypeScript もプロジェクト作成直後から利用できることが確認できました。
% bun run index.ts
Hello!
Node.js で TypeScript を利用したい場合は Node.js のプロジェクトを作成後、追加で TypeScript のパッケージのインストール, tsconfig.json ファイルの作成などを行う必要があります。
開発を行う際はコードの更新を行うたびにコマンドを再実行しないための機能があればとても便利です。Bun ではデフォルトからファイル更新の検知機能が備わっているので watch モードで実行することでファイルの更新を検知してくれます。
% bun --watch index.ts
HTTP サーバの設定
Bun ではシンプルな HTTP サーバの設定を行うことができます。確認するために以下のコードを記述します。
const server = Bun.serve({
port: 3000,
fetch(request) {
return new Response("Welcome to Bun!");
},
});
console.log(`Listening on localhost: ${server.port}`);
ポート番号 3000 で起動してリスエストがあると Response で”Welcome to Bun!”という文字列を戻します。
更新した index.ts ファイルを watch コードで実行します。
% bun --watch index.ts
Listening on localhost: 3000
ブラウザから http://localhost:3000 にアクセスするとブラウザ上には”Welcome to Bun!”が表示されます。
URL を http://localhost:3000/test に変更しても同様に”Welcome to Bun!”が表示されます。
watch モードを利用して実行しているので index.ts ファイルを更新してブラウザのリロードを行うと更新が反映されます。watch モードを利用していない場合にはブラウザのリロードを行っても更新は反映されません。
watch モードの他に hot モードというものもあります。hot モードで実行するとファイルを更新するとブラウザ上の更新まで行ってくれると思うかもしれませんがブラウザ上の更新は行われません。watch モードの場合は更新を行うとファイル全体の再読み込みを行い hot モードの場合は更新部分のみが反映されるという違いがあります。
ルーティングの設定
現在の index.ts ファイルでは localhost:3000 であればどのような URL(例:localhost:3000/test)でも同じ Response が戻されました。ルーティングを行うためにはアクセスのあった URL の情報を取得して分岐を行います。
const server = Bun.serve({
port: 3000,
fetch(request) {
const url = new URL(request.url);
if (url.pathname === '/') return new Response('Home page!');
if (url.pathname === '/blog') return new Response('Blog!');
return new Response('404!');
},
});
console.log(`Listening on localhost: ${server.port}`);
ブラウザから localhost:3000 にアクセスすると”Home page!”も文字列が表示され, localhost:3000/blog にアクセスすると”Blog!”の文字列が表示されます。それ以外の URL にアクセスすると”404!”の文字列が表示されます。
エラーハンドリング
/blog にアクセスがあった場合に Error が throw された場合の動作確認を行います。
const server = Bun.serve({
port: 3000,
fetch(request) {
const url = new URL(request.url);
if (url.pathname === '/') return new Response('Home page!');
if (url.pathname === '/blog') throw new Error('woops!');
return new Response('404!');
},
});
console.log(`Listening on localhost: ${server.port}`);
ブラウザから http://localhost:3000/blog にアクセスすると Bun のデフォルトのエラー画面が表示されます。
Bun のデフォルトのエラー画面を表示させるのではなくエラーが throw された場合のエラーハンドリングは error handler で行うことができます。
const server = Bun.serve({
port: 3000,
fetch(request) {
const url = new URL(request.url);
if (url.pathname === '/') return new Response('Home page!');
if (url.pathname === '/blog') throw new Error('woops!');
return new Response('404!');
},
error(error) {
return new Response(`<pre>${error}\n${error.stack}</pre>`, {
headers: {
'Content-Type': 'text/html',
},
});
},
});
console.log(`Listening on localhost: ${server.port}`);
ブラウザから http://localhost:3000/blog にアクセスすると Bun のデフォルトのエラー画面ではなく設定した内容でエラーが表示されます。
HTTP サーバ内でエラーが発生した場合はこのようにエラーハンドリングを行うことができます。
Express の設定
Bun を利用してシンプルな HTTP サーバが動作することが確認できました。Node.js で人気の Web フレームワークである Express を Bun でも利用できるか確認していきます。Express だけではなく Node.js パッケージも Bun で利用できることも確認します。また ES Modules でも CommonJS Modules の記述方法も Bun では利用できることも確認します。
Node.js で “npm install” コマンドを利用してパッケージのインストールを行いますが Bun では”bun install”コマンドを利用することができます。
% bun install express
bun add v1.0.7 (b0393fba)
installed express@4.18.2
62 packages installed [1068.00ms]
“bun add”コマンドでもインストールが可能です。
% bun add express
bun add v1.0.7 (b0393fba)
installed express@4.18.2
62 packages installed [851.00ms]
Node.js ではパッケージのインストールには npm 以外にも yarn, pnpm コマンドを利用することができます。
インストール後に package.json ファイルを見るとインストールした express の情報を確認することができます。
{
"name": "bun-test",
"module": "index.ts",
"type": "module",
"devDependencies": {
"bun-types": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
"express": "^4.18.2"
}
}
express の型情報もインストールしておきます。
% bun install --dev @types/express
bun add v1.0.7 (b0393fba)
installed @types/express@4.17.20
13 packages installed [984.00ms]
index.ts ファイルを更新します。
import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
“bun —hot index.ts”コマンドを実行してブラウザから http://localhost:3000 にアクセスすると”Hello World!”が戻されます。
index.ts ファイルでは import 文を利用していましたが Bun では特別な設定なしで require も利用することができます。Bun は ES Modules も CommonJS Modules もどちらもサポートしています。
// import express from 'express';
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
環境変数の設定
Node.js では環境変数を利用する場合に dotenv ライブラリなどを利用しますが Bun では追加のライブラリを利用することなく環境変数を利用することができます。
.env ファイルを作成して環境変数の PORT を設定します。
PORT=3000
index.ts ファイルで環境変数を利用する際は process.env の後に設定した環境変数の名前を指定します。
import express from 'express';
const app = express();
const port = process.env.PORT;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
process.env 以外に Bun.env も利用することができます。
const port = Bun.env.PORT;
ファイル I/O
ファイルへの書き込み
ファイルへの書き込みを行いたい場合には Bun が持つ API である Bun.write を利用することができます。第一引数にはファイルパス、第二引数にファイルに書き込みたい内容を指定します。
const message = 'Hello World';
await Bun.write('test.txt', message);
実行すると index.ts ファイルと同じディレクトリに test.txt ファイルが作成され”Hello World”が記述されています。
Bun では Node.js のコア API も利用することができるので fs モジュールを利用して書き込みを行うこともできます。
import fs from 'fs';
const content = 'Hello World by fs Modules';
fs.writeFile('test2.txt', content, (err) => {
if (err) {
console.error(err);
}
});
ファイルからの読み込み
Bun ではファイルからの読み込みは Bun.file で行うことができます。Bun.file の引数にファイルのパスを指定すると BunFile インスタンスが作成されます。
const file = Bun.file('text.txt');
const message = await file.text();
console.log(message);
Bun.file から戻される Bunfile インススタンスは name, type プロパティや exists メソッドを持っているのでファイルの情報、存在チェックを行うことができます。
import { BunFile } from 'bun';
const file = Bun.file('text.txt') as BunFile;
console.log(await file.exists());
console.log(file.type);
console.log(file.name);
// Result
true
text/plain;charset=utf-8
text.txt
このように Bun には独自の API を持っているだけではなく Node.js のコア API も利用できることがわかりました。その他に bun:sqlite を利用することで SQLite データベースを操作することや Bun.password.hash で文字列をハッシュ
SQLite
Bun では bun:sqlite を利用することで SQLite データベースを利用することができます。
データベースファイルを作成して users テーブルを作成して 1 件ユーザ情報を登録して users テーブルからデータを取得するコードを index.ts ファイルに記述します。
import { Database } from 'bun:sqlite';
const db = new Database('mydb.sqlite', { create: true });
db.run(
`CREATE TABLE IF NOT EXISTS users (id Integer Primary Key Autoincrement, name Text, email Text Unique)`
);
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
insertUser.run('John Doe', 'john@example.com');
const getUsers = db.prepare('SELECT * from users');
console.log(getUsers.all());
bun:sqlite から import した Database の引数にファイル名を指定します。第二引数に create を指定することでデータベースファイルが存在しない場合のみ作成する設定にしています。
users テーブルは id, nama, email は持ち id は AutoIncrement を設定して、email には Unique 制約を設定しています。id は自動で番号が設定され同じ email アドレスを登録することはできません。
実行は bun run コマンドで行います。
% bun run index.ts
[
{
id: 1,
name: "John Doe",
email: "john@example.com"
}
]
開発用に追加のインストールなしで SQLite データベースの操作を行うことができます。
Build(ビルド) – React 利用
Bun には Bundler も含まれているため、webpack や esbuild などのパッケージをインストールすることなしでビルドを行うことができます。
ここでは React を利用して Bun のビルドの動作確認を行います。
React を利用するためには react, react-dom パッケージのインストールが必要となります。
% bun install react react-dom
[0.77ms] ".env"
bun add v1.0.7 (b0393fba)
installed react@18.2.0
installed react-dom@18.2.0
5 packages installed [980.00ms]
TypeScript を利用するので react, react-dom の型情報のインストールも行います。
% bun install --dev @types/react
% bun install --dev @types/react-dom
index.ts ファイルの名前を index.tsx ファイルに変更して以下のコードを記述します。
import ReactDOM from 'react-dom/client';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<App />
);
上記のコードを記述すると document に関して”Cannot find name ‘document’. Do you need to change your target library? Try changing the ‘lib’ compiler option to include ‘dom’.ts(2584)“のエラーメッセージが表示されます。エラーメッセージの対応策としてドキュメントのDOM Typesに記述されているので index.tsx ファイルを更新します。
// <reference lib="dom" />
/// <reference lib="dom.iterable" />
import ReactDOM from 'react-dom/client';
import App from './App';
ReactDOM.createRoot(document.getElementById('root') as HTMLDivElement).render(
<App />
);
tsconfig.json ファイルの compilerOptions の lib オプションに”DOM”, “DOM.Iterable”を追加してもエラーは解消しません。
document に関するエラーが解消したら、index.tsx ファイルの中で App コンポーネントを import しているので App.tsx ファイルを同じディレクトリ内に作成して以下のコードを記述します。
import { useEffect } from 'react';
const App = () => {
useEffect(() => {
console.log('test');
}, []);
return <h1>Hello World</h1>;
};
export default App;
bun build コマンドを利用してビルドを行います。
% bun build ./index.tsx --outdir ./out
./index.js 962.18 KB
[92ms] bundle 11 modules
実行するとビルドに成功して out ディレクトリの下に index.js ファイルが作成されます。
作成された index.js ファイルを html ファイルから読み込むために index.html ファイルを作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>React App</title>
<script type="module" src="./out/index.js" defer></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
本文書の動作確認では VS Code を利用しているので Extensions の Live Server を利用して index.html ファイルを閲覧します。ブラウザ上には”Hello World!”が表示されブラウザのコンソールには文字列”test”が表示されます。
Live Server を利用しない場合は”bunx serve”で開発サーバを起動することで動作を確認することができます。
% bunx serve
┌───────────────────────────────────────────┐
│ │
│ Serving! │
│ │
│ - Local: http://localhost:3000 │
│ - Network: http://192.168.1.101:3000 │
│ │
│ Copied local address to clipboard! │
│ │
└───────────────────────────────────────────┘
React を利用して Bun の Build の利用方法を理解することができました。
テスト
Bun ではテストも Jest, Vitest などの追加のパッケージをインストールすることなく利用することができます。
ドキュメントに記載されているシンプルなテストコードを利用して動作確認します。
プロジェクトディレクトリ直下に math.test.ts ファイルを作成して以下のコードを記述します。
import { expect, test } from "bun:test";
test("2 + 2", () => {
expect(2 + 2).toBe(4);
});
テストの実行は”bun test”コマンドで実行することができます。
% bun test
bun test
bun test v1.0.7 (b0393fba)
math.test.ts:
✓ 2 + 2 [0.76ms]
1 pass
0 fail
1 expect() calls
Ran 1 tests across 1 files. [141.00ms]
ここまでの動作確認で Bun の基本的な機能を確認することができました。