← AppwriteCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to Appwrite
Snapshot Sep 30, 2026 · 23:07 UTC · version 1.0.0
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"name": "appwrite-rust",
"description": "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.",
"included_files": [],
"skill_md_contents": "---\r\nname: appwrite-rust\r\ndescription: 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.\r\n---\r\n\r\n\r\n# Appwrite Rust SDK\r\n\r\n## Installation\r\n\r\n```bash\r\ncargo add appwrite\r\ncargo add tokio --features full\r\ncargo add serde_json\r\n```\r\n\r\nOr add dependencies manually:\r\n\r\n```toml\r\n[dependencies]\r\nappwrite = \"0.3.0\"\r\ntokio = { version = \"1\", features = [\"full\"] }\r\nserde_json = \"1\"\r\n```\r\n\r\n## Setting Up the Client\r\n\r\nThe Rust SDK is async. Use it from a Tokio runtime and authenticate server-side with an API key.\r\n\r\n```rust\r\nuse appwrite::Client;\r\nuse std::env;\r\n\r\nlet client = Client::new()\r\n .set_endpoint(\"https://<REGION>.cloud.appwrite.io/v1\")\r\n .set_project(env::var(\"APPWRITE_PROJECT_ID\")?)\r\n .set_key(env::var(\"APPWRITE_API_KEY\")?);\r\n```\r\n\r\n### Complete server skeleton\r\n\r\n```rust\r\nuse appwrite::query::Query;\r\nuse appwrite::services::Users;\r\nuse appwrite::Client;\r\nuse std::env;\r\n\r\n#[tokio::main]\r\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\r\n let client = Client::new()\r\n .set_endpoint(\"https://<REGION>.cloud.appwrite.io/v1\")\r\n .set_project(env::var(\"APPWRITE_PROJECT_ID\")?)\r\n .set_key(env::var(\"APPWRITE_API_KEY\")?);\r\n\r\n let users = Users::new(&client);\r\n let _list = users\r\n .list(Some(vec![Query::limit(25).to_string()]), None, Some(true))\r\n .await?;\r\n\r\n Ok(())\r\n}\r\n```\r\n\r\n## Code Examples\r\n\r\n### User Management\r\n\r\n```rust\r\nuse appwrite::id::ID;\r\nuse appwrite::query::Query;\r\nuse appwrite::services::Users;\r\n\r\nlet users = Users::new(&client);\r\n\r\n// Create user\r\nlet _user = users\r\n .create(\r\n ID::unique(),\r\n Some(\"user@example.com\"),\r\n None,\r\n Some(\"password123\"),\r\n Some(\"User Name\"),\r\n )\r\n .await?;\r\n\r\n// List users\r\nlet _list = users\r\n .list(Some(vec![Query::limit(25).to_string()]), None, Some(true))\r\n .await?;\r\n\r\n// Get user\r\nlet _fetched = users.get(\"[USER_ID]\").await?;\r\n\r\n// Delete user\r\nusers.delete(\"[USER_ID]\").await?;\r\n```\r\n\r\n### Database Operations\r\n\r\n> **Note:** Use `TablesDB` for new code. Only use `Databases` if the existing project explicitly depends on the legacy Databases API.\r\n>\r\n> **Rust SDK calling convention:** Methods are async, use positional parameters, and represent optional API parameters as `Option<T>`. Pass `None` for optional values you are not setting.\r\n\r\n```rust\r\nuse appwrite::id::ID;\r\nuse appwrite::permission::Permission;\r\nuse appwrite::query::Query;\r\nuse appwrite::role::Role;\r\nuse appwrite::services::TablesDB;\r\nuse serde_json::json;\r\n\r\nlet tables_db = TablesDB::new(&client);\r\n\r\n// Create database\r\nlet _database = tables_db\r\n .create(ID::unique(), \"My Database\", Some(true))\r\n .await?;\r\n\r\n// Create table\r\nlet _table = tables_db\r\n .create_table(\r\n \"[DATABASE_ID]\",\r\n ID::unique(),\r\n \"articles\",\r\n Some(vec![\r\n Permission::read(Role::any()).to_string(),\r\n Permission::create(Role::users(None)).to_string(),\r\n ]),\r\n Some(true),\r\n Some(true),\r\n None,\r\n None,\r\n )\r\n .await?;\r\n\r\n// Create row\r\nlet _row = tables_db\r\n .create_row(\r\n \"[DATABASE_ID]\",\r\n \"[TABLE_ID]\",\r\n ID::unique(),\r\n json!({\r\n \"title\": \"Hello World\",\r\n \"done\": false\r\n }),\r\n None,\r\n None,\r\n )\r\n .await?;\r\n\r\n// Query rows\r\nlet _rows = tables_db\r\n .list_rows(\r\n \"[DATABASE_ID]\",\r\n \"[TABLE_ID]\",\r\n Some(vec![\r\n Query::equal(\"done\", false).to_string(),\r\n Query::limit(10).to_string(),\r\n ]),\r\n None,\r\n Some(true),\r\n None,\r\n )\r\n .await?;\r\n\r\n// Get row\r\nlet _row = tables_db\r\n .get_row(\"[DATABASE_ID]\", \"[TABLE_ID]\", \"[ROW_ID]\", None, None)\r\n .await?;\r\n\r\n// Update row\r\nlet _updated = tables_db\r\n .update_row(\r\n \"[DATABASE_ID]\",\r\n \"[TABLE_ID]\",\r\n \"[ROW_ID]\",\r\n Some(json!({ \"done\": true })),\r\n None,\r\n None,\r\n )\r\n .await?;\r\n\r\n// Delete row\r\ntables_db\r\n .delete_row(\"[DATABASE_ID]\", \"[TABLE_ID]\", \"[ROW_ID]\", None)\r\n .await?;\r\n```\r\n\r\n#### String Column Types\r\n\r\n> **Note:** The legacy `string` column type is deprecated. Use explicit string column types for new tables.\r\n\r\n| Type | Max characters | Indexing | Storage |\r\n|------|---------------|----------|---------|\r\n| `varchar` | 16,383 | Full index (if size <= 768) | Inline in row |\r\n| `text` | 16,383 | Prefix only | Off-page |\r\n| `mediumtext` | 4,194,303 | Prefix only | Off-page |\r\n| `longtext` | 1,073,741,823 | Prefix only | Off-page |\r\n\r\n- `varchar` is stored inline and counts toward the 64 KB row size limit. Prefer it for short, indexed fields like names, slugs, and identifiers.\r\n- `text`, `mediumtext`, and `longtext` are stored off-page, so they do not consume the row size budget. `size` is not required for these types.\r\n\r\n```rust\r\nuse appwrite::enums::{OrderBy, TablesDBIndexType};\r\n\r\n// Short, indexed string\r\nlet _title = tables_db\r\n .create_varchar_column(\r\n \"[DATABASE_ID]\",\r\n \"[TABLE_ID]\",\r\n \"title\",\r\n 255,\r\n true,\r\n None,\r\n None,\r\n None,\r\n )\r\n .await?;\r\n\r\n// Off-page longer text\r\nlet _summary = tables_db\r\n .create_text_column(\"[DATABASE_ID]\", \"[TABLE_ID]\", \"summary\", false, None, None, None)\r\n .await?;\r\n\r\nlet _body = tables_db\r\n .create_mediumtext_column(\"[DATABASE_ID]\", \"[TABLE_ID]\", \"body\", false, None, None, None)\r\n .await?;\r\n\r\nlet _raw_data = tables_db\r\n .create_longtext_column(\"[DATABASE_ID]\", \"[TABLE_ID]\", \"raw_data\", false, None, None, None)\r\n .await?;\r\n\r\n// Index only the varchar column fully.\r\nlet _index = tables_db\r\n .create_index(\r\n \"[DATABASE_ID]\",\r\n \"[TABLE_ID]\",\r\n \"title_idx\",\r\n TablesDBIndexType::Key,\r\n vec![\"title\"],\r\n Some(vec![OrderBy::Asc]),\r\n Some(vec![255]),\r\n )\r\n .await?;\r\n```\r\n\r\n### Query Methods\r\n\r\n`TablesDB` methods accept `Option<Vec<String>>` for queries. Convert each `Query` to a string.\r\n\r\n```rust\r\nuse appwrite::query::Query;\r\nuse serde_json::Value;\r\n\r\nlet queries = vec![\r\n Query::equal(\"status\", \"published\").to_string(),\r\n Query::not_equal(\"archived\", true).to_string(),\r\n Query::greater_than(\"views\", 100).to_string(),\r\n Query::less_than_equal(\"priority\", 5).to_string(),\r\n Query::between(\"score\", 1, 100).to_string(),\r\n Query::is_not_null(\"publishedAt\").to_string(),\r\n Query::search(\"title\", \"rust appwrite\").to_string(),\r\n Query::contains(\"tags\", \"rust\").to_string(),\r\n Query::order_desc(\"$createdAt\").to_string(),\r\n Query::limit(25).to_string(),\r\n Query::offset(50).to_string(),\r\n];\r\n\r\nlet multi_value = Query::equal(\r\n \"status\",\r\n Value::Array(vec![\r\n Value::String(\"draft\".to_string()),\r\n Value::String(\"published\".to_string()),\r\n ]),\r\n)\r\n.to_string();\r\n```\r\n\r\n### File Storage\r\n\r\n```rust\r\nuse appwrite::id::ID;\r\nuse appwrite::input_file::InputFile;\r\nuse appwrite::permission::Permission;\r\nuse appwrite::query::Query;\r\nuse appwrite::role::Role;\r\nuse appwrite::services::Storage;\r\n\r\nlet storage = Storage::new(&client);\r\n\r\n// Create bucket\r\nlet _bucket = storage\r\n .create_bucket(\r\n ID::unique(),\r\n \"Uploads\",\r\n Some(vec![\r\n Permission::read(Role::any()).to_string(),\r\n Permission::create(Role::users(None)).to_string(),\r\n ]),\r\n Some(true),\r\n Some(true),\r\n Some(30_000_000),\r\n Some(vec![\"jpg\".to_string(), \"png\".to_string(), \"pdf\".to_string()]),\r\n None,\r\n Some(true),\r\n Some(true),\r\n Some(true),\r\n )\r\n .await?;\r\n\r\n// Upload from disk\r\nlet input = InputFile::from_path(\"avatar.png\", Some(\"image/png\")).await?;\r\nlet _file = storage\r\n .create_file(\r\n \"[BUCKET_ID]\",\r\n ID::unique(),\r\n input,\r\n Some(vec![Permission::read(Role::any()).to_string()]),\r\n )\r\n .await?;\r\n\r\n// Upload from bytes\r\nlet input = InputFile::from_bytes(b\"hello\".to_vec(), \"hello.txt\", Some(\"text/plain\"));\r\nlet _file = storage.create_file(\"[BUCKET_ID]\", ID::unique(), input, None).await?;\r\n\r\n// List files\r\nlet _files = storage\r\n .list_files(\"[BUCKET_ID]\", Some(vec![Query::limit(10).to_string()]), None, Some(true))\r\n .await?;\r\n\r\n// Download file bytes\r\nlet _content = storage\r\n .get_file_download(\"[BUCKET_ID]\", \"[FILE_ID]\", None)\r\n .await?;\r\n\r\n// Delete file\r\nstorage.delete_file(\"[BUCKET_ID]\", \"[FILE_ID]\").await?;\r\n```\r\n\r\n### Functions\r\n\r\n```rust\r\nuse appwrite::enums::ExecutionMethod;\r\nuse appwrite::query::Query;\r\nuse appwrite::services::Functions;\r\nuse serde_json::json;\r\n\r\nlet functions = Functions::new(&client);\r\n\r\n// List functions\r\nlet _functions = functions\r\n .list(Some(vec![Query::limit(25).to_string()]), None, Some(true))\r\n .await?;\r\n\r\n// Execute function\r\nlet _execution = functions\r\n .create_execution(\r\n \"[FUNCTION_ID]\",\r\n Some(r#\"{\"hello\":\"world\"}\"#),\r\n Some(false),\r\n Some(\"/jobs/sync\"),\r\n Some(ExecutionMethod::POST),\r\n Some(json!({ \"content-type\": \"application/json\" })),\r\n None,\r\n )\r\n .await?;\r\n\r\n// Get execution\r\nlet _execution = functions\r\n .get_execution(\"[FUNCTION_ID]\", \"[EXECUTION_ID]\")\r\n .await?;\r\n```\r\n\r\n### Permissions\r\n\r\n```rust\r\nuse appwrite::permission::Permission;\r\nuse appwrite::role::Role;\r\n\r\nlet permissions = vec![\r\n Permission::read(Role::any()).to_string(),\r\n Permission::create(Role::users(None)).to_string(),\r\n Permission::update(Role::user(\"[USER_ID]\", None)).to_string(),\r\n Permission::delete(Role::team(\"[TEAM_ID]\", Some(\"owner\"))).to_string(),\r\n];\r\n```\r\n\r\n### Error Handling\r\n\r\n```rust\r\nmatch users.get(\"[USER_ID]\").await {\r\n Ok(user) => {\r\n let _user = user;\r\n }\r\n Err(error) if error.status_code() == 404 => {\r\n eprintln!(\"User not found: {}\", error.get_message());\r\n }\r\n Err(error) => {\r\n eprintln!(\"Appwrite error {}: {}\", error.status_code(), error.get_message());\r\n return Err(Box::new(error) as Box<dyn std::error::Error>);\r\n }\r\n}\r\n```\r\n\r\n## Common Pitfalls\r\n\r\n- 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.\r\n- Always `await` service calls.\r\n- Pass `None` for optional parameters you are not using.\r\n- Use `TablesDB`, not legacy `Databases`, for new database code.\r\n- Convert queries with `.to_string()` before passing them to APIs that expect `Option<Vec<String>>`.\r\n- Use `serde_json::json!({...})` for row data and JSON bodies.\r\n- Use `InputFile::from_path(...).await?` or `InputFile::from_bytes(...)` for uploads.\r\n"
}SHA-256: f4211f9941622ce8dd6ba41c2aca2eb7b5b6f4f08903fd101a2ede13bc7a261f