🐘

Rust | Axum と SQLx & PostgreSQL で Todo アプリを作る

2022/10/02に公開

Axum と SQLx

Rust の Web フレームワークである Axum と SQL クレートである SQLx を使って、Todo アプリを作って見ました。

ソースコードは Github においてあります。参考までに、どうぞ!!

https://github.com/codemountains/axum-ddd-explicit-architecture

Axum

実装したエンドポイントは以下です。

  • GET /v1/todos
  • POST /v1/todos
  • PATCH /v1/todos/:id
  • PUT /v1/todos/:id
  • DELETE /v1/todos/:id

Router

Routerを生成し、それぞれの HTTP メソッドに該当する関数に実行するハンドラ関数を引数として渡しています。

layerには DI コンテナとして振る舞うModulesExtensionとして渡して、各ハンドラ関数から Modulesを呼び出せるようにします。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-driver/src/startup/mod.rs#L13-L40

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-driver/src/module/mod.rs

URL クエリパラメータを受け取る

Query(query): Query<TodoQuery>でクエリパラメータを受け取ることができます。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-driver/src/routes/todo.rs#L39-L73

なお、構造体への変換に Serde の Deserialize が必要です。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-driver/src/model/todo.rs#L132-L144

URL パスパラメータ・JSON リクエストの受け取る

URLパスパラメータをPath(id): Pathで、JSON リクエストをValidatedRequest(source): ValidatedRequest<JsonUpdateTodoContents>でそれぞれ受け取ることができます。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-driver/src/routes/todo.rs#L92-L129

バリデーション

バリデーションに関するヘルパー等は、context に定義してあります。

validateで返ってきた結果をerrorsという配列に格納して、JSON で返却するためにJsonErrorResponseを定義しています。

型が異なる等で JSON リクエストを定義した構造体に変換する際にエラーが発生する場合、Json::::from_request(req).await?が先に実行されるため、独自に実装したバリデーションのエラーより先にAppError::JsonRejectionエラーが返ります。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-driver/src/context/request_helper.rs

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-driver/src/context/response_helper.rs

validator クレートのマクロを使って、バリデーションを実装しています。

requiredは Option 型のみで使用できます。そのため、別の構造体に詰め替える処理の中でバリデーションを通過した正常値である前提でunwrapを行っています。

titleOption<String>からStringに変更し、リクエストで null を送信した場合は、AppError::JsonRejectionエラーが返ることになります。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-driver/src/model/todo.rs#L46-L65

バリデーションの実装については、Axum exaples の validator も合わせてご確認いただくと良いかと思います。

SQLx

REST API のエンドポイントと内部で実行される SQL は以下のようになってます。

  • GET /v1/todos -> SELECT
  • POST /v1/todos -> INSERT
  • PATCH /v1/todos/:id -> UPDATE
  • PUT /v1/todos/:id -> UPSERT
  • DELETE /v1/todos/:id -> DELETE

PostgreSQL と接続する

コネクションをはるために、Poolを生成します。

connectに DB 接続用の URL を渡しています。

PostgreSQL の場合、postgresql://username:password@host:port/databaseです。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-adapter/src/persistence/postgres.rs

query_as で SELECT

fetch_one

ID 指定などで、SELECT の結果が1件の場合は、fetch_oneを使います。

返り値はquery_asで指定した構造体StructOptionです。

where句のt.id = $1$1bindで Todo レコードの ID を反映させます。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-adapter/src/repository/todo.rs#L14-L44

fetch_all

複数件の結果を取得する場合は、fetch_allを使います。

返り値はquery_asで指定した構造体StructOption<Vec>です。

URL クエリパラメータで検索するステータスを指定できます。

ステータスの指定があれば、該当するステータスのみの Todo を取得するように実装しました。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-adapter/src/repository/todo.rs#L46-L91

query で INSERT

queryexecuteで INSERT を実行できます。

$1から$3の順番にbindすれば OK です。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-adapter/src/repository/todo.rs#L93-L129

query で UPDATE

INSERT と同じで、queryexecuteで UPDATE を実行できます。

PATCH /v1/todos/:id の処理であるため、引数がNoneの場合は更新しないようにしています。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-adapter/src/repository/todo.rs#L131-L181

query で UPSERT

INSERT や UPDATE と同じで、queryexecuteで UPSERT[1] を実行できます。

UPSERT 自体は、PostgreSQL のON CONFLICT DO UPDATEを使って実装しています。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-adapter/src/repository/todo.rs#L184-L227

query で DELETE

INSERT や UPDATE と同じで、queryexecuteで DELETE を実行できます。

削除した Todo の情報をレスポンスとして返したいので、ここでは削除前に SELECT を入れています。

https://github.com/codemountains/axum-ddd-explicit-architecture/blob/main/todo-adapter/src/repository/todo.rs#L229-L272

まとめ

SQL を書くことに抵抗がない場合、ORM も楽に実装できる気がします。

もちろん、ORM には ORM の良さがあるので、採用時にはしっかりと検討する必要があります。

また、SQLx にもマイグレーション機能があるようなので、そちらも触っていきたいです。

Todo アプリとしては、ユーザーと紐づけて管理できるところぐらいまでは実装したいです。

参考

以下のブログでも、記事を公開しています。よければ遊びに来てください😽

https://dottrail.codemountains.org/rust-axum-sqlx-postgresql/


脚注
  1. UPDATE or INSERT(レコードがあれば更新、なければ作成) ↩︎

Discussion