appwrite/agent-skills/appwrite-php

Claude Code and agent skill by appwrite/agent-skills

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Appwrite PHP SDK skill. Use when building server-side PHP applications with Appwrite, including Laravel and Symfony integrations. Covers user management, database/table CRUD, file storage, and functions via API keys.

Install

npx vskill@latest install appwrite/agent-skills/appwrite-php

SKILL.md

Appwrite PHP SDK

Installation

bash
composer require appwrite/appwrite

Setting Up the Client

php
use Appwrite\Client;
use Appwrite\ID;
use Appwrite\Query;
use Appwrite\Services\Users;
use Appwrite\Services\TablesDB;
use Appwrite\Services\Storage;
use Appwrite\Services\Functions;
use Appwrite\InputFile;
$client = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject(getenv('APPWRITE_PROJECT_ID'))
->setKey(getenv('APPWRITE_API_KEY'));

Code Examples

User Management
php
$users = new Users($client);
// Create user
$user = $users->create(ID::unique(), 'user@example.com', null, 'password123', 'User Name');
// List users
$list = $users->list([Query::limit(25)]);
// Get user
$fetched = $users->get('[USER_ID]');
// Delete user
$users->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 named arguments (PHP 8+, e.g., databaseId: '...') for all SDK method calls. Only use positional arguments if the existing codebase already uses them or the user explicitly requests it.
php
$tablesDB = new TablesDB($client);
// Create database
$db = $tablesDB->create(ID::unique(), 'My Database');
// Create row
$doc = $tablesDB->createRow('[DATABASE_ID]', '[TABLE_ID]', ID::unique(), [
'title' => 'Hello World'
]);
// Query rows
$results = $tablesDB->listRows('[DATABASE_ID]', '[TABLE_ID]', [
Query::equal('title', ['Hello World']),
Query::limit(10)
]);
// Get row
$row = $tablesDB->getRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]');
// Update row
$tablesDB->updateRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]', [
'title' => 'Updated'
]);
// Delete row
$tablesDB->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.
php
// Create table with explicit string column types
$tablesDB->createTable('[DATABASE_ID]', ID::unique(), 'articles', [
['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
php
// Filtering
Query::equal('field', ['value']) // == (always pass array)
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 (string or array)
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(['field1', 'field2']) // return only specified fields
Query::or([Query::equal('a', [1]), Query::equal('b', [2])]) // OR
Query::and([Query::greaterThan('age', 18), Query::lessThan('age', 65)]) // AND (default)
Query::relationship('comments', [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(['status', 'year'])
Query::having([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')
// Nested join ON: extra predicates stay on the JOIN, not WHERE
Query::leftJoin('orders', 'ord', [
Query::on('$id', 'customerId'),
Query::equal('ord.status', 'paid'),
])
// Fluent builder. Pass build() into list methods.
$queries = Query::builder()
->equal('status', 'published')
->leftJoin('orders', '$id', 'customerId', '=', 'ord')
->count('*', 'total')
->groupBy(['status'])
->limit(25)
->build();
$tablesDB->listRows('[DATABASE_ID]', '[TABLE_ID]', $queries);
File Storage
php
$storage = new Storage($client);
// Upload file
$file = $storage->createFile('[BUCKET_ID]', ID::unique(), InputFile::withPath('/path/to/file.png'));
// List files
$files = $storage->listFiles('[BUCKET_ID]');
// Delete file
$storage->deleteFile('[BUCKET_ID]', '[FILE_ID]');
InputFile Factory Methods
php
use Appwrite\InputFile;
InputFile::withPath('/path/to/file.png') // from filesystem path
InputFile::withData('Hello world', 'hello.txt') // from string content
Teams
php
$teams = new Teams($client);
// Create team
$team = $teams->create(ID::unique(), 'Engineering');
// List teams
$list = $teams->list();
// Create membership (invite user by email)
$membership = $teams->createMembership('[TEAM_ID]', ['editor'], email: 'user@example.com');
// List memberships
$members = $teams->listMemberships('[TEAM_ID]');
// Update membership roles
$teams->updateMembership('[TEAM_ID]', '[MEMBERSHIP_ID]', ['admin']);
// Delete team
$teams->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
php
$functions = new Functions($client);
// Execute function
$execution = $functions->createExecution('[FUNCTION_ID]', '{"key": "value"}');
// List executions
$executions = $functions->listExecutions('[FUNCTION_ID]');
Writing a Function Handler (PHP runtime)
php
// src/main.php — Appwrite Function entry point
return function ($context) {
// $context->req->body — raw body (string)
// $context->req->bodyJson — parsed JSON (array or null)
// $context->req->headers — headers (array)
// $context->req->method — HTTP method
// $context->req->path — URL path
// $context->req->query — query params (array)
$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->bodyJson ?? [];
if (!isset($data['name'])) {
$context->error('Missing name field');
return $context->res->json(['error' => 'Name is required'], 400);
}
return $context->res->json(['success' => true]); // JSON
// return $context->res->text('Hello'); // plain text
// return $context->res->empty(); // 204
// return $context->res->redirect('https://...'); // 302
};
Server-Side Rendering (SSR) Authentication

SSR apps (Laravel, Symfony, 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)
php
use Appwrite\Client;
use Appwrite\Services\Account;
// Admin client (reusable)
$adminClient = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject('[PROJECT_ID]')
->setKey(getenv('APPWRITE_API_KEY'));
// Session client (create per-request)
$sessionClient = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
->setProject('[PROJECT_ID]');
$session = $_COOKIE['a_session_[PROJECT_ID]'] ?? null;
if ($session) {
$sessionClient->setSession($session);
}
Email/Password Login
php
$account = new Account($adminClient);
$session = $account->createEmailPasswordSession($email, $password);
// Cookie name must be a_session_<PROJECT_ID>
setcookie('a_session_[PROJECT_ID]', $session['secret'], [
'httpOnly' => true,
'secure' => true,
'sameSite' => 'strict',
'expires' => strtotime($session['expire']),
'path' => '/',
]);
Authenticated Requests
php
$session = $_COOKIE['a_session_[PROJECT_ID]'] ?? null;
if (!$session) {
http_response_code(401);
exit;
}
$sessionClient->setSession($session);
$account = new Account($sessionClient);
$user = $account->get();
OAuth2 SSR Flow
php
// Step 1: Redirect to OAuth provider
$account = new Account($adminClient);
$redirectUrl = $account->createOAuth2Token(
OAuthProvider::GITHUB(),
'https://example.com/oauth/success',
'https://example.com/oauth/failure',
);
header('Location: ' . $redirectUrl);
// Step 2: Handle callback — exchange token for session
$account = new Account($adminClient);
$session = $account->createSession($_GET['userId'], $_GET['secret']);
setcookie('a_session_[PROJECT_ID]', $session['secret'], [
'httpOnly' => true, 'secure' => true, 'sameSite' => 'strict',
'expires' => strtotime($session['expire']), 'path' => '/',
]);
Cookie security: Always use httpOnly, secure, and sameSite: 'strict' to prevent XSS. The cookie name must be a_session_<PROJECT_ID>.
Forwarding user agent: Call $sessionClient->setForwardedUserAgent($_SERVER['HTTP_USER_AGENT']) to record the end-user's browser info for debugging and security.

Error Handling

php
use Appwrite\AppwriteException;
try {
$row = $tablesDB->getRow('[DATABASE_ID]', '[TABLE_ID]', '[ROW_ID]');
} catch (AppwriteException $e) {
echo $e->getMessage(); // human-readable error message
echo $e->getCode(); // HTTP status code (int)
echo $e->getType(); // Appwrite error type string (e.g. 'document_not_found')
echo $e->getResponse(); // full response body (array)
}

Common error codes:

CodeMeaning
401Unauthorized — missing or invalid session/API key
403Forbidden — insufficient permissions for this action
404Not found — resource does not exist
409Conflict — duplicate ID or unique constraint violation
429Rate limited — too many requests, retry after backoff

Permissions & Roles (Critical)

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
11
7-day trend score
12

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 23 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/appwrite/agent-skills/appwrite-php/badge)](https://verified-skill.com/skills/appwrite/agent-skills/appwrite-php)

Something wrong with this skill or its page? Tell us.