Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
appwrite avatar

Go

  • 1 installs
  • 10 repo stars
  • Updated July 29, 2026
  • appwrite/claude-plugin

go is a Claude skill for building server-side Go applications with the Appwrite backend using the sdk-for-go package and functional options pattern.

About

go is a skill for building server-side Go applications with Appwrite. It covers user management, database and table CRUD, file storage and functions authenticated via API keys. A developer uses it when writing a Go backend that talks to Appwrite Cloud or a self-hosted instance. It uses the github.com/appwrite/sdk-for-go package with per-service packages and the functional options pattern, and the modern TablesDB API.

  • Appwrite Go SDK for server-side Go applications
  • Covers user management, TablesDB CRUD, file storage and functions via API keys
  • Uses per-service packages and the functional options pattern

Go by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #3,836 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Jul 30, 2026 (Skillselion catalog sync)
At a glance

go capabilities & compatibility

requires an Appwrite project ID and API key; Appwrite has a free cloud tier

Capabilities
appwrite cli · appwrite dart · api development · database
Use cases
api development · database
Pricing
Bring your own API key
From the docs

What go says it does

Appwrite Go SDK skill. Use when building server-side Go applications with Appwrite.
SKILL.md
Uses per-service packages and functional options pattern.
SKILL.md
npx skills add https://github.com/appwrite/claude-plugin --skill go

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars10
Last updatedJuly 29, 2026
Repositoryappwrite/claude-plugin

What it does

Build a server-side Go backend on Appwrite for users, TablesDB CRUD, storage and functions using API keys.

Who is it for?

writing a server-side Go backend that uses Appwrite for users, data, storage and functions

Skip if: client-side or non-Go Appwrite work

When should I use this skill?

building a Go server that calls Appwrite for users, TablesDB, storage or function execution

What you get

Working Appwrite user, TablesDB, storage and function calls from Go with correct functional options.

  • Appwrite Go client setup
  • TablesDB CRUD code
  • storage and function calls

By the numbers

  • four documented string column types
  • default query limit of 25, max 100

Files

SKILL.mdMarkdownGitHub ↗

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)

// 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.
TypeMax charactersIndexingStorage
varchar16,383Full index (if size ≤ 768)Inline in row
text16,383Prefix onlyOff-page
mediumtext4,194,303Prefix onlyOff-page
longtext1,073,741,823Prefix onlyOff-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.
// 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.LessThanEqual("field", 100)        // <=
query.GreaterThan("field", 100)          // >
query.GreaterThanEqual("field", 100)     // >=
query.Between("field", 1, 100)           // 1 <= field <= 100
query.IsNull("field")                    // is null
query.IsNotNull("field")                 // is not null
query.StartsWith("field", "prefix")      // starts with
query.EndsWith("field", "suffix")        // ends with
query.Contains("field", "sub")           // contains
query.Search("field", "keywords")        // full-text search (requires index)

// Sorting
query.OrderAsc("field")
query.OrderDesc("field")

// Pagination
query.Limit(25)                          // max rows (default 25, max 100)
query.Offset(0)                          // skip N rows
query.CursorAfter("[ROW_ID]")            // cursor pagination (preferred)
query.CursorBefore("[ROW_ID]")

// Selection & Logic
query.Select([]string{"field1", "field2"})
query.Or([]string{query.Equal("a", 1), query.Equal("b", 2)})    // OR
query.And([]string{query.GreaterThan("age", 18), query.LessThan("age", 65)})  // AND (default)

File Storage

import "github.com/appwrite/sdk-for-go/file"

service := storage.New(clt)

// Upload file
f, err := service.CreateFile(
    "[BUCKET_ID]",
    "[FILE_ID]",
    file.NewInputFile("/path/to/file.png", "file.png"),
)

// List files
files, err := service.ListFiles("[BUCKET_ID]")

// Delete file
_, 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")          // from filesystem path
file.NewInputFileFromReader(reader, "file.png", size)        // from io.Reader (size required)
file.NewInputFileFromBytes(data, "file.png")                 // from []byte

Teams

import "github.com/appwrite/sdk-for-go/teams"

svc := teams.New(clt)

// Create team
team, err := svc.Create(id.Unique(), "Engineering")

// List teams
list, err := svc.List()

// Create membership (invite user by email)
membership, err := svc.CreateMembership(
    "[TEAM_ID]",
    []string{"editor"},
    teams.WithCreateMembershipEmail("user@example.com"),
)

// List memberships
members, err := svc.ListMemberships("[TEAM_ID]")

// Update membership roles
_, err = svc.UpdateMembership("[TEAM_ID]", "[MEMBERSHIP_ID]", []string{"admin"})

// Delete team
_, 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)

// 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://<REGION>.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://<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
    }

    // Cookie name must be a_session_<PROJECT_ID>
    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()
    // 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_<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)    // 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:

CodeMeaning
401Unauthorized — missing or invalid session/API key
403Forbidden — insufficient permissions
404Not found — resource does not exist
409Conflict — duplicate ID or unique constraint
429Rate 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 document/file level or inherited from the collection/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 document/file-level permissions when you need per-resource access control. If all documents in a collection share the same rules, configure permissions at the collection/bucket level and leave document 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

Related skills

FAQ

How do I install it?

Via go get github.com/appwrite/sdk-for-go, then build a client with WithEndpoint, WithProject and WithKey.

Databases or TablesDB?

Use TablesDB for all new code; only use the deprecated Databases class if the existing codebase already relies on it.

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.