👯‍♀️

GoのSQLファーストORM「bun」が最高すぎたのでGORM・entとベンチマーク比較してみた

に公開

「GoのORM、結局どれを使えばいいの?」

Go言語でバックエンド開発をしていると、必ず一度はこの悩みにぶち当たりますよね。GORMは便利だけど少し重いしブラックボックス感がある。entは型安全で強力だけど、スキーマ定義やコード生成の学習コストが高い。標準の database/sqlsqlx だと、複雑なクエリを書くのがしんどい……。

そんな「ORM難民」の皆さんに朗報です。最近海外でじわじわと人気を集めている 「Bun」 というORMをご存知でしょうか?

今回は、日本語の解説記事がまだ少ないこの「Bun」について、公式サイトの情報を交えながら、その魅力や使い方、そして気になるパフォーマンス(GORM・entとの実測ベンチマーク比較)まで、たっぷりとお届けします!


Bunとは?「SQLファースト」なモダンORM

Bun は、Uptrace社が開発しているGo言語向けのORMです。最大の特徴は、その 「SQLファースト(SQL-first)」 という設計思想にあります。

公式サイトでは、Bunのコンセプトについて以下のように語られています。

"Write elegant SQL queries with type safety and performance. No magic, just pure Go and SQL working together."
Bun 公式サイト

また、公式サイトではその設計思想についてこう語っています。

"Traditional ORMs force you to learn their DSL and hide your SQL behind layers of abstraction. Bun takes a different approach: embrace SQL, enhance it with Go's type safety."
Why Bun? Because SQL shouldn't be scary.

つまり、「ORM独自のDSL(ドメイン固有言語)を覚えるのではなく、あなたが知っているSQLの知識をそのまま活かせる」ということです。SQLを隠蔽するのではなく、Goの型安全性と組み合わせてSQLを「強化」するアプローチをとっています。

主な特徴

1. SQLファーストなクエリビルダー

生成されるSQLが非常に予測しやすく、複雑なクエリ(CTEやWindow関数など)もGoのコードで直感的に書けます。実行されるSQLをほぼそのままGoコードに対応させることができるので、デバッグも楽です。

2. ゼロオーバーヘッドに近いパフォーマンス

database/sql の上に薄く構築されており、ORMとしての利便性を持ちながら、生SQLに近いパフォーマンスを発揮します(後述のベンチマーク参照)。

3. マルチデータベース対応

PostgreSQL(最優先サポート)、MySQL 5.0+/MariaDB、SQLite、SQL Server(MSSQL)、Oracleをサポートしています。

4. 型安全なスキャン

クエリ結果を構造体、マップ、スライス、スカラー値に自動的にマッピングします。

5. 豊富な機能

ソフトデリート、マイグレーション、フィクスチャ、フック、トランザクション管理など、本番環境で必要な機能が一通り揃っています。


実際にどれくらい速い? Ubuntu + PostgreSQL でベンチマーク対決!

「パフォーマンスが良い」と言われても、実際のところどうなの? と気になりますよね。
そこで今回は、Ubuntu 22.04 上に PostgreSQL 16 を構築し、BunGORMent、そして Raw SQL(database/sql + lib/pq) の4者でベンチマークを計測してみました。

計測環境と条件

項目 バージョン
OS Ubuntu 22.04 LTS
DB PostgreSQL 16.13
Go 1.25.0
Bun v1.2.18
GORM v1.31.1
ent v0.14.6

各操作(INSERT, SELECT, UPDATE, DELETE)を 1,000回 実行し、1操作あたりの平均処理時間(マイクロ秒: µs)を計測。3回実行した平均値を採用しました。

ベンチマーク結果

操作 Raw SQL Bun GORM ent
INSERT (1件ずつ) 203 µs 182 µs 267 µs 235 µs
SELECT (全件取得 ×10) 8.5 µs 13.6 µs 16.5 µs 10.6 µs
SELECT (WHERE条件) 175 µs 145 µs 137 µs 174 µs
UPDATE 244 µs 205 µs 274 µs 148 µs
BULK INSERT (1000件一括) 15.5 µs 10.8 µs 12.7 µs 12.1 µs
DELETE 209 µs 168 µs 258 µs 137 µs

(数値が小さいほど高速。環境・ネットワーク・DB負荷によって変動するため、あくまで参考値です)

考察

