mcpField
A
appwrite-go

appwrite-go

github.com/appwrite開発28902 回インストール1 週間前に更新
リポジトリ

appwrite-go について

appwrite-go は、Go アプリケーションで Appwrite Go SDK を利用するためのスキルです。データベース API、権限、認証、ストレージ、関数など、サーバー側の操作を扱います。

主な機能

  1. 01ユーザーとチームの管理
  2. 02TablesDB によるテーブルと行の作成
  3. 03クエリヘルパーによるデータの絞り込み
  4. 04ストレージ API によるファイルアップロード
  5. 05サーバーレス関数の実行
  6. 06権限と SSR 認証の設定

活用シーン

  1. 01Go サービスへの Appwrite データ操作の追加
  2. 02ユーザーとチームの管理機能の実装
  3. 03アクセス権限付きファイルの保存
  4. 04サーバー側セッションと OAuth の処理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/appwrite/skills --skill appwrite-go

インストール後、Claude Code で/appwrite-goを入力して読み込まれたか確認します。

詳細

Appwrite Go SDK

インストール

go get github.com/appwrite/sdk-for-go

クライアントのセットアップ

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")),
)

コード例

ユーザー管理
service := users.New(clt)

// Create user
user, err := service.Create(
    id.Unique(),
    "[email protected]",
    "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]")
データベース操作

Note: 新しいコードではすべて TablesDB(非推奨の Databases クラスではありません)を使用してください。Databases は、既存のコードベースがすでに依存している場合、またはユーザーが明示的に要求した場合にのみ使用してください。

Tip: 利用可能な場合は、裸の位置引数よりも明示的な関数オプションパラメータ(例: tablesdb.WithUpdateRowData(...))を優先してください。位置引数のみのスタイルは、既存のコードベースがすでに使用している場合、またはユーザーが明示的に要求した場合にのみ使用してください。

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]")
文字列カラムの型

Note: レガシーの string 型は非推奨です。新しいカラムではすべて明示的なカラム型を使用してください。

型最大文字数インデックスストレージ
varchar16,383フルインデックス(サイズが 768 以下の場合)行内にインライン
text16,383プレフィックスのみオフページ
mediumtext4,194,303プレフィックスのみオフページ
longtext1,073,741,823プレフィックスのみオフページ
  • varchar はインラインで保存され、64 KB の行サイズ制限にカウントされます。名前、スラッグ、識別子など、短くインデックスされるフィールドに適しています。
  • text、mediumtext、longtext はオフページで保存されるため(行内には 20 バイトのポインタのみが存在します)、行サイズの予算を消費しません。これらの型では size は不要です。
// 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},
    }),
)
クエリメソッド
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)
query.Relationship("comments", []string{query.Equal("approved", true), query.Limit(25)})  // related documents

// Aggregations (optional alias)
query.Count("*", "total")
query.Sum("amount", "revenue")
query.Avg("score", "avgScore")
query.Min("price", "minPrice")
query.Max("price", "maxPrice")

// Grouping
query.GroupBy([]interface{}{"status", "year"})
query.Having([]string{query.GreaterThan("total", 1)})
query.Distinct()

// Joins (table id, left key, right key, operator, alias)
query.Join("orders", "$id", "customerId", "=", "")
query.LeftJoin("orders", "$id", "customerId", "=", "ord")
query.LeftJoinOn("orders", "ord", []string{
    query.On("$id", "customerId", ""),
    query.Equal("ord.status", "paid"),
})

// Fluent builder. Pass Build() into list methods.
queries := query.NewBuilder().
    Equal("status", "published").
    LeftJoin("orders", "$id", "customerId", "=", "ord").
    Count("*", "total").
    GroupBy([]interface{}{"status"}).
    Limit(25).
    Build()

_, _ = service.ListRows("[DATABASE_ID]", "[TABLE_ID]", service.WithListRowsQueries(queries))
ファイルストレージ
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 ファクトリメソッド
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
チーム
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("[email protected]"),
)

// 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.Team("[TEAM_ID]") を、特定のチームロールには role.Team("[TEAM_ID]", "editor") を使用してください。

サーバーレス関数
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]")
関数ハンドラーの記述(Go ランタイム)
// 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
}
サーバーサイドレンダリング(SSR)認証

Go フレームワーク(net/http、Gin、Echo、Chi など)を使用する SSR アプリは、認証を処理するために サーバー SDK を使用します。2 つのクライアントが必要です:

  • 管理クライアント — API キーを使用し、セッションを作成し、レート制限をバイパスします(再利用可能なシングルトン)
  • セッションクライアント — セッション Cookie を使用し、ユーザーの代理として動作します(リクエストごとに作成し、決して共有しないでください)
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)
}
メール/パスワードログイン
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}`))
})
認証済みリクエスト
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 フロー
// 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 セキュリティ: XSS を防ぐために、常に HttpOnly、Secure、SameSiteStrictMode を使用してください。Cookie 名は a_session_<PROJECT_ID> でなければなりません。

ユーザーエージェントの転送: デバッグとセキュリティのためにエンドユーザーのブラウザ情報を記録するには、sessionClient.SetForwardedUserAgent(r.Header.Get("User-Agent")) を呼び出してください。

エラーハンドリング

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")
    }
}

一般的なエラーコード:

コード意味
401認証されていません — セッション/API キーが欠落しているか無効です
403禁止されています — 権限が不十分です
404見つかりません — リソースが存在しません
409競合 — ID の重複または一意制約違反
429レート制限 — リクエストが多すぎます

権限とロール(重要)

Appwrite は権限文字列を使用してリソースへのアクセスを制御します。各権限は、アクション(read、update、delete、create、または create + update + delete を付与する write)とロールターゲットを組み合わせたものです。デフォルトでは、行/ファイルレベルで権限が明示的に設定されているか、テーブル/バケット設定から継承されていない限り、どのユーザーもアクセスできません。権限は permission および role ヘルパーで構築された文字列の配列です。

import (
    "github.com/appwrite/sdk-for-go/permission"
    "github.com/appwrite/sdk-for-go/role"
)
権限を持つデータベース行
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
    }),
)
パーミッション付きのファイルアップロード
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]")),
    }),
)

パーミッションを設定すべきタイミング: リソースごとのアクセス制御が必要な場合は、行/ファイルレベルのパーミッションを設定します。テーブル内のすべての行が同じルールを共有する場合は、テーブル/バケットレベルでパーミッションを設定し、行のパーミッションは空のままにします。

よくある間違い:

  • パーミッションの設定忘れ — リソースがすべてのユーザー(作成者を含む)からアクセスできなくなります
  • write/update/delete を伴う role.Any() — 未認証のゲストを含む任意のユーザーがリソースを変更または削除できるようになります
  • 機密データに対する permission.Read(role.Any()) — リソースが誰でも読み取り可能になります

コメント