mcpField
A
appwrite-rust

appwrite-rust

github.com/appwrite开发28406 次安装1 周前更新
代码仓库

关于 appwrite-rust

面向服务端 Rust 开发者的 Appwrite 异步 SDK 使用指南,介绍用户、数据库、存储和函数等服务的调用方式。

主要功能

  1. 01异步服务端客户端
  2. 02用户管理
  3. 03TablesDB 数据操作
  4. 04文件存储与函数调用
  5. 05查询、权限及错误处理

适用场景

  1. 01在 Rust 服务中管理 Appwrite 用户
  2. 02创建表并查询数据
  3. 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 key 进行身份验证。

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 调用约定: 方法是异步的,使用位置参数,并通过 Option<T> 表示可选 API 参数。对于不设置的可选值,请传入 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 列类型已弃用。新建表时请使用明确指定的字符串列类型。

类型最大字符数索引存储
varchar16,383完整索引(如果大小 <= 768)行内
text16,383仅前缀页外
mediumtext4,194,303仅前缀页外
longtext1,073,741,823仅前缀页外
  • varchar 以行内方式存储,并计入 64 KB 的行大小限制。对于名称、slug 和标识符等较短且需要索引的字段,优先使用它。
  • 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。
  • 新的数据库代码请使用 TablesDB,不要使用旧版 Databases。
  • 将查询传递给需要 Option<Vec<String>> 的 API 之前,请使用 .to_string() 转换。
  • 行数据和 JSON 请求体请使用 serde_json::json!({...})。
  • 上传时请使用 InputFile::from_path(...).await? 或 InputFile::from_bytes(...)。

评论

支持 Markdown