agentsclimarketplace

Appwrite backend

Skill sgaabdu4/appwrite-backend/skills/appwrite-backend

Appwrite BaaS skill for AI agents. Covers TablesDB, Auth, Storage, Functions, Messaging, and Realtime in Dart, Python, and TypeScript.

Install
npx -y skills add sgaabdu4/appwrite-backend --skill appwrite-backend

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Appwrite backend development and operations. Use for Appwrite SDK work; any Appwrite CLI command or failure must route through the CLI safety branch.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

16.2 KB, as published. Nobody here has run it

Appwrite Development

Route

TriggerLoad before action
Any Appwrite CLI/wrapper command, deployment, schema sync, function-variable operation, or CLI failureappwrite-cli.md
Production schema/data/ACL/function cutoverproduction-migrations.md + CLI reference when CLI participates
TablesDB transaction or cross-service consistencytransactions.md + permissions.md
Self-hosted backup, restore, or data-loss incidentself-hosting-ops.md

Critical Rules

  1. Use official SDK packages only — Dart/Flutter/TypeScript/Python must use sdk-routing. Raw REST/GraphQL HTTP via fetch, requests, dio, package:http, curl, etc. is a violation unless the SDK lacks the endpoint or an isolated, tested Client.call works around SDK model parsing.
  2. Pin SDKs by target — Cloud: latest stable SDK. Self-hosted 1.9.x: dart_appwrite 25.1.0, Flutter appwrite 25.2.0, node-appwrite 26.2.0, web appwrite 26.1.0, Python appwrite 21.0.0, CLI 22.4.0.
  3. Use TablesDB API — Collections API deprecated 1.8.0
  4. Allocate Appwrite IDs once with ID.unique() — Appwrite resource IDs and surrogate entity IDs use the official SDK helper. Retryable create: call ID.unique() before the first attempt → persist the returned ID in the durable draft/intent → reuse that exact ID for every retry/reconciliation. Calling ID.unique() again on retry creates a second resource and breaks idempotency. Stable business/natural identity remains in indexed columns; never derive resource IDs from names, timestamps, slugs, hashes, or custom generators.
  5. Use Query.select() — Relationships return IDs only without explicit selection.
  6. Use cursor pagination — Offset degrades on large tables
  7. Use Operator for counters — Avoids race conditions
  8. Create indexes — Queries without scan entire tables
  9. Init outside handler — SDK/connections persist between warm invocations
  10. Group functions by domain — One per domain, not per op
  11. Event triggers over polling — One trigger replaces thousands of requests
  12. Use explicit string typesstring deprecated; use varchar or text/mediumtext/longtext
  13. Use appwrite generate — Type-safe SDK from schema
  14. Use Channel helpers — Type-safe realtime subs, not raw strings
  15. Use Realtime queries — Server-side event filtering, not client-side
  16. Async-start long-running Functions — Client createExecution calls for delete/sync/import/export/migrate/generate flows use async execution, then reconcile source-of-truth state with bounded polling/realtime/fetch. Do not block on backend completion; report destructive failures only after reconciliation proves the entity/account still exists.
  17. Guard schema pushesappwrite push tables reconciles remote TablesDB resources against the complete local manifest; omission means deletion. Production push requires appwrite-cli inventory + manifest guard PASS. push all, --all, or --force never substitutes for this gate.
  18. Stage production migrations — Additive expand → type-aware resumable backfill → compatible deployment → contract/read-back → consumer activation. Partial data/schema never activates downstream code. Use production-migrations.

CLI Quick Check (Top)

Any CLI/wrapper intent or failure → load appwrite-cli.md before installing, binding, probing, diagnosing, or mutating. Repository-pinned wrapper/version wins over this skill's generic pin. Never infer a command shape from another version.

Terminology (1.8.0+)

OldNew
CollectionsTables
DocumentsRows
AttributesColumns
DatabasesTablesDB

Setup

Package policy:

  • Cloud: latest stable official SDK.
  • Self-hosted 1.9.x: use Critical Rule 2 pins.
  • TypeScript/React browser: appwrite; TypeScript server/SSR/Functions: node-appwrite.
  • Python: appwrite; prefer keyword arguments for SDK calls.
  • Dart: appwrite for Flutter/client apps, dart_appwrite for server/Functions; prefer named parameters.
import 'package:dart_appwrite/dart_appwrite.dart';

final client = Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('<PROJECT_ID>')
    .setKey('<API_KEY>');

