Rust – Actix Web × MongoDB環境をサクッと起動
2024.01.18
この記事は最終更新日から1年以上が経過しています。

どもです。
昨年より業務でガッツリRustでの開発を行ってはいるのですが、主にクライアントアプリ開発の方で、そういえばサーバー周りはどうなっているのだろうと、最近はもごもご漁っている状況だったりします。
Rustのasync対応も終わり、サーバーフレームワークの方も安定且つ、増えて来ている状況で、全然Rustでのサーバー開発はアリだなと感じている今日このごろです。
というか、サーバー開発で使わない理由の少なさもあって、むしろ積極的にサーバー開発にRust採用していくべきだと思いますし、今後も増えていくのだろうと肌感で感じました。
という訳で、今日は、RustのサーバーフレームワークのActix Webを用いて、DBはmongodbでローカル開発環境をサクッと起ち上げて行きたいと思います。
HomebrewでMongoDBをインストール
HomebrewでMongoDBをインストールしていきます。この辺解説は割愛させていただきます。
brew tap mongodb/brew
brew install mongodb-community
バーションの確認
mongod --version
起動・停止方法の確認
brew services start mongodb-community
brew services stop mongodb-community
MongoDB compassでDB作成
MongoDBのGUIツールのMongoDB compassをインストールしましょう。
こちらは任意でインストールしていただければと。cliで行う方は飛ばして頂いて問題ありません。
MongoDB compass公式のHPよりcompassをインストールしていきます。

ダウンロードページは以下になります。該当するPlatformを選択しダウンロードしましょう。

brewコマンドでmongodbを起動しましたら、MongoDB compass「mongodb://localhost:27017」で接続。+アイコンでデータベース「myApp」を作成、左上の「+ Create collection」ボタンを押下、Collection Name「users」で、「Create collection」ボタンを押下。

これで、DB名「myApp」、collectionは「users」の形で、準備が完了。
Actix Web
それでは、Actix Webとmongodbを用いて、APIを作っていきます。
まずは、cargo newで新規プロジェクト作成。
cargo new mongodb --bin
createは以下の3つを使用します。
[dependencies]
mongodb = "2"
actix-web = "^4"
serde = { version = "^1", features = ["derive"] }
プログラムを書いて行くのですが、Actix Webはサンプルが豊富で、mongodbを用いたサンプルも以下に存在します。なので、ソースだけ知りたい方は、こちらを見ていただければと。
GitHub
examplesのソースを追っていきましょう。まずは「src/model.rs」ですが、struct Userを定義しております。
User Model: src/model.rs
pub struct User {
pub first_name: String,
pub last_name: String,
pub username: String,
pub email: String,
}
はい。後は「main.rs」となります。createのインポートや、定数を定義しましょう。
先程のDB名、collection名もこちらで定義しております。
src/main.rs
mod model;
use actix_web::{get, post, web, App, HttpResponse, HttpServer};
use model::User;
use mongodb::{bson::doc, options::IndexOptions, Client, Collection, IndexModel};
const DB_NAME: &str = "myApp";
const COLL_NAME: &str = "users";
Actix Webでは、#[post(“/add_user”)]といったアトリビュートでのAPIの定義を行うことが可能です。以下の場合は /add_userのURIに対して、add_user関数を実行し、ユーザーを作成する処理(API)となります。
mongodbである、clientを引数collectionにアクセスし、ユーザーを作成。失敗したらエラーとなります。
ユーザー作成 POST: /add_user
/// Adds a new user to the "users" collection in the database.
#[post("/add_user")]
async fn add_user(client: web::Data<Client>, form: web::Form<User>) -> HttpResponse {
let collection = client.database(DB_NAME).collection(COLL_NAME);
let result = collection.insert_one(form.into_inner(), None).await;
match result {
Ok(_) => HttpResponse::Ok().body("user added"),
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}
こちらは、ユーザーを参照するAPIとなります。usernameをURIに含め、DBを探索し該当するユーザーを取得します。
usernameに関してはuniqueとして設定されていますので、重複は想定していなくfind_oneで取得しています。
ユーザー参照 GET: /get_user/{username}
/// Gets the user with the supplied username.
#[get("/get_user/{username}")]
async fn get_user(client: web::Data<Client>, username: web::Path<String>) -> HttpResponse {
let username = username.into_inner();
let collection: Collection<User> = client.database(DB_NAME).collection(COLL_NAME);
match collection
.find_one(doc! { "username": &username }, None)
.await
{
Ok(Some(user)) => HttpResponse::Ok().json(user),
Ok(None) => {
HttpResponse::NotFound().body(format!("No user found with username {username}"))
}
Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
}
}
サーバー起動時にcreate_username_index関数を呼び、ソースコードからDocumentsのusernameに対してindex設定を行っています。
なので、mongodb側でindexを外しユニークでは無くなっていたりすると、エラーとなります。
index処理
create_username_index(&client).await;
/// Creates an index on the "username" field to force the values to be unique.
async fn create_username_index(client: &Client) {
let options = IndexOptions::builder().unique(true).build();
let model = IndexModel::builder()
.keys(doc! { "username": 1 })
.options(options)
.build();
client
.database(DB_NAME)
.collection::<User>(COLL_NAME)
.create_index(model, None)
.await
.expect("creating an index should succeed");
}
mainの処理は、mongodbにアクセスと、HttpServer::newでサーバーを起ち上げ、App::new()でmongodbのデータをapp data化。先程のAPIをservice化を行っています。
以下の環境で起動。
サーバー
MongoDB
mongodb://localhost:27017
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let uri = std::env::var("MONGODB_URI").unwrap_or_else(|_| "mongodb://localhost:27017".into());
let client = Client::with_uri_str(uri).await.expect("failed to connect");
create_username_index(&client).await;
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(client.clone()))
.service(add_user)
.service(get_user)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
それでは、cargo runで起動しましょう。
cargo run
API疎通テスト
Postmanを用いてAPI疎通テストを行います。まずは、ユーザー作成したいので、POSTに設定で
URIは「http://127.0.0.1:8080/add_user」入力。
BodyはJSON形式で以下のデータを送信。

ユーザー作成 POST: /add_user
{
"first_name": "daisuke",
"last_name": "takayama",
"username": "takayama_daisuke",
"email": "webcyou@webcyou.com",
}
エラーが出なければ成功。では、作成したユーザーを取得しましょう。
ユーザー取得はGETで、URIを取得したいユーザー名も含めた「http://127.0.0.1:8080/get_user/takayama_daisuke」を入力。

ユーザー参照 GET: /get_user/{username}
レスポンスに該当するユーザーがJSON形式で返却されれば成功。
{
"first_name": "daisuke",
"last_name": "takayama",
"username": "takayama_daisuke",
"email": "webcyou@webcyou.com"
}
と、一旦ここまでで起動は成功。
いやぁ。爆速のサーバーが容易に扱える事に喜びを感じております。
引き続きRustのサーバー周りも追っていこうと思います。
ではではぁ。














