| 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. |
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://<REGION>.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)
user, err := service.Create(
id.Unique(),
"user@example.com",
"password123",
users.WithCreateName("User Name"),
)
list, err := service.List()
fetched, err := service.Get("[USER_ID]")
_, 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)
db, err := service.Create(id.Unique(), "My Database")
doc, err := service.CreateRow(
"[DATABASE_ID]",
"[TABLE_ID]",
id.Unique(),
map[string]interface{}{"title": "Hello World"},
)
results, err := service.ListRows("[DATABASE_ID]", "[TABLE_ID]")
row, err := service.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]")
_, err = service.UpdateRow(
"[DATABASE_ID]",
"[TABLE_ID]",
"[ROW_ID]",
tablesdb.WithUpdateRowData(map[string]interface{}{"title": "Updated"}),
)
_, 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 |
varchar is stored inline and counts towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.
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.
_, 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"
query.Equal("field", "value")
query.NotEqual("field", "value")
query.LessThan("field", 100)
query.LessThanEqual("field", 100)
query.GreaterThan("field", 100)
query.GreaterThanEqual("field", 100)
query.Between("field", 1, 100)
query.IsNull("field")
query.IsNotNull("field")
query.StartsWith("field", "prefix")
query.EndsWith("field", "suffix")
query.Contains("field", "sub")
query.Search("field", "keywords")
query.OrderAsc("field")
query.OrderDesc("field")
query.Limit(25)
query.Offset(0)
query.CursorAfter("[ROW_ID]")
query.CursorBefore("[ROW_ID]")
query.Select([]string{"field1", "field2"})
query.Or([]string{query.Equal("a", 1), query.Equal("b", 2)})
query.And([]string{query.GreaterThan("age", 18), query.LessThan("age", 65)})
File Storage
import "github.com/appwrite/sdk-for-go/file"
service := storage.New(clt)
f, err := service.CreateFile(
"[BUCKET_ID]",
"[FILE_ID]",
file.NewInputFile("/path/to/file.png", "file.png"),
)
files, err := service.ListFiles("[BUCKET_ID]")
_, err = service.DeleteFile("[BUCKET_ID]", "[FILE_ID]")
InputFile Factory Methods
import "github.com/appwrite/sdk-for-go/file"
file.NewInputFile("/path/to/file.png", "file.png")
file.NewInputFileFromReader(reader, "file.png", size)
file.NewInputFileFromBytes(data, "file.png")
Teams
import "github.com/appwrite/sdk-for-go/teams"
svc := teams.New(clt)
team, err := svc.Create(id.Unique(), "Engineering")
list, err := svc.List()
membership, err := svc.CreateMembership(
"[TEAM_ID]",
[]string{"editor"},
teams.WithCreateMembershipEmail("user@example.com"),
)
members, err := svc.ListMemberships("[TEAM_ID]")
_, err = svc.UpdateMembership("[TEAM_ID]", "[MEMBERSHIP_ID]", []string{"admin"})
_, err = svc.Delete("[TEAM_ID]")
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
import "github.com/appwrite/sdk-for-go/functions"
svc := functions.New(clt)
execution, err := svc.CreateExecution(
"[FUNCTION_ID]",
functions.WithCreateExecutionBody(`{"key": "value"}`),
)
executions, err := svc.ListExecutions("[FUNCTION_ID]")
Writing a Function Handler (Go runtime)
package handler
import (
"github.com/open-runtimes/types-for-go/v4/openruntimes"
)
func Main(context openruntimes.Context) openruntimes.Response {
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})
}
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"
)
adminClient := client.New(
client.WithEndpoint("https://<REGION>.cloud.appwrite.io/v1"),
client.WithProject(os.Getenv("APPWRITE_PROJECT_ID")),
client.WithKey(os.Getenv("APPWRITE_API_KEY")),
)
sessionClient := client.New(
client.WithEndpoint("https://<REGION>.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
}
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://<REGION>.cloud.appwrite.io/v1"),
client.WithProject(os.Getenv("APPWRITE_PROJECT_ID")),
client.WithSession(cookie.Value),
)
svc := account.New(sessionClient)
user, err := svc.Get()
})
OAuth2 SSR Flow
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)
})
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_<PROJECT_ID>.
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)
fmt.Println(appErr.Code)
fmt.Println(appErr.Type)
}
}
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]")),
permission.Update(role.User("[USER_ID]")),
permission.Read(role.Team("[TEAM_ID]")),
permission.Read(role.Any()),
}),
)
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