結果を見ると、Bun は GORM と比較して全体的に 20~35% ほど高速 に動作していることがわかります。INSERT、UPDATE、DELETE の書き込み処理では特に差が顕著で、BULK INSERT に至っては Raw SQL を上回る結果を出しています。

ent は UPDATE、DELETE で特に高速な結果を出していますが、コード生成のアプローチをとっているため、リフレクションを多用する GORM と比較して有利な傾向にあります。Bun はコード生成なし(リフレクションベース)でありながら、INSERT や SELECT で ent に匹敵するスピードを実現している点が非常に優秀です。

GORM は SELECT WHERE では最高速な結果を出していますが、INSERT・UPDATE・DELETE では大きく差が開きます。これは GORM が内部でコールバックや型チェックを多数実行するためです。

なお、今回の計測はサンドボックス環境(他のプロセスとリソースを共有)での計測のため、実際の本番環境では差が変わる可能性があります。大まかな傾向として参考にしてください。


GORM・ent との機能比較

パフォーマンス以外の観点でも比較してみましょう。

機能 Bun GORM ent
SQLファーストなクエリビルダー
コード生成
型安全なスキーマ定義 △ (タグ) △ (タグ)
マイグレーション
ソフトデリート
トランザクション
フック
バルクインサート ✅ (ネイティブ)
CTE / Window関数
生SQL実行
PostgreSQL固有機能 ✅ (JSONB等)
学習コスト 低〜中 中〜高
コードの可読性

entはコード生成により型安全性が最も高いですが、スキーマ変更のたびに go generate が必要です。GORMは最も学習コストが低いですが、複雑なクエリになるとSQLが予測しにくくなります。Bunはその中間で、SQLの知識をそのまま活かしつつ、型安全なクエリが書けるバランスの良い選択肢です。


Bun の基本的な使い方・コマンドまとめ

ここからは、実際に Bun をプロジェクトに導入して使うための基本的な手順と設定項目をまとめます。

1. インストール

使用するデータベースに応じて、ダイアレクトとドライバーを選んでインストールします。

# Bun本体(必須)
go get github.com/uptrace/Bun

# PostgreSQL の場合
go get github.com/uptrace/Bun/dialect/pgdialect
go get github.com/uptrace/Bun/driver/pgdriver

# MySQL の場合
go get github.com/uptrace/Bun/dialect/mysqldialect
go get github.com/go-sql-driver/mysql

# SQLite の場合
go get github.com/uptrace/Bun/dialect/sqlitedialect
go get github.com/mattn/go-sqlite3

# デバッグ用ロガー(オプション)
go get github.com/uptrace/Bun/extra/Bundebug

2. データベース接続と設定

Bun では、標準の database/sql でコネクションを作成し、それを Bun でラップする形をとります。

package main

import (
    "database/sql"
    "github.com/uptrace/Bun"
    "github.com/uptrace/Bun/dialect/pgdialect"
    "github.com/uptrace/Bun/driver/pgdriver"
    "github.com/uptrace/Bun/extra/Bundebug"
)

func main() {
    // 1. pgdriverを使用して標準のsql.DBを作成
    dsn := "postgres://user:pass@localhost:5432/dbname?sslmode=disable"
    sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn)))

    // 2. Bun.DBでラップする
    db := Bun.NewDB(sqldb, pgdialect.New())
    defer db.Close()

    // オプション: 開発用にSQLクエリをログ出力する
    db.AddQueryHook(Bundebug.NewQueryHook(
        Bundebug.WithVerbose(true), // trueにするとすべてのクエリを出力
    ))
}

3. モデルの定義(構造体タグ)

Bun のモデル定義は、Goの構造体に Bun タグを付与して行います。

import (
    "time"
    "github.com/uptrace/Bun"
)

type User struct {
    // テーブル名やエイリアスを指定(省略時は構造体名の複数形スネークケース)
    Bun.BaseModel `Bun:"table:users,alias:u"`

    ID        int64     `Bun:"id,pk,autoincrement"`  // プライマリーキー、自動採番
    Name      string    `Bun:"name,notnull"`          // NOT NULL制約
    Email     string    `Bun:"email,unique"`          // UNIQUE制約
    CreatedAt time.Time `Bun:",nullzero,notnull,default:current_timestamp"`
    // ソフトデリート用フィールド
    DeletedAt time.Time `Bun:",soft_delete,nullzero"`
}

