appwrite-rust について
サーバーサイドのRust開発者向けに、Appwriteの非同期SDKの使い方を案内します。ユーザー、データベース、ストレージ、関数などを扱います。
主な機能
- 01非同期のサーバーサイドクライアント
- 02ユーザー管理
- 03TablesDBのデータ操作
- 04ファイルストレージと関数呼び出し
- 05クエリ、権限、エラー処理
活用シーン
- 01RustサービスでAppwriteのユーザーを管理
- 02テーブルを作成してデータを検索
- 03ファイルをアップロードしてアクセス権を管理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ appwrite/ skills --skill appwrite-rustインストール後、Claude Code で/appwrite-rustを入力して読み込まれたか確認します。
詳細
Appwrite Rust SDK
インストール
cargo add appwrite
cargo add tokio --features full
cargo add serde_json
クライアントのセットアップ
Rust SDK は非同期です。Tokio ランタイムから使用し、サーバー側では API キーで認証してください。
use appwrite::Client;
use std::env;
let client = Client::new()
.set_endpoint("https://<REGION>.cloud.appwrite.io/v1")
.set_project(env::var("APPWRITE_PROJECT_ID")?)
.set_key(env::var("APPWRITE_API_KEY")?);
サーバーの完全なスケルトン
use appwrite::query::Query;
use appwrite::services::Users;
use appwrite::Client;
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new()
.set_endpoint("https://<REGION>.cloud.appwrite.io/v1")
.set_project(env::var("APPWRITE_PROJECT_ID")?)
.set_key(env::var("APPWRITE_API_KEY")?);
let users = Users::new(&client);
let _list = users
.list(Some(vec![Query::limit(25).to_string()]), None, Some(true))
.await?;
Ok(())
}
コード例
ユーザー管理
use appwrite::id::ID;
use appwrite::query::Query;
use appwrite::services::Users;
let users = Users::new(&client);
// Create user
let _user = users
.create(
ID::unique(),
Some("[email protected]"),
None,
Some("password123"),
Some("User Name"),
)
.await?;
// List users
let _list = users
.list(Some(vec![Query::limit(25).to_string()]), None, Some(true))
.await?;
// Get user
let _fetched = users.get("[USER_ID]").await?;
// Delete user
users.delete("[USER_ID]").await?;
データベース操作
注記: 新しいコードでは
TablesDBを使用してください。既存のプロジェクトがレガシーの Databases API に明示的に依存している場合に限り、Databasesを使用してください。Rust SDK の呼び出し規約: メソッドは非同期で、位置引数を使用し、オプションの API パラメーターは
Option<T>として表します。設定しないオプション値にはNoneを渡してください。
use appwrite::id::ID;
use appwrite::permission::Permission;
use appwrite::query::Query;
use appwrite::role::Role;
use appwrite::services::TablesDB;
use serde_json::json;
let tables_db = TablesDB::new(&client);
// Create database
let _database = tables_db
.create(ID::unique(), "My Database", Some(true))
.await?;
// Create table
let _table = tables_db
.create_table(
"[DATABASE_ID]",
ID::unique(),
"articles",
Some(vec![
Permission::read(Role::any()).to_string(),
Permission::create(Role::users(None)).to_string(),
]),
Some(true),
Some(true),
None,
None,
)
.await?;
// Create row
let _row = tables_db
.create_row(
"[DATABASE_ID]",
"[TABLE_ID]",
ID::unique(),
json!({
"title": "Hello World",
"done": false
}),
None,
None,
)
.await?;
// Query rows
let _rows = tables_db
.list_rows(
"[DATABASE_ID]",
"[TABLE_ID]",
Some(vec![
Query::equal("done", false).to_string(),
Query::limit(10).to_string(),
]),
None,
Some(true),
None,
)
.await?;
// Get row
let _row = tables_db
.get_row("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]", None, None)
.await?;
// Update row
let _updated = tables_db
.update_row(
"[DATABASE_ID]",
"[TABLE_ID]",
"[ROW_ID]",
Some(json!({ "done": true })),
None,
None,
)
.await?;
// Delete row
tables_db
.delete_row("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]", None)
.await?;
文字列カラムの型
注記: レガシーの
stringカラム型は非推奨です。新しいテーブルでは、明示的な文字列型を使用してください。
| 型 | 最大文字数 | インデックス作成 | ストレージ |
|---|---|---|---|
varchar | 16,383 | フルインデックス(サイズが 768 以下の場合) | 行内 |
text | 16,383 | プレフィックスのみ | 行外 |
mediumtext | 4,194,303 | プレフィックスのみ | 行外 |
longtext | 1,073,741,823 | プレフィックスのみ | 行外 |
varcharは行内に保存され、64 KB の行サイズ上限にカウントされます。名前、スラッグ、識別子など、短くインデックスを作成するフィールドに適しています。text、mediumtext、longtextは行外に保存されるため、行サイズの上限を消費しません。これらの型ではsizeは不要です。
use appwrite::enums::{OrderBy, TablesDBIndexType};
// Short, indexed string
let _title = tables_db
.create_varchar_column(
"[DATABASE_ID]",
"[TABLE_ID]",
"title",
255,
true,
None,
None,
None,
)
.await?;
// Off-page longer text
let _summary = tables_db
.create_text_column("[DATABASE_ID]", "[TABLE_ID]", "summary", false, None, None, None)
.await?;
let _body = tables_db
.create_mediumtext_column("[DATABASE_ID]", "[TABLE_ID]", "body", false, None, None, None)
.await?;
let _raw_data = tables_db
.create_longtext_column("[DATABASE_ID]", "[TABLE_ID]", "raw_data", false, None, None, None)
.await?;
// Index only the varchar column fully.
let _index = tables_db
.create_index(
"[DATABASE_ID]",
"[TABLE_ID]",
"title_idx",
TablesDBIndexType::Key,
vec!["title"],
Some(vec![OrderBy::Asc]),
Some(vec![255]),
)
.await?;
クエリメソッド
TablesDB メソッドは、クエリに Option<Vec<String>> を受け取ります。各 Query を文字列に変換してください。
use appwrite::query::Query;
use serde_json::Value;
let queries = vec![
Query::equal("status", "published").to_string(),
Query::not_equal("archived", true).to_string(),
Query::greater_than("views", 100).to_string(),
Query::less_than_equal("priority", 5).to_string(),
Query::between("score", 1, 100).to_string(),
Query::is_not_null("publishedAt").to_string(),
Query::search("title", "rust appwrite").to_string(),
Query::contains("tags", "rust").to_string(),
Query::order_desc("$createdAt").to_string(),
Query::limit(25).to_string(),
Query::offset(50).to_string(),
];
let multi_value = Query::equal(
"status",
Value::Array(vec![
Value::String("draft".to_string()),
Value::String("published".to_string()),
]),
)
.to_string();
let related = Query::relationship(
"comments",
vec![
Query::equal("approved", true).to_string(),
Query::limit(25).to_string(),
],
)
.to_string();
// Aggregations (pass "" for no alias)
let aggregations = vec![
Query::count("*", "total").to_string(),
Query::sum("amount", "revenue").to_string(),
Query::avg("score", "avgScore").to_string(),
Query::min("price", "minPrice").to_string(),
Query::max("price", "maxPrice").to_string(),
];
// Grouping
let grouping = vec![
Query::group_by(["status", "year"]).to_string(),
Query::having([Query::greater_than("total", 1).to_string()]).to_string(),
Query::distinct().to_string(),
];
// Joins (table id, left key, right key, operator, alias; "" for no alias)
let joins = vec![
Query::join("orders", "$id", "customerId", "=", "").to_string(),
Query::left_join("orders", "$id", "customerId", "=", "ord").to_string(),
Query::left_join_on(
"orders",
"ord",
[
Query::on("$id", "customerId", "=").to_string(),
Query::equal("ord.status", "paid").to_string(),
],
)
.to_string(),
];
// Fluent builder. Pass build() into list methods.
let queries = Query::builder()
.equal("status", "published")
.left_join("orders", "$id", "customerId", "=", "ord")
.count("*", "total")
.group_by(["status"])
.limit(25)
.build();
let _rows = tables_db
.list_rows("[DATABASE_ID]", "[TABLE_ID]", Some(queries), None, None, None)
.await?;
ファイルストレージ
use appwrite::id::ID;
use appwrite::input_file::InputFile;
use appwrite::permission::Permission;
use appwrite::query::Query;
use appwrite::role::Role;
use appwrite::services::Storage;
let storage = Storage::new(&client);
// Create bucket
let _bucket = storage
.create_bucket(
ID::unique(),
"Uploads",
Some(vec![
Permission::read(Role::any()).to_string(),
Permission::create(Role::users(None)).to_string(),
]),
Some(true),
Some(true),
Some(30_000_000),
Some(vec!["jpg".to_string(), "png".to_string(), "pdf".to_string()]),
None,
Some(true),
Some(true),
Some(true),
)
.await?;
// Upload from disk
let input = InputFile::from_path("avatar.png", Some("image/png")).await?;
let _file = storage
.create_file(
"[BUCKET_ID]",
ID::unique(),
input,
Some(vec![Permission::read(Role::any()).to_string()]),
)
.await?;
// Upload from bytes
let input = InputFile::from_bytes(b"hello".to_vec(), "hello.txt", Some("text/plain"));
let _file = storage.create_file("[BUCKET_ID]", ID::unique(), input, None).await?;
// List files
let _files = storage
.list_files("[BUCKET_ID]", Some(vec![Query::limit(10).to_string()]), None, Some(true))
.await?;
// Download file bytes
let _content = storage
.get_file_download("[BUCKET_ID]", "[FILE_ID]", None)
.await?;
// Delete file
storage.delete_file("[BUCKET_ID]", "[FILE_ID]").await?;
関数
use appwrite::enums::ExecutionMethod;
use appwrite::query::Query;
use appwrite::services::Functions;
use serde_json::json;
let functions = Functions::new(&client);
// List functions
let _functions = functions
.list(Some(vec![Query::limit(25).to_string()]), None, Some(true))
.await?;
// Execute function
let _execution = functions
.create_execution(
"[FUNCTION_ID]",
Some(r#"{"hello":"world"}"#),
Some(false),
Some("/jobs/sync"),
Some(ExecutionMethod::POST),
Some(json!({ "content-type": "application/json" })),
None,
)
.await?;
// Get execution
let _execution = functions
.get_execution("[FUNCTION_ID]", "[EXECUTION_ID]")
.await?;
権限
use appwrite::permission::Permission;
use appwrite::role::Role;
let permissions = vec![
Permission::read(Role::any()).to_string(),
Permission::create(Role::users(None)).to_string(),
Permission::update(Role::user("[USER_ID]", None)).to_string(),
Permission::delete(Role::team("[TEAM_ID]", Some("owner"))).to_string(),
];
エラー処理
match users.get("[USER_ID]").await {
Ok(user) => {
let _user = user;
}
Err(error) if error.status_code() == 404 => {
eprintln!("User not found: {}", error.get_message());
}
Err(error) => {
eprintln!("Appwrite error {}: {}", error.status_code(), error.get_message());
return Err(Box::new(error) as Box<dyn std::error::Error>);
}
}
よくある落とし穴
- 現在、Rust SDK はサーバー側 SDK です。ブラウザー/モバイルのクライアント認証フローでは、TypeScript/Web、Flutter、Apple、Android、または React Native SDK を使用してください。
- サービス呼び出しは必ず
awaitしてください。 - 使用しないオプションパラメーターには
Noneを渡してください。 - 新しいデータベースコードでは、レガシーの
DatabasesではなくTablesDBを使用してください。 Option<Vec<String>>を受け取る API に渡す前に、クエリを.to_string()で変換してください。- 行データと JSON 本文には
serde_json::json!({...})を使用してください。 - アップロードには
InputFile::from_path(...).await?またはInputFile::from_bytes(...)を使用してください。
コメント
Markdown 対応