final tablesDB = TablesDB(client);
from appwrite.client import Client
from appwrite.services.tables_db import TablesDB
client = Client()
client.set_endpoint('https://cloud.appwrite.io/v1')
client.set_project('<PROJECT_ID>')
client.set_key('<API_KEY>')
tables_db = TablesDB(client)
import { Client, TablesDB } from 'node-appwrite';
const client = new Client()
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('<PROJECT_ID>')
    .setKey('<API_KEY>');
const tablesDB = new TablesDB(client);

TablesDB CRUD

// Create
await tablesDB.createRow(databaseId: 'db', tableId: 'users', rowId: ID.unique(),
    data: {'name': 'Alice'});

// Read
final rows = await tablesDB.listRows(databaseId: 'db', tableId: 'users',
    queries: [Query.equal('status', 'active'), Query.select(['name', 'email'])]);

// Update
await tablesDB.updateRow(databaseId: 'db', tableId: 'users', rowId: 'user_123',
    data: {'status': 'inactive'});

// Upsert
await tablesDB.upsertRow(databaseId: 'db', tableId: 'settings', rowId: 'prefs',
    data: {'theme': 'dark'});

// Delete
await tablesDB.deleteRow(databaseId: 'db', tableId: 'users', rowId: 'user_123');

Use SDK idioms:

  • TypeScript uses object parameters: tablesDB.createRow({ databaseId, tableId, rowId, data }).
  • Python uses keyword arguments: tables_db.create_row(database_id='db', table_id='users', row_id=ID.unique(), data={...}).
  • Dart uses named parameters as shown above.

Bulk: bulk-operations.md | Chunked ID queries: chunked-queries.md


Query Reference

Comparison: equal | notEqual | lessThan | lessThanEqual | greaterThan | greaterThanEqual | between | notBetween String: startsWith | endsWith | contains | search (+ not variants) Null: isNull | isNotNull · Logical: and([...]) | or([...]) Pagination: select | limit | cursorAfter | cursorBefore | orderAsc | orderDesc | orderRandom Timestamp: createdAfter | createdBefore | updatedAfter | updatedBefore Spatial: distanceEqual | distanceLessThan | distanceGreaterThan | intersects | overlaps | touches | crosses (+ not variants)

All prefixed Query.. Details: query-optimization.md


Operators (Atomic Updates)

data: {
    'likes': Operator.increment(1),
    'tags': Operator.arrayAppend(['trending']),
    'updatedAt': Operator.dateSetNow(),
}

Numeric: increment | decrement | multiply | divide Array: arrayAppend | arrayPrepend | arrayRemove | arrayUnique | arrayIntersect | arrayDiff Other: toggle | stringConcat | stringReplace | dateAddDays | dateSetNow

Details: atomic-operators.md


Column Types

TypeMax CharsIndexingUse
varchar16,383Full (if size < 768)Queryable short strings
text16,383Prefix onlyDescriptions, notes
mediumtext4,194,303Prefix onlyArticles
longtext1,073,741,823Prefix onlyLarge documents

string deprecated. Use varchar for queryable, text for non-indexed.

Other: integer | float | boolean | datetime | email | url | ip | enum | relationship | point | line | polygon

Details: schema-management.md


Performance

RuleImpact
Cursor pagination10-100x faster than offset
Pagination mixin (Dart)~50 lines saved per datasource
Query.select()12-18x faster for relationships
total: falseEliminates COUNT scan
Indexes100x faster on large tables
OperatorsNo race conditions
Bulk operationsN → 1 request
Delta syncFetches only changed rows

Details: performance.md, pagination-performance.md


Type-Safe SDK Generation

appwrite generate

Gen typed helpers from schema into generated/appwrite/. Autocomplete + compile checks. Regen after schema change. CLI flow: login -> init project -> pull -> generate -> push. Details: appwrite-cli


Authentication

Email/password, OAuth (50+ providers), phone, magic link, anon, email OTP, custom token. MFA: TOTP/email/phone/recovery. SSR sessions. JWT for functions. SSR cookie: a_session_<PROJECT_ID>. Admin client creates session. Per-request session client reads user context. Email policies can block free, aliased, or disposable emails at signup/update.

Details: authentication.md | auth-methods.md


Storage

Upload/download/preview w/ transforms (resize, format conversion). File tokens for shareable URLs. HEIC, AVIF, WebP supported. SDKs handle chunking/parallel chunk uploads; do not hand-roll upload HTTP.

Details: storage-files.md


Realtime

final sub = realtime.subscribe(['tablesdb.db.tables.posts.rows']);
sub.stream.listen((e) => print(e.events));

Channels: account | tablesdb.<DB>.tables.<TABLE>.rows | buckets.<BUCKET>.files | presences

Channel helpers (preferred): Channel class for type-safe subs w/ IDE autocomplete:

import { Client, Realtime, Channel, Query } from "appwrite";
const sub = await realtime.subscribe(
    Channel.tablesdb('<DB>').table('<TABLE>').row(),
    response => console.log(response.payload),
    [Query.equal('status', ['active'])]  // server-side filtering
);

Use Presences API for online/typing/active state when supported; avoid durable DB rows + cleanup cron for ephemeral status.

Details: realtime.md


Functions

Init SDK outside handler. Group by domain. Event triggers, not polling. Functions: self-hosted uses Rule 2 Dart pin; Cloud uses latest SDK/runtime.

Details: functions.md | functions-advanced.md


Transactions

final tx = await tablesDB.createTransaction(ttl: 300);
await tablesDB.createRow(..., transactionId: tx.$id);
await tablesDB.updateTransaction(transactionId: tx.$id, commit: true);

Details: transactions.md


Relationships

await tablesDB.listRows(databaseId: 'db', tableId: 'posts',
    queries: [Query.equal('author.country', 'US'), Query.select(['title', 'author.name'])]);

Types: oneToOne | oneToMany | manyToOne | manyToMany

Details: relationships.md


Permissions

permissions: [
    Permission.read(Role.any()),
    Permission.update(Role.user(userId)),
    Permission.delete(Role.team('admin')),
    Permission.create(Role.label('premium')),
]

Default: Server SDK/Console create = empty resource ACL; Client SDK create = creator read/update/delete. Pass explicit permissions whenever ACL correctness matters. Use row/file perms for per-resource ACL. If all resources share rules, set table/bucket perms, leave row/file perms empty. write = create + update + delete Avoid: missing perms = lockout; Role.any() + write/update/delete = public mutation; Permission.read(Role.any()) on sensitive data = public leak. Roles: any() | guests() | users() | user(id) | team(id) | team(id, role) | label(name) Details: permissions | teams | storage-files


Limits

Default page: 25 · Bulk: 1000 rows · Query.equal(): 100 values · Nesting: 3 levels · Queries/req: 100 · Timeout: 15s

Error Codes

400 Bad request · 401 Unauthorized · 403 Forbidden · 404 Not found · 409 Conflict · 429 Rate limited (client SDKs only) Catch AppwriteException. 429 -> exponential backoff.

Details: error-handling.md


Anti-Patterns

WrongRightWhy
N+1 queriesQuery.select(['col', 'relation.col'])Kills extra round-trips
Read-modify-writeOperator.increment()Race condition
Large offsetsQuery.cursorAfter(id)O(n) vs O(1)
Skip totalstotal: falseKills COUNT scan
Missing indexesCreate for queried columnsQueries scan entire table
SDK init inside handlerInit outside for warm reuseRepeated setup each call
Hardcoded secretsEnv varsSecurity risk
PollingRealtime or event triggersWasted executions
Client-side filteringRealtime queriesServer does work
Raw channel stringsChannel helpersTypos, no autocomplete
ColumnStringColumnVarchar or ColumnTextstring deprecated
Hand-writing typesappwrite generateSchema drift, no autocomplete
databases.listDocuments()tablesDB.listRows()Deprecated API
Raw Appwrite HTTP (fetch, requests, dio, package:http, curl)Official SDK packageVersion drift, auth mistakes, lost typed APIs
Derived/custom resource ID or fresh ID.unique() per retryPreallocate one ID.unique(), persist, reuseLeakage/collision or duplicate resource
Full re-fetch every syncQuery.updatedAfter() + per-table timestampsWastes bandwidth, slow
Loop w/ createRow()createRows() bulkN requests vs 1

Cost Optimization

  1. Query.select() — cuts bandwidth
  2. Cursor pagination + total: false — fastest queries
  3. Realtime over polling — one connection vs repeated calls
  4. Batch ops — 1 execution vs N
  5. WebP quality 80 — smallest files, universal support
  6. Init outside handler — fewer cold starts
  7. Budget cap — Organization → Billing → Budget cap

Details: cost-optimization.md


Reference Files

Data: schema-management · production-migrations · query-optimization · atomic-operators · relationships · transactions · bulk-operations · chunked-queries Performance: performance · pagination-performance · cost-optimization Auth: authentication · auth-methods · permissions · teams Services: storage-files · functions · functions-advanced · realtime · messaging · webhooks · avatars · graphql · locale Tooling: sdk-routing · appwrite-cli Platform: error-handling · limits · health · self-hosting · self-hosting-ops


Resources

Docs: https://appwrite.io/docs · API: https://appwrite.io/docs/references · SDKs: https://github.com/appwrite

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.