← AppwriteCONTENT HISTORY

Update to Appwrite

Snapshot Sep 30, 2026 · 23:07 UTC · version 1.0.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "appwrite-go",
  "description": "Appwrite Go SDK skill. Use when building server-side Go applications with Appwrite. Covers user management, database/table CRUD, file storage, and functions via API keys. Uses per-service packages and functional options pattern.",
  "included_files": [],
  "skill_md_contents": "---\r\nname: appwrite-go\r\ndescription: Appwrite Go SDK skill. Use when building server-side Go applications with Appwrite. Covers user management, database/table CRUD, file storage, and functions via API keys. Uses per-service packages and functional options pattern.\r\n---\r\n\r\n\r\n# Appwrite Go SDK\r\n\r\n## Installation\r\n\r\n```bash\r\ngo get github.com/appwrite/sdk-for-go\r\n```\r\n\r\n## Setting Up the Client\r\n\r\n```go\r\nimport (\r\n    \"os\"\r\n\r\n    \"github.com/appwrite/sdk-for-go/client\"\r\n    \"github.com/appwrite/sdk-for-go/id\"\r\n    \"github.com/appwrite/sdk-for-go/users\"\r\n    \"github.com/appwrite/sdk-for-go/tablesdb\"\r\n    \"github.com/appwrite/sdk-for-go/storage\"\r\n)\r\n\r\nclt := client.New(\r\n    client.WithEndpoint(\"https://<REGION>.cloud.appwrite.io/v1\"),\r\n    client.WithProject(os.Getenv(\"APPWRITE_PROJECT_ID\")),\r\n    client.WithKey(os.Getenv(\"APPWRITE_API_KEY\")),\r\n)\r\n```\r\n\r\n## Code Examples\r\n\r\n### User Management\r\n\r\n```go\r\nservice := users.New(clt)\r\n\r\n// Create user\r\nuser, err := service.Create(\r\n    id.Unique(),\r\n    \"user@example.com\",\r\n    \"password123\",\r\n    users.WithCreateName(\"User Name\"),\r\n)\r\n\r\n// List users\r\nlist, err := service.List()\r\n\r\n// Get user\r\nfetched, err := service.Get(\"[USER_ID]\")\r\n\r\n// Delete user\r\n_, err = service.Delete(\"[USER_ID]\")\r\n```\r\n\r\n### Database Operations\r\n\r\n> **Note:** Use `TablesDB` (not the deprecated `Databases` class) for all new code. Only use `Databases` if the existing codebase already relies on it or the user explicitly requests it.\r\n>\r\n> **Tip:** Prefer explicit functional option parameters (e.g., `tablesdb.WithUpdateRowData(...)`) over bare positional arguments where available. Only use positional-only style if the existing codebase already uses it or the user explicitly requests it.\r\n\r\n```go\r\nservice := tablesdb.New(clt)\r\n\r\n// Create database\r\ndb, err := service.Create(id.Unique(), \"My Database\")\r\n\r\n// Create row\r\ndoc, err := service.CreateRow(\r\n    \"[DATABASE_ID]\",\r\n    \"[TABLE_ID]\",\r\n    id.Unique(),\r\n    map[string]interface{}{\"title\": \"Hello World\"},\r\n)\r\n\r\n// List rows\r\nresults, err := service.ListRows(\"[DATABASE_ID]\", \"[TABLE_ID]\")\r\n\r\n// Get row\r\nrow, err := service.GetRow(\"[DATABASE_ID]\", \"[TABLE_ID]\", \"[ROW_ID]\")\r\n\r\n// Update row\r\n_, err = service.UpdateRow(\r\n    \"[DATABASE_ID]\",\r\n    \"[TABLE_ID]\",\r\n    \"[ROW_ID]\",\r\n    tablesdb.WithUpdateRowData(map[string]interface{}{\"title\": \"Updated\"}),\r\n)\r\n\r\n// Delete row\r\n_, err = service.DeleteRow(\"[DATABASE_ID]\", \"[TABLE_ID]\", \"[ROW_ID]\")\r\n```\r\n\r\n#### String Column Types\r\n\r\n> **Note:** The legacy `string` type is deprecated. Use explicit column types for all new columns.\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 towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.\r\n- `text`, `mediumtext`, and `longtext` are stored off-page (only a 20-byte pointer lives in the row), so they don't consume the row size budget. `size` is not required for these types.\r\n\r\n```go\r\n// Create table with explicit string column types\r\n_, err = service.CreateTable(\r\n    \"[DATABASE_ID]\",\r\n    id.Unique(),\r\n    \"articles\",\r\n    tablesdb.WithCreateTableColumns([]map[string]interface{}{\r\n        {\"key\": \"title\",    \"type\": \"varchar\",    \"size\": 255, \"required\": true},\r\n        {\"key\": \"summary\",  \"type\": \"text\",                    \"required\": false},\r\n        {\"key\": \"body\",     \"type\": \"mediumtext\",              \"required\": false},\r\n        {\"key\": \"raw_data\", \"type\": \"longtext\",                \"required\": false},\r\n    }),\r\n)\r\n```\r\n\r\n### Query Methods\r\n\r\n```go\r\nimport \"github.com/appwrite/sdk-for-go/query\"\r\n\r\n// Filtering\r\nquery.Equal(\"field\", \"value\")             // == (or pass slice for IN)\r\nquery.NotEqual(\"field\", \"value\")          // !=\r\nquery.LessThan(\"field\", 100)             // <\r\nquery.LessThanEqual(\"field\", 100)        // <=\r\nquery.GreaterThan(\"field\", 100)          // >\r\nquery.GreaterThanEqual(\"field\", 100)     // >=\r\nquery.Between(\"field\", 1, 100)           // 1 <= field <= 100\r\nquery.IsNull(\"field\")                    // is null\r\nquery.IsNotNull(\"field\")                 // is not null\r\nquery.StartsWith(\"field\", \"prefix\")      // starts with\r\nquery.EndsWith(\"field\", \"suffix\")        // ends with\r\nquery.Contains(\"field\", \"sub\")           // contains\r\nquery.Search(\"field\", \"keywords\")        // full-text search (requires index)\r\n\r\n// Sorting\r\nquery.OrderAsc(\"field\")\r\nquery.OrderDesc(\"field\")\r\n\r\n// Pagination\r\nquery.Limit(25)                          // max rows (default 25, max 100)\r\nquery.Offset(0)                          // skip N rows\r\nquery.CursorAfter(\"[ROW_ID]\")            // cursor pagination (preferred)\r\nquery.CursorBefore(\"[ROW_ID]\")\r\n\r\n// Selection & Logic\r\nquery.Select([]string{\"field1\", \"field2\"})\r\nquery.Or([]string{query.Equal(\"a\", 1), query.Equal(\"b\", 2)})    // OR\r\nquery.And([]string{query.GreaterThan(\"age\", 18), query.LessThan(\"age\", 65)})  // AND (default)\r\n```\r\n\r\n### File Storage\r\n\r\n```go\r\nimport \"github.com/appwrite/sdk-for-go/file\"\r\n\r\nservice := storage.New(clt)\r\n\r\n// Upload file\r\nf, err := service.CreateFile(\r\n    \"[BUCKET_ID]\",\r\n    \"[FILE_ID]\",\r\n    file.NewInputFile(\"/path/to/file.png\", \"file.png\"),\r\n)\r\n\r\n// List files\r\nfiles, err := service.ListFiles(\"[BUCKET_ID]\")\r\n\r\n// Delete file\r\n_, err = service.DeleteFile(\"[BUCKET_ID]\", \"[FILE_ID]\")\r\n```\r\n\r\n#### InputFile Factory Methods\r\n\r\n```go\r\nimport \"github.com/appwrite/sdk-for-go/file\"\r\n\r\nfile.NewInputFile(\"/path/to/file.png\", \"file.png\")          // from filesystem path\r\nfile.NewInputFileFromReader(reader, \"file.png\", size)        // from io.Reader (size required)\r\nfile.NewInputFileFromBytes(data, \"file.png\")                 // from []byte\r\n```\r\n\r\n### Teams\r\n\r\n```go\r\nimport \"github.com/appwrite/sdk-for-go/teams\"\r\n\r\nsvc := teams.New(clt)\r\n\r\n// Create team\r\nteam, err := svc.Create(id.Unique(), \"Engineering\")\r\n\r\n// List teams\r\nlist, err := svc.List()\r\n\r\n// Create membership (invite user by email)\r\nmembership, err := svc.CreateMembership(\r\n    \"[TEAM_ID]\",\r\n    []string{\"editor\"},\r\n    teams.WithCreateMembershipEmail(\"user@example.com\"),\r\n)\r\n\r\n// List memberships\r\nmembers, err := svc.ListMemberships(\"[TEAM_ID]\")\r\n\r\n// Update membership roles\r\n_, err = svc.UpdateMembership(\"[TEAM_ID]\", \"[MEMBERSHIP_ID]\", []string{\"admin\"})\r\n\r\n// Delete team\r\n_, err = svc.Delete(\"[TEAM_ID]\")\r\n```\r\n\r\n> **Role-based access:** Use `role.Team(\"[TEAM_ID]\")` for all team members or `role.Team(\"[TEAM_ID]\", \"editor\")` for a specific team role when setting permissions.\r\n\r\n### Serverless Functions\r\n\r\n```go\r\nimport \"github.com/appwrite/sdk-for-go/functions\"\r\n\r\nsvc := functions.New(clt)\r\n\r\n// Execute function\r\nexecution, err := svc.CreateExecution(\r\n    \"[FUNCTION_ID]\",\r\n    functions.WithCreateExecutionBody(`{\"key\": \"value\"}`),\r\n)\r\n\r\n// List executions\r\nexecutions, err := svc.ListExecutions(\"[FUNCTION_ID]\")\r\n```\r\n\r\n#### Writing a Function Handler (Go runtime)\r\n\r\n```go\r\n// src/main.go — Appwrite Function entry point\r\npackage handler\r\n\r\nimport (\r\n    \"github.com/open-runtimes/types-for-go/v4/openruntimes\"\r\n)\r\n\r\nfunc Main(context openruntimes.Context) openruntimes.Response {\r\n    // context.Req.Body        — raw body (string)\r\n    // context.Req.BodyJson    — parsed JSON (map[string]interface{})\r\n    // context.Req.Headers     — headers (map[string]string)\r\n    // context.Req.Method      — HTTP method\r\n    // context.Req.Path        — URL path\r\n    // context.Req.Query       — query params (map[string]string)\r\n\r\n    context.Log(\"Processing: \" + context.Req.Method + \" \" + context.Req.Path)\r\n\r\n    if context.Req.Method == \"GET\" {\r\n        return context.Res.Json(map[string]interface{}{\"message\": \"Hello!\"})\r\n    }\r\n\r\n    return context.Res.Json(map[string]interface{}{\"success\": true})\r\n    // context.Res.Text(\"Hello\")                  // plain text\r\n    // context.Res.Empty()                         // 204\r\n    // context.Res.Redirect(\"https://...\")          // 302\r\n}\r\n```\r\n\r\n### Server-Side Rendering (SSR) Authentication\r\n\r\nSSR apps using Go frameworks (net/http, Gin, Echo, Chi, etc.) use the **server SDK** to handle auth. You need two clients:\r\n\r\n- **Admin client** — uses an API key, creates sessions, bypasses rate limits (reusable singleton)\r\n- **Session client** — uses a session cookie, acts on behalf of a user (create per-request, never share)\r\n\r\n```go\r\nimport (\r\n    \"github.com/appwrite/sdk-for-go/client\"\r\n    \"github.com/appwrite/sdk-for-go/account\"\r\n)\r\n\r\n// Admin client (reusable)\r\nadminClient := client.New(\r\n    client.WithEndpoint(\"https://<REGION>.cloud.appwrite.io/v1\"),\r\n    client.WithProject(os.Getenv(\"APPWRITE_PROJECT_ID\")),\r\n    client.WithKey(os.Getenv(\"APPWRITE_API_KEY\")),\r\n)\r\n\r\n// Session client (create per-request)\r\nsessionClient := client.New(\r\n    client.WithEndpoint(\"https://<REGION>.cloud.appwrite.io/v1\"),\r\n    client.WithProject(os.Getenv(\"APPWRITE_PROJECT_ID\")),\r\n)\r\n\r\ncookie, err := r.Cookie(\"a_session_[PROJECT_ID]\")\r\nif err == nil {\r\n    sessionClient.SetSession(cookie.Value)\r\n}\r\n```\r\n\r\n#### Email/Password Login\r\n\r\n```go\r\nhttp.HandleFunc(\"/login\", func(w http.ResponseWriter, r *http.Request) {\r\n    svc := account.New(adminClient)\r\n    session, err := svc.CreateEmailPasswordSession(r.FormValue(\"email\"), r.FormValue(\"password\"))\r\n    if err != nil {\r\n        http.Error(w, err.Error(), http.StatusBadRequest)\r\n        return\r\n    }\r\n\r\n    // Cookie name must be a_session_<PROJECT_ID>\r\n    http.SetCookie(w, &http.Cookie{\r\n        Name:     \"a_session_[PROJECT_ID]\",\r\n        Value:    session.Secret,\r\n        HttpOnly: true,\r\n        Secure:   true,\r\n        SameSite: http.SameSiteStrictMode,\r\n        Path:     \"/\",\r\n    })\r\n\r\n    w.Header().Set(\"Content-Type\", \"application/json\")\r\n    w.Write([]byte(`{\"success\": true}`))\r\n})\r\n```\r\n\r\n#### Authenticated Requests\r\n\r\n```go\r\nhttp.HandleFunc(\"/user\", func(w http.ResponseWriter, r *http.Request) {\r\n    cookie, err := r.Cookie(\"a_session_[PROJECT_ID]\")\r\n    if err != nil {\r\n        http.Error(w, \"Unauthorized\", http.StatusUnauthorized)\r\n        return\r\n    }\r\n\r\n    sessionClient := client.New(\r\n        client.WithEndpoint(\"https://<REGION>.cloud.appwrite.io/v1\"),\r\n        client.WithProject(os.Getenv(\"APPWRITE_PROJECT_ID\")),\r\n        client.WithSession(cookie.Value),\r\n    )\r\n\r\n    svc := account.New(sessionClient)\r\n    user, err := svc.Get()\r\n    // Marshal user to JSON and write response\r\n})\r\n```\r\n\r\n#### OAuth2 SSR Flow\r\n\r\n```go\r\n// Step 1: Redirect to OAuth provider\r\nhttp.HandleFunc(\"/oauth\", func(w http.ResponseWriter, r *http.Request) {\r\n    svc := account.New(adminClient)\r\n    redirectURL, err := svc.CreateOAuth2Token(\r\n        \"github\",\r\n        account.WithCreateOAuth2TokenSuccess(\"https://example.com/oauth/success\"),\r\n        account.WithCreateOAuth2TokenFailure(\"https://example.com/oauth/failure\"),\r\n    )\r\n    if err != nil {\r\n        http.Error(w, err.Error(), http.StatusInternalServerError)\r\n        return\r\n    }\r\n    http.Redirect(w, r, redirectURL, http.StatusFound)\r\n})\r\n\r\n// Step 2: Handle callback — exchange token for session\r\nhttp.HandleFunc(\"/oauth/success\", func(w http.ResponseWriter, r *http.Request) {\r\n    svc := account.New(adminClient)\r\n    session, err := svc.CreateSession(r.URL.Query().Get(\"userId\"), r.URL.Query().Get(\"secret\"))\r\n    if err != nil {\r\n        http.Error(w, err.Error(), http.StatusBadRequest)\r\n        return\r\n    }\r\n\r\n    http.SetCookie(w, &http.Cookie{\r\n        Name: \"a_session_[PROJECT_ID]\", Value: session.Secret,\r\n        HttpOnly: true, Secure: true, SameSite: http.SameSiteStrictMode, Path: \"/\",\r\n    })\r\n    w.Write([]byte(`{\"success\": true}`))\r\n})\r\n```\r\n\r\n> **Cookie security:** Always use `HttpOnly`, `Secure`, and `SameSiteStrictMode` to prevent XSS. The cookie name must be `a_session_<PROJECT_ID>`.\r\n\r\n> **Forwarding user agent:** Call `sessionClient.SetForwardedUserAgent(r.Header.Get(\"User-Agent\"))` to record the end-user's browser info for debugging and security.\r\n\r\n## Error Handling\r\n\r\n```go\r\nimport \"github.com/appwrite/sdk-for-go/apperr\"\r\n\r\ndoc, err := service.GetRow(\"[DATABASE_ID]\", \"[TABLE_ID]\", \"[ROW_ID]\")\r\nif err != nil {\r\n    var appErr *apperr.AppwriteException\r\n    if errors.As(err, &appErr) {\r\n        fmt.Println(appErr.Message)    // human-readable message\r\n        fmt.Println(appErr.Code)       // HTTP status code (int)\r\n        fmt.Println(appErr.Type)       // error type (e.g. \"document_not_found\")\r\n    }\r\n}\r\n```\r\n\r\n**Common error codes:**\r\n\r\n| Code | Meaning |\r\n|------|---------|\r\n| `401` | Unauthorized — missing or invalid session/API key |\r\n| `403` | Forbidden — insufficient permissions |\r\n| `404` | Not found — resource does not exist |\r\n| `409` | Conflict — duplicate ID or unique constraint |\r\n| `429` | Rate limited — too many requests |\r\n\r\n## Permissions & Roles (Critical)\r\n\r\nAppwrite uses permission strings to control access to resources. Each permission pairs an action (`read`, `update`, `delete`, `create`, or `write` which grants create + update + delete) with a role target. By default, **no user has access** unless permissions are explicitly set at the row/file level or inherited from the table/bucket settings. Permissions are arrays of strings built with the `permission` and `role` helpers.\r\n\r\n```go\r\nimport (\r\n    \"github.com/appwrite/sdk-for-go/permission\"\r\n    \"github.com/appwrite/sdk-for-go/role\"\r\n)\r\n```\r\n\r\n### Database Row with Permissions\r\n\r\n```go\r\ndoc, err := service.CreateRow(\r\n    \"[DATABASE_ID]\",\r\n    \"[TABLE_ID]\",\r\n    \"[ROW_ID]\",\r\n    map[string]interface{}{\"title\": \"Hello World\"},\r\n    tablesdb.WithCreateRowPermissions([]string{\r\n        permission.Read(role.User(\"[USER_ID]\")),     // specific user can read\r\n        permission.Update(role.User(\"[USER_ID]\")),   // specific user can update\r\n        permission.Read(role.Team(\"[TEAM_ID]\")),     // all team members can read\r\n        permission.Read(role.Any()),                 // anyone (including guests) can read\r\n    }),\r\n)\r\n```\r\n\r\n### File Upload with Permissions\r\n\r\n```go\r\nf, err := service.CreateFile(\r\n    \"[BUCKET_ID]\",\r\n    \"[FILE_ID]\",\r\n    file.NewInputFile(\"/path/to/file.png\", \"file.png\"),\r\n    storage.WithCreateFilePermissions([]string{\r\n        permission.Read(role.Any()),\r\n        permission.Update(role.User(\"[USER_ID]\")),\r\n        permission.Delete(role.User(\"[USER_ID]\")),\r\n    }),\r\n)\r\n```\r\n\r\n> **When to set permissions:** Set row/file-level permissions when you need per-resource access control. If all rows in a table share the same rules, configure permissions at the table/bucket level and leave row permissions empty.\r\n\r\n> **Common mistakes:**\r\n> - **Forgetting permissions** — the resource becomes inaccessible to all users (including the creator)\r\n> - **`role.Any()` with `write`/`update`/`delete`** — allows any user, including unauthenticated guests, to modify or remove the resource\r\n> - **`permission.Read(role.Any())` on sensitive data** — makes the resource publicly readable\r\n\r\n"
}

SHA-256: bd39048bad968d48ec36c0cbf414dae65f1e47beeafe086cc22d983b6576f229