skills/appwrite-rust/SKILL.md
Appwrite Rust SDK skill. Use when building server-side Rust applications with Appwrite. Covers async client setup with API keys, user management, TablesDB database/table/row operations, file storage, function executions, permissions, queries, and error handling. Uses the crates.io `appwrite` package and Tokio.
npx skillsauth add appwrite/agent-skills appwrite-rustInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
cargo add appwrite
cargo add tokio --features full
cargo add serde_json
Or add dependencies manually:
[dependencies]
appwrite = "0.3.0"
tokio = { version = "1", features = ["full"] }
serde_json = "1"
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")?);
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?;
Note: Use
TablesDBfor 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 as
Option<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?;
Note: The legacy
stringcolumn 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 |
varchar is stored inline and counts toward the 64 KB row size limit. Prefer it for short, indexed fields like names, slugs, and identifiers.text, mediumtext, and longtext are stored off-page, so they do not consume the row size budget. size is 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?;
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();
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>);
}
}
await service calls.None for optional parameters you are not using.TablesDB, not legacy Databases, for new database code..to_string() before passing them to APIs that expect Option<Vec<String>>.serde_json::json!({...}) for row data and JSON bodies.InputFile::from_path(...).await? or InputFile::from_bytes(...) for uploads.tools
Appwrite TypeScript SDK skill. Use when building browser-based JavaScript/TypeScript apps, React Native mobile apps, or server-side Node.js/Deno backends with Appwrite. Covers client-side auth (email, OAuth, anonymous), database queries, file uploads, real-time subscriptions, and server-side admin via API keys for user management, database administration, storage, and functions.
tools
Appwrite Swift SDK skill. Use when building native iOS, macOS, watchOS, or tvOS apps, or server-side Swift applications with Appwrite. Covers client-side auth (email, OAuth), database queries, file uploads, real-time subscriptions with async/await, and server-side admin via API keys for user management, database administration, storage, and functions.
development
Appwrite Ruby SDK skill. Use when building server-side Ruby applications with Appwrite, including Rails and Sinatra integrations. Covers user management, database/table CRUD, file storage, and functions via API keys.
development
Appwrite Python SDK skill. Use when building server-side Python applications with Appwrite, including Django, Flask, and FastAPI integrations. Covers user management, database/table CRUD, file storage, and functions via API keys.