構造体タグ(設定項目)完全リファレンス(公式ドキュメントより):

テーブルレベルのタグ(Bun.BaseModel に付与):

タグ 説明
table:名前 Bun:"table:users" テーブル名を上書き(デフォルトは構造体名の複数形スネークケース)
alias:名前 Bun:"alias:u" クエリ内のテーブルエイリアスを設定
select:名前 Bun:"select:users_view" SELECT時に使用するテーブル/ビュー名を別途指定

フィールドレベルのタグ(各フィールドに付与):

タグ 説明
pk Bun:",pk" プライマリーキーとして扱う(notnull を暗黙的に含む)
autoincrement Bun:",autoincrement" 自動採番(SERIAL/AUTO_INCREMENT等、nullzero を暗黙的に含む)
notnull Bun:",notnull" NOT NULL制約を付与
unique Bun:",unique" UNIQUE制約を付与
unique:グループ名 Bun:",unique:email_domain" グループUNIQUE制約(複数フィールドで一意性を保証)
default:値 Bun:"default:current_timestamp" DB側のDEFAULT値を設定(gen_random_uuid() 等の式も可)
nullzero Bun:",nullzero" Goのゼロ値(""0)をDBの NULL として扱う
type:型名 Bun:"type:jsonb" DB側のカラム型を明示的に指定
soft_delete Bun:",soft_delete" ソフトデリートフラグとして使用
scanonly Bun:",scanonly" SELECTのスキャン専用(INSERT/UPDATEでは無視)
array Bun:",array" PostgreSQL の配列型として扱う
json_use_number Bun:",json_use_number" JSONデコード時に精度の高い数値型を使用
msgpack Bun:",msgpack" MessagePackエンコーディングを使用
alt:名前 Bun:"alt:old_name" マイグレーション用の旧カラム名エイリアス
Bun:"-" Bun:"-" このフィールドをBunが完全に無視する

リレーション関連のタグ:

タグ 説明
rel:has-one has-one リレーションを定義
rel:has-many has-many リレーションを定義
rel:belongs-to belongs-to リレーションを定義
m2m:テーブル名 many-to-many リレーションを定義(中間テーブル名を指定)
join:自テーブルFK=相手テーブルPK リレーションのJOIN条件を指定

4. 基本的なCRUD操作

クエリビルダーはメソッドチェーンで直感的に記述できます。

ctx := context.Background()

// ---- CREATE ----

// 単一INSERT(IDが自動的にセットされる)
user := &User{Name: "Alice", Email: "alice@example.com"}
_, err := db.NewInsert().Model(user).Exec(ctx)

// バルクINSERT(スライスを渡すだけ!)
users := []User{
    {Name: "Bob", Email: "bob@example.com"},
    {Name: "Carol", Email: "carol@example.com"},
}
_, err = db.NewInsert().Model(&users).Exec(ctx)

// ON CONFLICT DO NOTHING(PostgreSQL)
_, err = db.NewInsert().
    Model(user).
    On("CONFLICT DO NOTHING").
    Exec(ctx)

// ---- READ ----

// 単一取得(WherePKでプライマリーキー検索)
var u User
err = db.NewSelect().Model(&u).Where("id = ?", 1).Scan(ctx)

// 複数取得(スライスにScan)
var list []User
err = db.NewSelect().
    Model(&list).
    Where("name LIKE ?", "A%").
    Order("created_at DESC").
    Limit(10).
    Scan(ctx)

// 件数取得
count, err := db.NewSelect().Model((*User)(nil)).Count(ctx)

// 取得と件数を同時に(ページネーション等に便利)
var users []User
count, err = db.NewSelect().Model(&users).Limit(20).ScanAndCount(ctx)

// ---- UPDATE ----

// 特定フィールドのみ更新
_, err = db.NewUpdate().
    Model(&u).
    Set("name = ?", "Alice Updated").
    Where("id = ?", u.ID).
    Exec(ctx)

// ---- DELETE ----

// 条件指定で削除
_, err = db.NewDelete().
    Model((*User)(nil)).
    Where("id = ?", 1).
    Exec(ctx)

// ソフトデリートが設定されている場合は自動的にUPDATEになる
// 物理削除したい場合は ForceDelete() を使う
_, err = db.NewDelete().
    Model((*User)(nil)).
    Where("id = ?", 1).
    ForceDelete().
    Exec(ctx)

