appwrite-python
Appwrite の Python SDK に関するスキルです。Django・Flask・FastAPI などを使ったサーバーサイド Python アプリケーションの開発時に活用でき、APIキーを通じたユーザー管理・データベースの CRUD 操作・ファイルストレージ・関数の呼び出しに対応しています。
description の原文を見る
Appwrite Python SDK skill. Use when building server-side Python applications with Appwrite, including Django, Flask, and FastAPI integrations. Covers user management, database/table CRUD, file storage, and functions via API keys.
SKILL.md 本文
Appwrite Python SDK
インストール
pip install appwrite
クライアントのセットアップ
from appwrite.client import Client
from appwrite.id import ID
from appwrite.query import Query
from appwrite.services.users import Users
from appwrite.services.tables_db import TablesDB
from appwrite.services.storage import Storage
from appwrite.services.functions import Functions
from appwrite.enums.o_auth_provider import OAuthProvider
import os
client = (Client()
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
.set_project(os.environ['APPWRITE_PROJECT_ID'])
.set_key(os.environ['APPWRITE_API_KEY']))
コード例
ユーザー管理
users = Users(client)
# Create user
user = users.create(ID.unique(), 'user@example.com', None, 'password123', 'User Name')
# List users
result = users.list([Query.limit(25)])
# Get user
fetched = users.get('[USER_ID]')
# Delete user
users.delete('[USER_ID]')
データベース操作
注意: すべての新しいコードには
TablesDBを使用してください (非推奨のDatabasesクラスは使用しないでください)。既存のコードベースが既に依存している場合またはユーザーが明示的に要求した場合にのみDatabasesを使用してください。ヒント: すべての SDK メソッド呼び出しで、位置引数ではなくキーワード引数 (例:
database_id='...') を優先してください。既存のコードベースが既に位置引数スタイルを使用している場合またはユーザーが明示的に要求した場合にのみ位置引数スタイルを使用してください。
tables_db = TablesDB(client)
# Create database
db = tables_db.create(ID.unique(), 'My Database')
# Create row
doc = tables_db.create_row('[DATABASE_ID]', '[TABLE_ID]', ID.unique(), {
'title': 'Hello World'
})
# Query rows
results = tables_db.list_rows('[DATABASE_ID]', '[TABLE_ID]', [
Query.equal('title', 'Hello World'),
Query.limit(10)
])
# Get row
row = tables_db.get_row('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]')
# Update row
tables_db.update_row('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]', {
'title': 'Updated'
})
# Delete row
tables_db.delete_row('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]')
文字列カラム型
注意: レガシーの
string型は非推奨です。すべての新しいカラムで明示的なカラム型を使用してください。
| 型 | 最大文字数 | インデックス | ストレージ |
|---|---|---|---|
varchar | 16,383 | 完全インデックス (サイズ ≤ 768 の場合) | 行内 |
text | 16,383 | プレフィックスのみ | ページ外 |
mediumtext | 4,194,303 | プレフィックスのみ | ページ外 |
longtext | 1,073,741,823 | プレフィックスのみ | ページ外 |
varcharは行内に保存され、64 KB の行サイズ制限にカウントされます。名前、スラッグ、識別子などのショートでインデックス可能なフィールドに推奨します。text、mediumtext、longtextはページ外に保存されます (行には 20 バイトのポインタのみが保存される)。そのため行サイズ予算を消費しません。これらの型ではsizeは不要です。
# 明示的な文字列カラム型でテーブルを作成
tables_db.create_table(
database_id='[DATABASE_ID]',
table_id=ID.unique(),
name='articles',
columns=[
{'key': 'title', 'type': 'varchar', 'size': 255, 'required': True}, # inline, fully indexable
{'key': 'summary', 'type': 'text', 'required': False}, # off-page, prefix index only
{'key': 'body', 'type': 'mediumtext', 'required': False}, # up to ~4 M chars
{'key': 'raw_data', 'type': 'longtext', 'required': False}, # up to ~1 B chars
]
)
クエリメソッド
# Filtering
Query.equal('field', 'value') # == (or pass list for IN)
Query.not_equal('field', 'value') # !=
Query.less_than('field', 100) # <
Query.less_than_equal('field', 100) # <=
Query.greater_than('field', 100) # >
Query.greater_than_equal('field', 100) # >=
Query.between('field', 1, 100) # 1 <= field <= 100
Query.is_null('field') # is null
Query.is_not_null('field') # is not null
Query.starts_with('field', 'prefix') # starts with
Query.ends_with('field', 'suffix') # ends with
Query.contains('field', 'sub') # contains (string or array)
Query.search('field', 'keywords') # full-text search (requires index)
# Sorting
Query.order_asc('field')
Query.order_desc('field')
# Pagination
Query.limit(25) # max rows (default 25, max 100)
Query.offset(0) # skip N rows
Query.cursor_after('[ROW_ID]') # cursor pagination (preferred)
Query.cursor_before('[ROW_ID]')
# Selection & Logic
Query.select(['field1', 'field2']) # return only specified fields
Query.or_queries([Query.equal('a', 1), Query.equal('b', 2)]) # OR
Query.and_queries([Query.greater_than('age', 18), Query.less_than('age', 65)]) # AND (default)
ファイルストレージ
from appwrite.input_file import InputFile
storage = Storage(client)
# Upload file
file = storage.create_file('[BUCKET_ID]', ID.unique(), InputFile.from_path('/path/to/file.png'))
# List files
files = storage.list_files('[BUCKET_ID]')
# Delete file
storage.delete_file('[BUCKET_ID]', '[FILE_ID]')
InputFile ファクトリメソッド
from appwrite.input_file import InputFile
InputFile.from_path('/path/to/file.png') # from filesystem path
InputFile.from_bytes(byte_data, 'file.png') # from bytes
InputFile.from_string('Hello world', 'hello.txt') # from string content
チーム
from appwrite.services.teams import Teams
teams = Teams(client)
# Create team
team = teams.create(ID.unique(), 'Engineering')
# List teams
team_list = teams.list()
# Create membership (invite user by email)
membership = teams.create_membership('[TEAM_ID]', roles=['editor'], email='user@example.com')
# List memberships
members = teams.list_memberships('[TEAM_ID]')
# Update membership roles
teams.update_membership('[TEAM_ID]', '[MEMBERSHIP_ID]', roles=['admin'])
# Delete team
teams.delete('[TEAM_ID]')
ロールベースアクセス: すべてのチームメンバーに対して
Role.team('[TEAM_ID]')を使用するか、特定のチームロールに対してRole.team('[TEAM_ID]', 'editor')を使用してください (パーミッションを設定する際)。
サーバーレス関数
functions = Functions(client)
# Execute function
execution = functions.create_execution('[FUNCTION_ID]', body='{"key": "value"}')
# List executions
executions = functions.list_executions('[FUNCTION_ID]')
関数ハンドラーの記述 (Python ランタイム)
# src/main.py — Appwrite Function エントリーポイント
def main(context):
# context.req — request object
# .body — raw request body (string)
# .body_json — parsed JSON body (dict, or None if not JSON)
# .headers — request headers (dict)
# .method — HTTP method (GET, POST, etc.)
# .path — URL path
# .query — parsed query parameters (dict)
# .query_string — raw query string
context.log('Processing: ' + context.req.method + ' ' + context.req.path)
if context.req.method == 'GET':
return context.res.json({'message': 'Hello from Appwrite Function!'})
data = context.req.body_json or {}
if 'name' not in data:
context.error('Missing name field')
return context.res.json({'error': 'Name is required'}, 400)
# Response methods
return context.res.json({'success': True}) # JSON response
# return context.res.text('Hello') # plain text
# return context.res.empty() # 204 No Content
# return context.res.redirect('https://example.com') # 302 Redirect
# return context.res.send('data', 200, {'X-Custom': '1'}) # custom response
サーバーサイドレンダリング (SSR) 認証
SSR アプリ (Flask、Django、FastAPI など) は server SDK を使用して認証を処理します。2 つのクライアントが必要です:
- Admin クライアント — API キーを使用し、セッションを作成し、レート制限をバイパスします (再利用可能なシングルトン)
- Session クライアント — セッション cookie を使用し、ユーザーの代わりに動作します (リクエストごとに作成、決して共有しない)
from appwrite.client import Client
from appwrite.services.account import Account
from flask import request, jsonify, make_response, redirect
# Admin client (reusable)
admin_client = (Client()
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
.set_project('[PROJECT_ID]')
.set_key(os.environ['APPWRITE_API_KEY']))
# Session client (create per-request)
session_client = (Client()
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
.set_project('[PROJECT_ID]'))
session = request.cookies.get('a_session_[PROJECT_ID]')
if session:
session_client.set_session(session)
メール/パスワードログイン
@app.post('/login')
def login():
account = Account(admin_client)
session = account.create_email_password_session(
request.json['email'], request.json['password']
)
# Cookie name must be a_session_<PROJECT_ID>
resp = make_response(jsonify({'success': True}))
resp.set_cookie('a_session_[PROJECT_ID]', session['secret'],
httponly=True, secure=True, samesite='Strict',
expires=session['expire'], path='/')
return resp
認証されたリクエスト
@app.get('/user')
def get_user():
session = request.cookies.get('a_session_[PROJECT_ID]')
if not session:
return jsonify({'error': 'Unauthorized'}), 401
session_client = (Client()
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
.set_project('[PROJECT_ID]')
.set_session(session))
account = Account(session_client)
return jsonify(account.get())
OAuth2 SSR フロー
# Step 1: Redirect to OAuth provider
@app.get('/oauth')
def oauth():
account = Account(admin_client)
redirect_url = account.create_o_auth2_token(
OAuthProvider.Github,
'https://example.com/oauth/success',
'https://example.com/oauth/failure',
)
return redirect(redirect_url)
# Step 2: Handle callback — exchange token for session
@app.get('/oauth/success')
def oauth_success():
account = Account(admin_client)
session = account.create_session(request.args['userId'], request.args['secret'])
resp = make_response(jsonify({'success': True}))
resp.set_cookie('a_session_[PROJECT_ID]', session['secret'],
httponly=True, secure=True, samesite='Strict',
expires=session['expire'], path='/')
return resp
Cookie セキュリティ: XSS を防ぐために、常に
httponly、secure、samesite='Strict'を使用してください。Cookie 名はa_session_<PROJECT_ID>である必要があります。
ユーザーエージェントの転送:
session_client.set_forwarded_user_agent(request.headers.get('user-agent'))を呼び出して、デバッグとセキュリティのためにエンドユーザーのブラウザ情報を記録してください。
エラーハンドリング
from appwrite.exception import AppwriteException
try:
row = tables_db.get_row('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]')
except AppwriteException as e:
print(e.message) # human-readable error message
print(e.code) # HTTP status code (int)
print(e.type) # Appwrite error type string (e.g. 'document_not_found')
print(e.response) # full response body (dict)
一般的なエラーコード:
| コード | 意味 |
|---|---|
401 | 未認可 — セッション/API キーがないか無効 |
403 | 禁止 — このアクションのパーミッションが不足 |
404 | 見つかりません — リソースが存在しません |
409 | 競合 — 重複 ID または一意制約違反 |
429 | レート制限 — リクエストが多すぎます、バックオフ後に再試行してください |
パーミッション & ロール (重要)
Appwrite はパーミッション文字列を使用してリソースへのアクセスを制御します。各パーミッションはアクション (read、update、delete、create、または write (create + update + delete を付与)) とロールターゲットをペアリングします。デフォルトでは、**パーミッションが行/ファイルレベルで明示的に設定されるか、テーブル/バケット設定から継承されない限り、ユーザーはアクセス権を持ちません。パーミッションは Permission および Role ヘルパーで構築される文字列の配列です。
from appwrite.permission import Permission
from appwrite.role import Role
パーミッション付きデータベース行
doc = tables_db.create_row('[DATABASE_ID]', '[TABLE_ID]', ID.unique(), {
'title': 'Hello World'
}, [
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 = storage.create_file('[BUCKET_ID]', ID.unique(), InputFile.from_path('/path/to/file.png'), [
Permission.read(Role.any()),
Permission.update(Role.user('[USER_ID]')),
Permission.delete(Role.user('[USER_ID]')),
])
パーミッションを設定する時期: リソースごとのアクセス制御が必要な場合、行/ファイルレベルのパーミッションを設定してください。テーブル内のすべての行が同じルールを共有する場合、テーブル/バケットレベルでパーミッションを設定し、行パーミッションを空にしてください。
一般的な間違い:
- パーミッションの忘却 — リソースはすべてのユーザー (作成者を含む) にアクセス不可になります
Role.any()とwrite/update/delete— 非認証ゲストを含むすべてのユーザーがリソースを変更または削除できます- 機密データに対する
Permission.read(Role.any())— リソースが公開読み取り可能になります
ライセンス: BSD-3-Clause(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- appwrite
- ライセンス
- BSD-3-Clause
- 最終更新
- 不明
Source: https://github.com/appwrite/agent-skills / ライセンス: BSD-3-Clause
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。