データベース

SQLx CLI(sqlx-cli)の使い方|インストール・マイグレーション・オフラインモードをRustで実践

SQLx CLI(sqlx-cli)は、RustのSQLxクレートと組み合わせて使うコマンドラインツールで、データベースの作成、マイグレーションの管理、そしてデータベースへ接続せずにビルドするための「オフラインモード」の準備を担う。SQLxクレート本体がアプリからSQLを実行するライブラリなのに対し、sqlx-cliはその周辺の運用作業をコマンド1つで回すための補助ツールという役割分担になっている。この記事はバージョン0.9系(2026年5月リリース)を前提に、導入から実運用までのコマンドを動くコードで整理する。なお、同じ「sqlx」でもGoのjmoiron/sqlxは別ライブラリで、CLIやマイグレーション機能は持たない。本記事はRustのSQLxを対象とする。

まとめ:sqlx-cliの全体像とコマンド早見

sqlx-cliはcargo installで入れる独立したバイナリで、実行にはほぼ全コマンドで接続先を示すDATABASE_URLが必要になる。日常的に使うのは「DBの作成」「マイグレーションの追加・適用」「オフライン用メタデータの生成」の3系統に集約される。全体像は次の早見表のとおり。

コマンド 役割
cargo install sqlx-cli sqlx-cli本体の導入(DB別にfeature指定)
sqlx database create / drop DATABASE_URLのデータベース作成・削除
sqlx migrate add -r 名前 可逆(up/down)マイグレーションの作成
sqlx migrate run / revert / info マイグレーションの適用・巻き戻し・状態確認
cargo sqlx prepare オフライン用メタデータ(.sqlx)の生成
cargo sqlx prepare –check メタデータが最新かCIで検証

SQLx本体とsqlx-cliはバージョンを揃えるのが原則で、本記事執筆時点の最新は0.9.0。cargo install sqlx-cli --lockedのように--lockedを付けると、公開時点で検証済みの依存関係でビルドされ、途中の依存更新による失敗を避けやすい。

sqlx-cliとSQLxクレートの役割分担

SQLxは「DSLを使わずに、生SQLをコンパイル時に検証する」ことを特徴にした非同期SQLツールキットで、PostgreSQL・MySQL・SQLiteに対応する。アプリのコードから接続プールを張りquery!系マクロでSQLを実行するのがクレート本体の仕事だ。

一方のsqlx-cliは、アプリのビルドや実行とは切り離された運用コマンドを提供する。具体的にはマイグレーションファイルの生成と適用、開発用データベースの作成・破棄、そしてquery!マクロがコンパイル時検証に使うクエリ情報を事前生成するcargo sqlx prepareである。したがって「SQLxを使う=sqlx-cliが必須」ではなく、マイグレーションやオフラインビルドを行う段階でsqlx-cliを追加導入する、という関係になる。

sqlx-cliのインストールとDATABASE_URLの設定

cargo installとデータベース別のfeature指定

sqlx-cliはcargo installで導入する。デフォルトでは全DB・全TLSバックエンドが含まれ、コンパイルに時間がかかる。使うDBが決まっているなら、--no-default-featuresで既定を切り、必要なfeatureだけを指定してビルドを軽くする。PostgreSQL+rustls構成なら次のとおり。

cargo install sqlx-cli --no-default-features --features rustls,postgres --locked

MySQLならpostgresmysqlに、SQLiteならsqliteに置き換える。複数DBを扱うならカンマ区切りで併記できる。導入後はsqlx --versionでバージョンを確認しておくと、クレート側との食い違いに早く気づける。

DATABASE_URLと.envの設定

sqlx-cliのほぼ全コマンドは接続先データベースを必要とする。指定方法は--database-urlオプションか、環境変数DATABASE_URL、またはCargo.tomlと同じ階層(プロジェクトルート)の.envファイルのいずれか。運用では.envに書くのが扱いやすい。

DATABASE_URL=postgres://user:password@localhost/mydb

このDATABASE_URLはsqlx-cliだけでなく、アプリ側のquery!マクロがコンパイル時にスキーマを問い合わせる先にもなる。CLIとアプリで同じ接続先を共有する設計が基本だ。cargoそのものの使い方に不安があれば、Cargo(Rust)のインストールから主要コマンドまでの解説も合わせて確認しておくとよい。

