今回はBun + TypeScriptにおける実行環境を構築する手順を備忘録としてまとめていきます。
最終的にはテンプレートリポジトリとして使いまわしていくための準備を実施していきます。
また、Node.jsではありますが、以前npm編の構築手順についてもまとめていますので、良かったら参考にしてください。
Bunについて
Bunは、JavaScript、TypeScriptのための新世代ランタイムおよびパッケージマネージャーです。2023年9月に安定版(v1.0.0)がリリースされました。
JavaScriptとTypeScriptプロジェクトの開発、テスト、実行、バンドルはすべてBunで対応可能。Bunは、バンドル、テストランナー、Node.js互換のパッケージマネージャを備えた、スピードを追求したオールインワンのJavaScriptランタイム&ツールキットです。
https://bun.sh
Bunは特に、開発効率と実行速度を重視する開発者に向けて設計されており、Node.jsに代わるランタイムとして開発されています。Bunを使用することで、プロジェクトのセットアップからデプロイメントまでの全工程がスムーズになり、開発サイクルを短縮できるため、多くの開発者にとって新たな選択肢となっています。
主な特長は以下の通りです。
- インストールから実行まで、非常に高速なパフォーマンスを提供。BunがRust言語で書かれ、最適化された実行エンジンを持っている
- GitHubリポジトリとシームレスに連携し、簡単に依存関係の管理やプロジェクトのセットアップが可能
- JavaScriptやTypeScriptのコードを即座にコンパイルし、自動的に依存関係を解決。これにより、開発プロセスが大幅に効率化
- TypeScriptにもデフォルトで対応
その他、詳しい情報は公式Docを参照
今回はランタイム、パッケージマネージャーとして使用しますが、既存プロジェクトに対してもBunに切り替えて使用可能
という声もよく聞きます
実装の流れ
bunコマンドを利用するための準備
ベースとなるアプリケーションフォルダの作成
簡単なAPIサーバーを起動させて動作確認
開発環境
- Bun:
v1.0.29
- TypeScript:
v5.4.2
- OS:
MacOS Ventura
構築手順
Bunのインストール
公式Docの通りにインストールを実施します。
$ curl -fsSL <https://bun.sh/install> | bash
# 一度ターミナルを再起動
$ bun --version
1.0.29
他にもbrewコマンドやnpmコマンドなどでインストールが可能
# brewコマンドの場合
$ brew install oven-sh/bun/bun
# npmコマンドの場合
$ npm install -g bun
bun initで環境構築
bun initの実行
bun init
コマンドでアプリケーションの土台となるファイルを生成していきます。
$ bun init 21:48:19
bun init helps you get started with a minimal project and tries to guess sensible defaults. Press ^C anytime to quit
package name (ts-bun-template):
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
ディレクトリ構成
実行後のディレクトリ構成は以下
.
├── node_modules/
├── README.md
├── bun.lockb
├── index.ts
├── package.json
└── tsconfig.json
package.json
@types/bun
によって、BunのAPIに対する型定義が有効になります。
{
"name": "ts-bun-template",
"module": "index.ts",
"type": "module",
"devDependencies": {
"@types/bun": "^1.0.8"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
tsconfig.jsonの中身
デフォルトでbunが推奨する設定が反映されています。
基本的には、最新のECMAScript標準に準拠したコードを使用するようになっており、モジュールの形式にについてもESモジュール形式となっております。
詳しくは公式Docへ
{
"compilerOptions": {
// Enable latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}
サーバー起動にて動作検証
実行ファイルであるindex.tsは以下のように実装されているため、実行するとBunが3000番でサーバーを起動するようになっています。
index.ts
const server = Bun.serve({
port: 3000,
fetch(req) {
return new Response("Bun!");
},
});
console.log(`Listening on http://localhost:${server.port} ...`);
bun run
コマンドで実行していきます。
$ bun run index.ts
Listening on http://localhost:3000 ...
curlコマンドで該当URLを叩いてあげると、「Bun!」と表示されるはずです。
$ curl http://localhost:3000
Bun!
一旦これで最低限の構築作業は完了となります。これをもとに要件に合わせて実装を進めていける状態です。
※ 細かい設定やLinterなどの設定は別途追加実装してください
bunコマンドでよく使うもの
ついでにBunコマンドでよく使うものを整理して終わりにしたいと思います。
bunコマンドの一覧
コマンド | 機能 | 説明 | 使用例 |
---|---|---|---|
bun run | ランタイム | 高速なJavaScriptランタイム。Node.jsの代替として設計されている。 | bun run index.tsx |
bun install / add / remove / update | パッケージマネージャー | Node.js互換のパッケージマネージャー。依存関係のインストールや管理が可能。 | bun install <pkg> |
bun build | バンドラー | JavaScriptとTypeScriptのコードをバンドルし、最小化する。esbuildに触発されたプラグインAPIを提供。 | bun build ./index.tsx |
bun test | テストランナー | Jest互換の高速テストランナー。TypeScriptとJSXをサポートし、スナップショットテストやDOMテストが可能。 | bun test |
bunx | パッケージランナー | npxやyarn dlxに相当する機能。パッケージの自動インストールと実行が可能。パッケージをインストールすることなく、一時的に実行ができる機能。 | bunx eslint –fix src/ |
bun pm | ユーティリティセット | パッケージマネージャーを操作するためのユーティリティセットを提供することが可能。 | bun pm ls |
bun pmコマンド
bun pm
コマンドはBunのパッケージマネージャーを操作するためのユーティリティセットを提供するコマンドです。
現在のプロジェクトにインストールされている依存関係とそのバージョンを表示したり、キャッシュの確認&クリアができたりします。
詳しくは公式Docを確認
インストールされているパッケージのバージョン確認
$ bun pm ls
/Users/gorida/Github/ts-bun-template node_modules (7)
├── @types/bun@1.0.8
└── typescript@5.4.2
$ bun pm ls --all
/Users/gorida/Github/ts-bun-template node_modules
├── @types/bun@1.0.8
├── @types/node@20.11.25
├── @types/ws@8.5.10
├── bun-types@1.0.29
├── typescript@5.4.2
└── undici-types@5.26.5
キャッシュの確認とクリア
$ bun pm cache
/Users/gorida/.bun/install/cache
$ bun pm cache rm
Cache directory deleted:
/Users/daikigoto/.bun/install/cache
package.jsonに起動コマンド追加する
サーバー起動時のコマンドをscripts
に追加していきます。
合わせて、watch
オプションとhot
オプションについても使えるように追加します。
{
"name": "ts-bun-template",
"module": "index.ts",
"type": "module",
"scripts": {
"start": "bun run index.ts",
"start:watch": "bun run --watch index.ts",
"start:hot": "bun run --hot index.ts"
},
"devDependencies": {
"@types/bun": "^1.0.8"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
watchオプションとhotオプションの違い
watch
オプションとhot
オプションは、バックエンド開発時のコード変更を検知してサーバーを再起動/ホットリロードする機能ですが、動作には違いがあります。
※ 詳しくは公式Docを確認してください
watchオプション
- ファイルの変更を検知すると、サーバープロセスを完全に再起動する
- サーバーの状態がリセットされ、メモリ上のデータなどは初期化される
- コード変更後、新しいプロセスが起動するまでリクエストは処理されない
hotオプション
- ファイルの変更を検知すると、サーバープロセスをホットリロードする
- プロセスを再起動せずに新しいコードをロードするのみ
- メモリ上の状態が維持されるため、サーバーの状態が継続される
- ホットリロード中もリクエストは継続して処理される
watchオプションとhotオプションの違いを検証
試しにindex.ts
を以下のように実装し、挙動の違いを確認していきます。
declare global {
var items: string[];
}
globalThis.items ??= []; // itemsが未定義の場合、空の配列で初期化
const server = Bun.serve({
port: 3000,
fetch(req) {
// リクエストごとに配列にアイテムを追加
globalThis.items.push(`Item${globalThis.items.length + 1}`);
return new Response(`Items: ${globalThis.items.join(", ")}`);
},
});
console.log(`Listening on http://localhost:${server.port} ...`);
まずはwatchオプションから見ていきます。
$ curl http://localhost:3000
Items: Item1
$ curl http://localhost:3000
Items: Item1, Item2
$ curl http://localhost:3000
Items: Item1, Item2, Item3
#ここでindex.tsのconsole.log内を更新
#サーバーが再起動される
$ curl http://localhost:3000
(更新後)Items: Item1
#Items内が初期化されている
配列が初期化されている(=メモリが初期化されている)ため、初期状態から開始されていることがわかります。
次にhotオプションです。
$ curl http://localhost:3000
Items: Item1
$ curl http://localhost:3000
Items: Item1, Item2
$ curl http://localhost:3000
Items: Item1, Item2, Item3
#ここでindex.tsのconsole.log内を更新
$ curl http://localhost:3000
(更新後)Items: Item1, Item2, Item3, Item4
#Items内が継続してItem4が追加されている
配列がそのまま(メモリが保持されている)のため、新たにItemが追加されています。
バックエンド開発では、ホットリロードによるメモリリークのリスクや、状態の不整合などの問題が発生する可能性があるため、watchオプションを使うことが一般的です。一方、アプリケーションの状態を維持する必要がある場合や、リクエスト処理の途中で再起動が問題になる場合は、hotオプションを使うメリットがあります。
まとめ
今回はBun + TypeScriptにおける実行環境を構築する手順についてまとめてみました。これをテンプレートとして、今後の開発で使っていければと思います。また、bunコマンドについても少し整理できました。体感的にも実行速度が速い印象はありますので、基本bunを用いて開発を進められればと思います。