5. SELECT クエリの詳細な使い方

公式ドキュメントに掲載されている SELECT クエリのAPIは非常に豊富です。

db.NewSelect().
    With("cte_name", subquery).           // CTE(共通テーブル式)

    Model(&strct).                         // 構造体にスキャン
    Model(&slice).                         // スライスにスキャン

    Column("col1", "col2").               // カラム名(クォートあり)
    ColumnExpr("col1, col2").             // 任意のSQL式
    ColumnExpr("count(*)").
    ColumnExpr("count(?)", Bun.Ident("id")).
    ColumnExpr("(?) AS alias", subquery). // サブクエリをカラムに
    ExcludeColumn("col1").                // 特定カラムを除外
    ExcludeColumn("*").                   // 全カラムを除外

    Table("table1", "table2").            // テーブル名(クォートあり)
    TableExpr("table1 AS t1").            // 任意のSQL式
    ModelTableExpr("table1 AS t1").       // モデルのテーブル名を上書き

    Join("JOIN table2 AS t2 ON t2.id = t1.id").
    Join("LEFT JOIN table2 AS t2").JoinOn("t2.id = t1.id").

    WherePK().                            // プライマリーキーでWHERE
    Where("id = ?", 123).
    Where("name LIKE ?", "my%").
    Where("id IN (?)", Bun.In([]int64{1, 2, 3})).
    WhereGroup(" AND ", func(q *Bun.SelectQuery) *Bun.SelectQuery {
        return q.WhereOr("id = 1").WhereOr("id = 2")
    }).

    Group("col1", "col2").
    Order("col1 ASC", "col2 DESC").
    Having("column_name > ?", 123).

    Limit(100).
    Offset(100).

    For("UPDATE").                        // SELECT FOR UPDATE(悲観的ロック)

    Scan(ctx)

6. リレーション(テーブル間の関係)

Bun は4種類のリレーションをサポートしています。

// has-one: UserはProfileを1つ持つ
type Profile struct {
    ID     int64 `Bun:",pk"`
    Lang   string
    UserID int64
}

type User struct {
    ID      int64    `Bun:",pk"`
    Name    string
    Profile *Profile `Bun:"rel:has-one,join:id=user_id"`
}

// belongs-to: BookはAuthorに属する
type Book struct {
    ID       int64
    AuthorID int64
    Author   *Author `Bun:"rel:belongs-to,join:author_id=id"`
}

// has-many: UserはProfileを複数持つ
type User struct {
    ID       int64      `Bun:",pk"`
    Name     string
    Profiles []*Profile `Bun:"rel:has-many,join:id=user_id"`
}

// many-to-many: OrderはItemを複数持ち、ItemもOrderを複数持つ
func init() {
    db.RegisterModel((*OrderToItem)(nil)) // 中間テーブルを登録
}

type Order struct {
    ID    int64  `Bun:",pk"`
    Items []Item `Bun:"m2m:order_to_items,join:Order=Item"`
}

// リレーションを使ったクエリ
err := db.NewSelect().
    Model(book).
    Relation("Author").
    Where("book.id = ?", 1).
    Scan(ctx)

7. トランザクション

Bun のトランザクションは RunInTx を使うのが最もシンプルです。

// RunInTx: エラーが返ればロールバック、nilならコミット
err := db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx Bun.Tx) error {
    if _, err := tx.NewInsert().Model(user1).Exec(ctx); err != nil {
        return err // ロールバック
    }
    if _, err := tx.NewInsert().Model(user2).Exec(ctx); err != nil {
        return err // ロールバック
    }
    return nil // コミット
})

// 手動でトランザクションを管理する場合
tx, err := db.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
    return err
}
defer tx.Rollback() // エラー時のフォールバック

if _, err := tx.NewInsert().Model(user).Exec(ctx); err != nil {
    return err
}

return tx.Commit()

Bun.IDB インターフェースを使うと、*Bun.DBBun.Tx を同じ関数で扱えます。

// db でも tx でも同じ関数が使える
func InsertUser(ctx context.Context, db Bun.IDB, user *User) error {
    _, err := db.NewInsert().Model(user).Exec(ctx)
    return err
}

// 通常の呼び出し
err := InsertUser(ctx, db, user1)

// トランザクション内での呼び出し
err = db.RunInTx(ctx, nil, func(ctx context.Context, tx Bun.Tx) error {
    return InsertUser(ctx, tx, user2)
})

