
Rust
- 1 installs
- 10 repo stars
- Updated July 29, 2026
- appwrite/claude-plugin
Appwrite Rust SDK skill is a Claude Code skill that guides building async server-side Rust applications against the Appwrite backend-as-a-service using the crates.io appwrite package and Tokio.
About
This skill documents the Appwrite Rust SDK for server-side Rust applications. A developer uses it to set up an async client with Tokio and an API key, then manage users, run TablesDB database and row operations, handle permissions and queries, upload files, and execute functions. It captures the Rust-specific calling convention where optional parameters are Option<T> and methods are positional and async.
- Async server-side Rust client using the crates.io appwrite package and Tokio
- User management, TablesDB operations, storage, function executions, permissions, and error handling
- Documents positional Option<T> calling convention specific to the Rust SDK
Rust by the numbers
- 1 all-time installs (skills.sh)
- Ranked #3,830 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
rust capabilities & compatibility
Requires an Appwrite project and API key; Appwrite Cloud has a free tier.
- Capabilities
- appwrite sdk python · appwrite sdk typescript · backend integration
- Works with
- github
- Use cases
- api development · database
- Pricing
- Bring your own API key
What rust says it does
Uses the crates.io `appwrite` package and Tokio.
The Rust SDK is async. Use it from a Tokio runtime and authenticate server-side with an API key.
npx skills add https://github.com/appwrite/claude-plugin --skill rustAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 10 |
| Last updated | July 29, 2026 |
| Repository | appwrite/claude-plugin ↗ |
What it does
Build an async Rust backend that manages users, data, storage, and functions on Appwrite.
Who is it for?
Rust developers building async Appwrite backends with Tokio.
Skip if: Client-only or non-Appwrite backends; there is no browser client here.
When should I use this skill?
Building server-side Rust applications with Appwrite.
What you get
- Async Appwrite Rust client skeleton
- TablesDB and users code
By the numbers
- appwrite crate version 0.3.0 pinned in the example
- 4 string column types documented
Files
Appwrite Rust SDK
Installation
cargo add appwrite
cargo add tokio --features full
cargo add serde_jsonOr add dependencies manually:
[dependencies]
appwrite = "0.3.0"
tokio = { version = "1", features = ["full"] }
serde_json = "1"Setting Up the Client
The Rust SDK is async. Use it from a Tokio runtime and authenticate server-side with an 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")?);Complete server skeleton
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(())
}Code Examples
User Management
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("user@example.com"),
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?;Database Operations
Note: UseTablesDBfor new code. Only useDatabasesif the existing project explicitly depends on the legacy Databases API.
>
Rust SDK calling convention: Methods are async, use positional parameters, and represent optional API parameters asOption<T>. PassNonefor optional values you are not setting.
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 Column Types
Note: The legacy string column type is deprecated. Use explicit string column types for new tables.| Type | Max characters | Indexing | Storage |
|---|---|---|---|
varchar | 16,383 | Full index (if size <= 768) | Inline in row |
text | 16,383 | Prefix only | Off-page |
mediumtext | 4,194,303 | Prefix only | Off-page |
longtext | 1,073,741,823 | Prefix only | Off-page |
varcharis stored inline and counts toward the 64 KB row size limit. Prefer it for short, indexed fields like names, slugs, and identifiers.text,mediumtext, andlongtextare stored off-page, so they do not consume the row size budget.sizeis not required for these types.
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?;Query Methods
TablesDB methods accept Option<Vec<String>> for queries. Convert each Query to a string.
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();File Storage
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?;Functions
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?;Permissions
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(),
];Error Handling
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>);
}
}Common Pitfalls
- The Rust SDK is currently a server-side SDK. Prefer TypeScript/Web, Flutter, Apple, Android, or React Native SDKs for browser/mobile client auth flows.
- Always
awaitservice calls. - Pass
Nonefor optional parameters you are not using. - Use
TablesDB, not legacyDatabases, for new database code. - Convert queries with
.to_string()before passing them to APIs that expectOption<Vec<String>>. - Use
serde_json::json!({...})for row data and JSON bodies. - Use
InputFile::from_path(...).await?orInputFile::from_bytes(...)for uploads.
Related skills
FAQ
Is the Rust SDK async?
Yes, the Rust SDK is async and is used from a Tokio runtime with server-side API key auth.
How are optional params handled?
Methods use positional parameters and represent optional API parameters as Option<T>; pass None for values you are not setting.