SQLxクレートのプロジェクトへの追加

アプリからSQLを実行するには、CLIとは別にSQLxクレートを依存に加える。cargo addで非同期ランタイム(Tokio)とTLS、対象DBのfeatureを指定する。

cargo add sqlx --features runtime-tokio,tls-rustls,postgres
cargo add tokio --features full

0.7系以降、ランタイムとTLSのfeatureはruntime-tokiotls-rustlsのように分割されている点に注意する(旧版のruntime-tokio-rustlsのような結合名ではない)。0.9系のtls-rustlsはringを暗号プロバイダの既定に使うため、AWS-LC系を使いたい場合はtls-rustls-aws-lc-rsを選ぶ。SQLxは非同期APIなので、実行には#[tokio::main]などのランタイムが要る。非同期の考え方そのものはRustのasync/await構文の基本ガイドで整理できる。

sqlx-cliでのデータベースマイグレーション

sqlx-cliの中心的な用途がマイグレーション管理だ。GSC上でも「sqlx migrate」「sqlx migrate add」といったクエリが安定して表示されており、実務で最も参照される領域といえる。手順はDBの用意、マイグレーションの作成、適用の3段階になる。

データベースの作成と削除(sqlx database)

DATABASE_URLが指すデータベースを、CLIから直接作成・削除できる。開発環境を作り直すときに使う。

sqlx database create
sqlx database drop

マイグレーションの作成(sqlx migrate add)

マイグレーションファイルはsqlx migrate addで生成する。-r(reversible)を付けると、タイムスタンプ_名前.up.sql(適用用)と.down.sql(巻き戻し用)の接尾辞が付いた2ファイルが対で作られる。ファイル名の先頭のタイムスタンプにより、適用順序が一意に決まる。

sqlx migrate add -r create_users

生成されたup.sqlにテーブル定義を書く。

CREATE TABLE users (
    id   BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

対になるdown.sqlにはDROP TABLE users;のように、適用を打ち消す操作を書いておく。-rを付けない場合は前進のみのマイグレーションになり、revertで戻せなくなるため、本番運用では可逆で作っておくのが無難だ。

マイグレーションの適用・巻き戻し・状態確認

作成したマイグレーションはrunで適用する。SQLxは適用履歴を_sqlx_migrationsテーブルに記録し、未適用分だけを順に流す。infoで各マイグレーションの適用状況を一覧でき、revertで最後の可逆マイグレーションを1つ巻き戻せる。

sqlx migrate run
sqlx migrate info
sqlx migrate revert

runしたのに反映されない」場合は、まずinfoで状態を確認する。すでに_sqlx_migrationsに記録済みの版はスキップされるため、いったん適用したファイルの中身を後から書き換えても再実行されない。修正は新しいマイグレーションを追加して行うのが原則だ。

コンパイル時クエリ検証とオフラインモード(cargo sqlx prepare)

SQLxのquery!query_as!マクロは、ビルド時に実データベースへ接続してSQLの妥当性と型を検証する。この仕組みは強力だが、そのままではCIやDockerビルドなど「DBに接続できない環境」でコンパイルが通らない。これを解決するのがsqlx-cliのオフラインモードで、旧来のSQLx入門記事では触れられないことが多いが、実運用ではほぼ必須になる機能だ。

手順は、DBに接続できる開発環境でcargo sqlx prepareを実行し、各クエリの検証結果をJSONメタデータとして.sqlxディレクトリに書き出す。ファイルはSQL文字列のハッシュごとに1つ生成される。

cargo sqlx prepare

生成された.sqlxディレクトリをGitにコミットしておけば、以降は環境変数SQLX_OFFLINE=trueを設定するだけで、マクロはDBへ接続せず.sqlxのメタデータを読んでビルドできる。CIではキャッシュが古くなっていないかを--checkで検証し、スキーマ変更後のprepare忘れを検出する。

cargo sqlx prepare --check

ワークスペース構成ではcargo sqlx prepare --workspaceで全クレートをまとめて準備できる。スキーマやクエリを変えたらprepareをやり直し、.sqlxの更新をコミットに含める運用にしておくと、DBを持たないビルド環境でも壊れない。この仕組みはWebアプリのバックエンドをRust製WebフレームワークのAxumなどで組んでコンテナデプロイする際に効いてくる。

SQLxでのクエリ実行の基本と正しい書き方

query!とquery_as!の使い分け

接続プールを張り、query!でINSERTする最小例を示す。PostgreSQLでは挿入した行のIDをRETURNING句で受け取るのが定石で、返り値をfetch_oneで取得する。

use sqlx::postgres::PgPoolOptions;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect("postgres://user:password@localhost/mydb")
        .await?;

    let rec = sqlx::query!(
        "INSERT INTO users (name) VALUES ($1) RETURNING id",
        "Alice"
    )
    .fetch_one(&pool)
    .await?;

    println!("inserted id = {}", rec.id);
    Ok(())
}