8. ソフトデリート

Bun のソフトデリートは、soft_delete タグを付けるだけで自動的に有効になります。

type User struct {
    ID        int64
    Name      string
    CreatedAt time.Time `Bun:",nullzero,notnull,default:current_timestamp"`
    DeletedAt time.Time `Bun:",soft_delete,nullzero"` // このタグを付けるだけ!
}

// 通常のDELETE → 自動的に UPDATE SET deleted_at = now() に変換される
_, err := db.NewDelete().Model(user).Where("id = ?", 1).Exec(ctx)
// → UPDATE users SET deleted_at = current_timestamp WHERE id = 1

// 通常のSELECT → deleted_at IS NULL が自動的に追加される
err = db.NewSelect().Model(&users).Scan(ctx)
// → SELECT * FROM users WHERE deleted_at IS NULL

// ソフトデリート済みのレコードを取得
err = db.NewSelect().Model(&users).WhereDeleted().Scan(ctx)
// → SELECT * FROM users WHERE deleted_at IS NOT NULL

// 全件取得(ソフトデリート含む)
err = db.NewSelect().Model(&users).WhereAllWithDeleted().Scan(ctx)
// → SELECT * FROM users

// 物理削除
_, err = db.NewDelete().Model(user).Where("id = ?", 1).ForceDelete().Exec(ctx)
// → DELETE FROM users WHERE id = 1

9. フック(Hooks)

Bun のフックには「モデルフック」と「クエリフック」の2種類があります。

// モデルフック: INSERT/UPDATE前にフィールドを自動更新
type Model struct {
    ID        int64
    CreatedAt time.Time
    UpdatedAt time.Time
}

var _ Bun.BeforeAppendModelHook = (*Model)(nil)

func (m *Model) BeforeAppendModel(ctx context.Context, query Bun.Query) error {
    switch query.(type) {
    case *Bun.InsertQuery:
        m.CreatedAt = time.Now()
    case *Bun.UpdateQuery:
        m.UpdatedAt = time.Now()
    }
    return nil
}

// クエリフック: 全クエリの実行前後に処理を挟む(ロギング、APM等)
type QueryHook struct{}

func (h *QueryHook) BeforeQuery(ctx context.Context, event *Bun.QueryEvent) context.Context {
    return ctx
}

func (h *QueryHook) AfterQuery(ctx context.Context, event *Bun.QueryEvent) {
    fmt.Println(time.Since(event.StartTime), string(event.Query))
}

db.AddQueryHook(&QueryHook{})

公式の注意書き: フックをバリデーションやキャッシュに使うのは一見良さそうに見えますが、コードが読みにくく、デバッグしにくくなります。代わりに、シンプルな関数を書くことを推奨します。

10. マイグレーション

Bun のマイグレーションは、Go関数ベースとSQLファイルベースの2種類があります。

マイグレーションファイルの命名規則:

20210505110026_add_foo_column.go   # タイムスタンプ_説明.go
20210505110026_add_foo_column.sql  # SQLファイルの場合

Goベースのマイグレーション:

// migrations/main.go
package migrations

import "github.com/uptrace/Bun/migrate"

var Migrations = migrate.NewMigrations()

// migrations/20210505110026_init.go
func init() {
    Migrations.MustRegister(
        // UP
        func(ctx context.Context, db *Bun.DB) error {
            _, err := db.NewCreateTable().Model((*User)(nil)).Exec(ctx)
            return err
        },
        // DOWN
        func(ctx context.Context, db *Bun.DB) error {
            _, err := db.NewDropTable().Model((*User)(nil)).Exec(ctx)
            return err
        },
    )
}

SQLベースのマイグレーション:

-- 20210505110026_init.up.sql
CREATE TABLE users (
    id   BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

--Bun:split  ← これで複数のSQL文を区切る

CREATE INDEX idx_users_name ON users(name);

マイグレーション管理CLIコマンド:

スターターキットを使うと、以下のCLIコマンドが使えます。

# マイグレーションテーブルを初期化
go run cmd/Bun/main.go db init

# 未適用のマイグレーションをすべて実行
go run cmd/Bun/main.go db migrate

# 最後のマイグレーショングループをロールバック
go run cmd/Bun/main.go db rollback

# マイグレーションのロックを解除(失敗時のリカバリ)
go run cmd/Bun/main.go db unlock

# Goマイグレーションファイルを新規作成
go run cmd/Bun/main.go db create_go add_users_table

# SQLマイグレーションファイルを新規作成
go run cmd/Bun/main.go db create_sql add_users_table

11. デバッグ・開発時の便利機能

// クエリのデバッグ出力(Bundebugパッケージ)
import "github.com/uptrace/Bun/extra/Bundebug"

db.AddQueryHook(Bundebug.NewQueryHook(
    Bundebug.WithVerbose(true),    // 全クエリを出力
    Bundebug.WithEnabled(true),    // 有効/無効の切り替え
))

// 生SQLを直接実行
err := db.NewRaw("SELECT id, name FROM ? LIMIT ?",
    Bun.Ident("users"), 100,
).Scan(ctx, &users)

// クエリを文字列として確認(実行せずにSQLを確認)
query := db.NewSelect().Model((*User)(nil)).Where("id = ?", 1)
fmt.Println(query.String())
// → SELECT "user"."id", "user"."name" FROM "users" AS "user" WHERE (id = 1)

pgdriver の設定オプション

PostgreSQL ドライバー(pgdriver)の接続設定オプションをまとめます。

// DSN形式での設定
dsn := "postgres://user:pass@localhost:5432/dbname?sslmode=disable&connect_timeout=5"
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn)))

// オプション形式での設定(より細かく制御したい場合)
connector := pgdriver.NewConnector(
    pgdriver.WithAddr("localhost:5432"),
    pgdriver.WithUser("user"),
    pgdriver.WithPassword("pass"),
    pgdriver.WithDatabase("dbname"),
    pgdriver.WithTLSConfig(nil),          // TLS設定(nilで無効)
    pgdriver.WithConnParams(map[string]interface{}{
        "search_path": "myschema",        // スキーマ設定
    }),
)

database/sql のコネクションプール設定:

sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn)))

// コネクションプールの設定(本番環境では必ず設定すること)
sqldb.SetMaxOpenConns(25)                  // 最大接続数
sqldb.SetMaxIdleConns(25)                  // アイドル接続数
sqldb.SetConnMaxLifetime(5 * time.Minute)  // 接続の最大生存時間
sqldb.SetConnMaxIdleTime(5 * time.Minute)  // アイドル接続の最大生存時間

まとめ:Bunを選ぶべき理由

GoのORM「Bun」について、特徴からベンチマーク、基本的な使い方までをご紹介しました。

最後に、どんな場合に Bun を選ぶべきかをまとめます。

Bun が向いているケース:

  • SQLの知識をそのまま活かして、型安全にクエリを書きたい
  • GORMの「マジック」や重さが気になっている
  • entのコード生成やスキーマ定義が大げさすぎると感じる
  • PostgreSQLの固有機能(JSONB、CTEなど)を積極的に使いたい
  • パフォーマンスを重視している

Bun が向いていないケース:

  • コード生成による完全な型安全性が必要(→ ent を検討)
  • チームのGoスキルが低く、最もシンプルなAPIが必要(→ GORM を検討)
  • スキーマ変更の追跡を自動化したい(→ ent の Atlas を検討)

Bun は「SQLを知っているGoエンジニア」にとって、最も自然に使えるORMだと思います。公式ドキュメントも非常に読みやすいので、ぜひ一度試してみてはいかがでしょうか?

Happy Hacking! 🍔


参考リンク

リソース URL
Bun 公式サイト https://Bun.uptrace.dev/
Bun GitHub https://github.com/uptrace/Bun
Bun モデル定義 https://Bun.uptrace.dev/guide/models.html
Bun SELECT クエリ https://Bun.uptrace.dev/guide/query-select.html
Bun ORM リレーション https://Bun.uptrace.dev/guide/relations.html
Bun マイグレーション https://Bun.uptrace.dev/guide/migrations.html
Bun トランザクション https://Bun.uptrace.dev/guide/transactions.html
Bun ソフトデリート https://Bun.uptrace.dev/guide/soft-deletes.html
Bun フック https://Bun.uptrace.dev/guide/hooks.html
Bun スターターキット https://Bun.uptrace.dev/guide/starter-kit.html
Bun ドライバー・ダイアレクト https://Bun.uptrace.dev/guide/drivers.html
VeriCerts Tech Blog

Discussion