К основному содержанию
скилл CX Codex

appwrite-php — скилл для Claude Code: PHP SDK Appwrite

Скилл подсказывает агенту, как писать серверный PHP под Appwrite: пользователи, таблицы, файлы, функции. Пригодится в Laravel и Symfony.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add appwrite/skills --skill appwrite-php -a claude-code
Открыть на GitHub

Что делает appwrite-php

Скилл даёт агенту готовые примеры работы с PHP SDK Appwrite. Внутри — настройка клиента через setEndpoint, setProject и setKey, управление пользователями, работа с таблицами через TablesDB, загрузка файлов в Storage и вызов Functions. Плюс шпаргалка по методам Query: фильтры, сортировка, пагинация курсором, агрегации, join и fluent-билдер.

Для работы нужен ключ API: клиент берёт его из переменной окружения APPWRITE_API_KEY, а ID проекта — из APPWRITE_PROJECT_ID. Без ключа запросы к серверу не пройдут, потому что SDK авторизуется именно им. Сам пакет ставится через composer require appwrite/appwrite.

Отдельно скилл предупреждает: класс Databases устарел, для нового кода берите TablesDB. Тип колонки string тоже устарел — вместо него varchar, text, mediumtext или longtext. У varchar лимит 16,383 символа и он хранится внутри строки, а text и длиннее лежат вне строки. Если в вашем проекте уже используется Databases, скилл разрешает оставить его.

Какие задачи решает

  • создать пользователя и выдать ему роль в команде
  • собрать запрос с фильтром, сортировкой и курсорной пагинацией
  • загрузить файл в бакет и удалить его по ID
  • выбрать тип колонки вместо устаревшего string
  • вызвать функцию Appwrite из PHP-кода

Примеры запросов агенту

  • Подключи Appwrite PHP SDK и создай пользователя с email и паролем
  • Сделай выборку из таблицы articles с фильтром по title и лимитом 10
  • Загрузи файл в бакет через Storage и верни его ID

Как установить appwrite-php

1Через Skills CLI (Vercel)

npx skills add appwrite/skills --skill appwrite-php -a claude-code

2Вручную: положить файлы

~/.claude/skills/appwrite-php/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/appwrite-php/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о appwrite-php

Как установить appwrite-php в Claude?

Выполните npx skills add appwrite/skills --skill appwrite-php -a claude-code. Скилл появится в ~/.claude/skills/appwrite-php/SKILL.md.

Что делает appwrite-php?

Даёт агенту примеры работы с Appwrite PHP SDK: пользователи, таблицы, файлы, команды и функции. Помогает писать серверный код под Laravel и Symfony.

Какой класс использовать для базы данных?

TablesDB. Класс Databases помечен как устаревший — оставляйте его только если существующий код уже на нём.

Исходный текст

SKILL.md · BSD-3-Clause
---
name: appwrite-php
description: 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.
---


# 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(), '[email protected]', 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.

| 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.

```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: '[email protected]');

// 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:**

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

## 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.

```php
use Appwrite\Permission;
use Appwrite\Role;
```

### Database Row with Permissions

```php
$doc = $tablesDB->createRow('[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 Upload with Permissions

```php
$file = $storage->createFile('[BUCKET_ID]', ID::unique(), InputFile::withPath('/path/to/file.png'), [
    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

© appwrite · BSD-3-Clause · appwrite/skills

Похожие скиллы

Всё по теме «Бэкенд и API» →
php-pro https://github.com/Jeffallan Задаёт правила разработки на PHP 8.3+ для Laravel и Symfony: включает strict_types, анализ в PHPStan на уровне 9 и тесты PHPUnit или Pest. CX Codex +1 15 500установок sentry-php-sdk getsentry Помогает настроить мониторинг Sentry в проектах на чистом PHP, Laravel или Symfony. Распознаёт фреймворк, очереди и планировщики, после чего предлагает нужные пакеты и конфигурацию. CX Codex +1 2 200установок php-mcp-development официальный Awesome Copilot Community Плагин помогает создавать MCP-серверы на PHP через официальный SDK. Он пригодится для генерации проекта, инструментов, ресурсов, промптов и тестов. ★ 40 418звёзд репо php-development Mindrally Скилл задаёт агенту правила написания PHP 8+ кода: SOLID, PSR-12, строгая типизация, тесты на PHPUnit. Пригодится, когда проект на PHP растёт и хочется единый стиль. CX Codex +1 1 100установок php-symfony официальный github Правило задаёт Copilot стиль разработки на Symfony: структура проекта, DI, Doctrine, Twig и тесты. Пригодится, если не хочется объяснять агенту конвенции в каждом чате. ★ 40 418звёзд репо aws-lambda-php-integration giuseppe-trisciuoglio Скилл подсказывает агенту, как развернуть PHP или Symfony на AWS Lambda через Bref: обработчики, слои рантайма, триггеры SQS/SNS и холодный старт. CX Codex +1 3 500установок

Из того же набора8

appwrite-typescript Подсказывает агенту, как писать код на TypeScript SDK Appwrite: вход, база, файлы, realtime и серверные операции. CX Codex +1 2 100установок appwrite-cli Скилл объясняет, как правильно работать с Appwrite CLI: когда держать проект в appwrite.config.json, а когда хватит разового вызова. CX Codex +1 1 900установок appwrite-dart Подсказывает агенту, как работать с Appwrite из Dart: авторизация, база, файлы, realtime и серверное администрирование. CX Codex +1 1 100установок appwrite-python Подсказывает агенту, как писать серверный код на Python с Appwrite SDK: пользователи, таблицы, хранилище файлов, функции. CX Codex +1 1 100установок appwrite-kotlin Подсказывает агенту, как писать код на Kotlin SDK Appwrite: авторизация, база, файлы, realtime и серверные админ-вызовы. CX Codex +1 862установок appwrite-ruby Подсказывает агенту, как работать с Appwrite из Ruby: пользователи, таблицы, файлы, команды. Пригодится в Rails и Sinatra. CX Codex +1 796установок appwrite-dotnet Скилл для работы с Appwrite .NET SDK: подсказывает агенту, как писать серверный код на C# — пользователи, таблицы, файлы, функции. CX Codex +1 544установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой