Install
$ agentstack add skill-appwrite-agent-skills-appwrite-go ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Appwrite Go SDK
Installation
go get github.com/appwrite/sdk-for-go
Setting Up the Client
import (
"os"
"github.com/appwrite/sdk-for-go/client"
"github.com/appwrite/sdk-for-go/id"
"github.com/appwrite/sdk-for-go/users"
"github.com/appwrite/sdk-for-go/tablesdb"
"github.com/appwrite/sdk-for-go/storage"
)
clt := client.New(
client.WithEndpoint("https://.cloud.appwrite.io/v1"),
client.WithProject(os.Getenv("APPWRITE_PROJECT_ID")),
client.WithKey(os.Getenv("APPWRITE_API_KEY")),
)
Code Examples
User Management
service := users.New(clt)
// Create user
user, err := service.Create(
id.Unique(),
"user@example.com",
"password123",
users.WithCreateName("User Name"),
)
// List users
list, err := service.List()
// Get user
fetched, err := service.Get("[USER_ID]")
// Delete user
_, err = service.Delete("[USER_ID]")
Database Operations
> 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. > > 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.
service := tablesdb.New(clt)
// Create database
db, err := service.Create(id.Unique(), "My Database")
// Create row
doc, err := service.CreateRow(
"[DATABASE_ID]",
"[TABLE_ID]",
id.Unique(),
map[string]interface{}{"title": "Hello World"},
)
// List rows
results, err := service.ListRows("[DATABASE_ID]", "[TABLE_ID]")
// Get row
row, err := service.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]")
// Update row
_, err = service.UpdateRow(
"[DATABASE_ID]",
"[TABLE_ID]",
"[ROW_ID]",
tablesdb.WithUpdateRowData(map[string]interface{}{"title": "Updated"}),
)
// Delete row
_, err = service.DeleteRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]")
String Column Types
> Note: The legacy string type is deprecated. Use explicit column types for all new columns.
| 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 towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.text,mediumtext, andlongtextare stored off-page (only a 20-byte pointer lives in the row), so they don't consume the row size budget.sizeis not required for these types.
// Create table with explicit string column types
_, err = service.CreateTable(
"[DATABASE_ID]",
id.Unique(),
"articles",
tablesdb.WithCreateTableColumns([]map[string]interface{}{
{"key": "title", "type": "varchar", "size": 255, "required": true},
{"key": "summary", "type": "text", "required": false},
{"key": "body", "type": "mediumtext", "required": false},
{"key": "raw_data", "type": "longtext", "required": false},
}),
)
Query Methods
import "github.com/appwrite/sdk-for-go/query"
// Filtering
query.Equal("field", "value") // == (or pass slice for IN)
query.NotEqual("field", "value") // !=
query.LessThan("field", 100) //
query.GreaterThanEqual("field", 100) // >=
query.Between("field", 1, 100) // 1 **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.
### Serverless Functions
```go
import "github.com/appwrite/sdk-for-go/functions"
svc := functions.New(clt)
// Execute function
execution, err := svc.CreateExecution(
"[FUNCTION_ID]",
functions.WithCreateExecutionBody(`{"key": "value"}`),
)
// List executions
executions, err := svc.ListExecutions("[FUNCTION_ID]")
Writing a Function Handler (Go runtime)
// src/main.go — Appwrite Function entry point
package handler
import (
"github.com/open-runtimes/types-for-go/v4/openruntimes"
)
func Main(context openruntimes.Context) openruntimes.Response {
// context.Req.Body — raw body (string)
// context.Req.BodyJson — parsed JSON (map[string]interface{})
// context.Req.Headers — headers (map[string]string)
// context.Req.Method — HTTP method
// context.Req.Path — URL path
// context.Req.Query — query params (map[string]string)
context.Log("Processing: " + context.Req.Method + " " + context.Req.Path)
if context.Req.Method == "GET" {
return context.Res.Json(map[string]interface{}{"message": "Hello!"})
}
return context.Res.Json(map[string]interface{}{"success": true})
// context.Res.Text("Hello") // plain text
// context.Res.Empty() // 204
// context.Res.Redirect("https://...") // 302
}
Server-Side Rendering (SSR) Authentication
SSR apps using Go frameworks (net/http, Gin, Echo, Chi, etc.) use the server SDK to handle auth. You need two clients:
- Admin client — uses an API key, creates sessions, bypasses rate limits (reusable singleton)
- Session client — uses a session cookie, acts on behalf of a user (create per-request, never share)
import (
"github.com/appwrite/sdk-for-go/client"
"github.com/appwrite/sdk-for-go/account"
)
// Admin client (reusable)
adminClient := client.New(
client.WithEndpoint("https://.cloud.appwrite.io/v1"),
client.WithProject(os.Getenv("APPWRITE_PROJECT_ID")),
client.WithKey(os.Getenv("APPWRITE_API_KEY")),
)
// Session client (create per-request)
sessionClient := client.New(
client.WithEndpoint("https://.cloud.appwrite.io/v1"),
client.WithProject(os.Getenv("APPWRITE_PROJECT_ID")),
)
cookie, err := r.Cookie("a_session_[PROJECT_ID]")
if err == nil {
sessionClient.SetSession(cookie.Value)
}
Email/Password Login
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
svc := account.New(adminClient)
session, err := svc.CreateEmailPasswordSession(r.FormValue("email"), r.FormValue("password"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Cookie name must be a_session_
http.SetCookie(w, &http.Cookie{
Name: "a_session_[PROJECT_ID]",
Value: session.Secret,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
Path: "/",
})
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"success": true}`))
})
Authenticated Requests
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("a_session_[PROJECT_ID]")
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
sessionClient := client.New(
client.WithEndpoint("https://.cloud.appwrite.io/v1"),
client.WithProject(os.Getenv("APPWRITE_PROJECT_ID")),
client.WithSession(cookie.Value),
)
svc := account.New(sessionClient)
user, err := svc.Get()
// Marshal user to JSON and write response
})
OAuth2 SSR Flow
// Step 1: Redirect to OAuth provider
http.HandleFunc("/oauth", func(w http.ResponseWriter, r *http.Request) {
svc := account.New(adminClient)
redirectURL, err := svc.CreateOAuth2Token(
"github",
account.WithCreateOAuth2TokenSuccess("https://example.com/oauth/success"),
account.WithCreateOAuth2TokenFailure("https://example.com/oauth/failure"),
)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, redirectURL, http.StatusFound)
})
// Step 2: Handle callback — exchange token for session
http.HandleFunc("/oauth/success", func(w http.ResponseWriter, r *http.Request) {
svc := account.New(adminClient)
session, err := svc.CreateSession(r.URL.Query().Get("userId"), r.URL.Query().Get("secret"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
http.SetCookie(w, &http.Cookie{
Name: "a_session_[PROJECT_ID]", Value: session.Secret,
HttpOnly: true, Secure: true, SameSite: http.SameSiteStrictMode, Path: "/",
})
w.Write([]byte(`{"success": true}`))
})
> Cookie security: Always use HttpOnly, Secure, and SameSiteStrictMode to prevent XSS. The cookie name must be a_session_.
> Forwarding user agent: Call sessionClient.SetForwardedUserAgent(r.Header.Get("User-Agent")) to record the end-user's browser info for debugging and security.
Error Handling
import "github.com/appwrite/sdk-for-go/apperr"
doc, err := service.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]")
if err != nil {
var appErr *apperr.AppwriteException
if errors.As(err, &appErr) {
fmt.Println(appErr.Message) // human-readable message
fmt.Println(appErr.Code) // HTTP status code (int)
fmt.Println(appErr.Type) // error type (e.g. "document_not_found")
}
}
Common error codes:
| Code | Meaning | |------|---------| | 401 | Unauthorized — missing or invalid session/API key | | 403 | Forbidden — insufficient permissions | | 404 | Not found — resource does not exist | | 409 | Conflict — duplicate ID or unique constraint | | 429 | Rate limited — too many requests |
Permissions & Roles (Critical)
Appwrite 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.
import (
"github.com/appwrite/sdk-for-go/permission"
"github.com/appwrite/sdk-for-go/role"
)
Database Row with Permissions
doc, err := service.CreateRow(
"[DATABASE_ID]",
"[TABLE_ID]",
"[ROW_ID]",
map[string]interface{}{"title": "Hello World"},
tablesdb.WithCreateRowPermissions([]string{
permission.Read(role.User("[USER_ID]")), // specific user can read
permission.Update(role.User("[USER_ID]")), // specific user can update
permission.Read(role.Team("[TEAM_ID]")), // all team members can read
permission.Read(role.Any()), // anyone (including guests) can read
}),
)
File Upload with Permissions
f, err := service.CreateFile(
"[BUCKET_ID]",
"[FILE_ID]",
file.NewInputFile("/path/to/file.png", "file.png"),
storage.WithCreateFilePermissions([]string{
permission.Read(role.Any()),
permission.Update(role.User("[USER_ID]")),
permission.Delete(role.User("[USER_ID]")),
}),
)
> 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.
> Common mistakes: > - Forgetting permissions — the resource becomes inaccessible to all users (including the creator) > - role.Any() with write/update/delete — allows any user, including unauthenticated guests, to modify or remove the resource > - permission.Read(role.Any()) on sensitive data — makes the resource publicly readable
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: appwrite
- Source: appwrite/agent-skills
- License: BSD-3-Clause
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.