MySQLのlast_insert_id()のような戻り値メソッドはPostgreSQLには無い。IDが欲しいときは上記のようにRETURNINGを使う。取得結果を自前の構造体へマッピングしたい場合はquery_as!を使い、第一引数にマッピング先の型を渡す(クエリ文字列だけを渡すのは誤り)。

struct User {
    id: i64,
    name: String,
}

let user = sqlx::query_as!(
    User,
    "SELECT id, name FROM users WHERE id = $1",
    1i64
)
.fetch_one(&pool)
.await?;

SQLiteでの最小構成

組み込み用途や軽量なテストではSQLiteが扱いやすい。接続先をsqlite:スキームにするだけで、ほぼ同じAPIで使える。

use sqlx::sqlite::SqlitePoolOptions;

let pool = SqlitePoolOptions::new()
    .connect("sqlite:app.db")
    .await?;

対象ファイルが存在しないとデフォルトでは接続エラーになる。ファイルを自動作成したい場合はsqlite://app.db?mode=rwcのようにmode=rwcを付けるか、インメモリで済むならsqlite::memory:を使う。

トランザクションの書き方(&mut *tx)

複数の操作をまとめて成功・失敗させるにはトランザクションを使う。0.7系以降、Transactionは直接Executorにならないため、実行対象には&mut *txのように参照外し(deref)した接続を渡す。旧来の&mut txのままではコンパイルが通らない。

let mut tx = pool.begin().await?;

sqlx::query!("INSERT INTO users (name) VALUES ($1)", "Bob")
    .execute(&mut *tx)
    .await?;

tx.commit().await?;

なお、トランザクションを別関数に&mut Transactionとして渡して実行する場合は、参照が一段増えるため&mut **txと二重に外す必要がある。所有したtxを直接使うか、参照で受け取るかで書き分ける。

よくある質問(FAQ)

sqlx-cliとSQLx本体はどう違いますか?

SQLx本体はアプリからSQLを実行するライブラリ(クレート)で、sqlx-cliはDB作成やマイグレーション、オフライン用メタデータ生成を担うコマンドラインツールです。SQLxを使うだけならCLIは必須ではなく、マイグレーションやDBなしビルドを行う段階で追加導入します。

cargo install sqlx-cliが遅い・失敗するときは?

既定では全DB・全TLSがコンパイルされ時間がかかります。--no-default-features --features rustls,postgresのように使うDBとTLSだけへ絞り、--lockedを付けて検証済みの依存で固定すると成功率と速度が上がります。

DATABASE_URLに接続できない環境でビルドするには?

接続できる環境でcargo sqlx prepareを実行し、生成される.sqlxディレクトリをコミットします。ビルド側ではSQLX_OFFLINE=trueを設定すれば、DBへ接続せずコンパイルできます。スキーマ変更のたびにprepareをやり直してください。

Goのsqlx(jmoiron/sqlx)とRustのSQLxは同じものですか?

別物です。Goのjmoiron/sqlxは標準のdatabase/sqlを拡張するライブラリで、CLIやマイグレーション、コンパイル時クエリ検証の仕組みは持ちません。本記事のsqlx-cliはRustのSQLx専用ツールです。

sqlx migrate runが反映されないときは?

まずsqlx migrate infoで適用状況を確認します。適用済みの版は_sqlx_migrationsテーブルに記録されスキップされるため、既存ファイルの中身を後から書き換えても再実行されません。変更は新しいマイグレーションを追加して行います。

関連記事

資料請求

RELATED POSTS 関連記事