From dfb23612b0173aed5367077ef255f2ae88ab5363 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 31 Mar 2026 14:44:24 +1300 Subject: [PATCH] (docs): rewrite AGENTS.md with module structure and action patterns --- AGENTS.md | 185 +++++++++++++++++++++++++++++------------------------- 1 file changed, 99 insertions(+), 86 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bb24d9f4fe..4d11ff0ee3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,107 +1,120 @@ -# AGENTS.md +# Appwrite -Appwrite is an end-to-end backend server for web, mobile, native, and backend apps. This guide provides context and instructions for AI coding agents working on the Appwrite codebase. +Self-hosted Backend-as-a-Service platform. Hybrid monolithic-microservice architecture built with PHP 8.3+ on Swoole, delivered as Docker containers. -## Project Overview +## Commands -Appwrite is a self-hosted Backend-as-a-Service (BaaS) platform that provides developers with a set of APIs and tools to build secure, scalable applications. The project uses a hybrid monolithic-microservice architecture built with PHP, running on Swoole for high performance. +| Command | Purpose | +|---------|---------| +| `docker compose up -d --force-recreate --build` | Build and start all services | +| `docker compose exec appwrite test tests/e2e/Services/[Service]` | Run E2E tests for a service | +| `docker compose exec appwrite test tests/e2e/Services/[Service] --filter=[Method]` | Run a single test method | +| `docker compose exec appwrite test tests/unit/` | Run unit tests | +| `composer format` | Auto-format code (Pint, PSR-12) | +| `composer format ` | Format a specific file | +| `composer lint ` | Check formatting of a file | +| `composer analyze` | Static analysis (PHPStan level 3) | +| `composer check` | Same as `analyze` | -**Key Technologies:** -- **Backend:** PHP 8.3+, Swoole -- **Libraries:** Utopia PHP -- **Database:** MariaDB, Redis -- **Cache:** Redis -- **Queue:** Redis -- **Containers:** Docker +## Stack -## Development Commands +- PHP 8.3+, Swoole 6.x (async runtime, replaces PHP-FPM) +- Utopia PHP framework (HTTP routing, CLI, DI, queue) +- MongoDB (default), MariaDB, MySQL, PostgreSQL (adapters via utopia-php/database) +- Redis (cache, queue, pub/sub) +- Docker + Traefik (reverse proxy) +- PHPUnit 12, Pint (PSR-12), PHPStan level 3 -```bash -# Run Appwrite -docker compose up -d --force-recreate --build +## Project layout -# Run specific test -docker compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName] --filter=[FunctionName] +- **src/Appwrite/Platform/Modules/** -- feature modules (Account, Avatars, Compute, Console, Databases, Functions, Health, Project, Projects, Proxy, Sites, Storage, Teams, Tokens, VCS, Webhooks) +- **src/Appwrite/Platform/Workers/** -- background job workers +- **src/Appwrite/Platform/Tasks/** -- CLI tasks +- **app/init.php** -- bootstrap (registers services, resources, listeners) +- **app/init/** -- configs, constants, locales, models, registers, resources, span, database filters/formats +- **bin/** -- CLI entry points: `worker-*` (14 workers), `schedule-*`, `queue-*`, plus `doctor`, `install`, `migrate`, `realtime`, `upgrade`, `ssl`, `vars`, `maintenance`, `interval`, `specs`, `sdks`, etc. +- **tests/e2e/** -- end-to-end tests per service +- **tests/unit/** -- unit tests +- **public/** -- static assets and generated SDKs -# Format code -composer format +## Module structure + +Each module under `src/Appwrite/Platform/Modules/{Name}/` contains: + +``` +Module.php -- registers all services for the module +Services/Http.php -- registers HTTP endpoints +Services/Workers.php -- registers background workers +Services/Tasks.php -- registers CLI tasks +Http/{Service}/ -- endpoint actions (Create.php, Get.php, Update.php, Delete.php, XList.php) +Workers/ -- worker implementations +Tasks/ -- CLI task implementations ``` -## Code Style Guidelines +HTTP endpoint nesting reflects the URL path. Sub-resources get subdirectories. For example, within the Functions module: +`Http/Deployments/Template/Create.php` -> `POST /v1/functions/:functionId/deployments/template` -- Follow [PSR-12](https://www.php-fig.org/psr/psr-12/) coding standard -- Use PSR-4 autoloading -- Strict type declarations where applicable -- Comprehensive PHPDoc comments +File names in Http directories must only be `Get.php`, `Create.php`, `Update.php`, `Delete.php`, or `XList.php`. For non-CRUD operations, model the endpoint as a property update. For example, updating a team membership status lives at `Teams/Http/Memberships/Status/Update.php` (`PATCH /v1/teams/:teamId/memberships/:membershipId/status`). -### Naming Conventions +Register new modules in `src/Appwrite/Platform/Appwrite.php`. Detailed module guide: `src/Appwrite/Platform/AGENTS.md`. -#### `resourceType` Naming Rule +## Action pattern (HTTP endpoints) -When a collection has a combination of `resourceType`, `resourceId`, and/or `resourceInternalId`, the value of `resourceType` MUST always be **plural** - for example: `functions`, `sites`, `deployments`. - -Examples: ```php -'resourceType' => 'functions' -'resourceType' => 'sites' -'resourceType' => 'deployments' +class Create extends Action +{ + public static function getName(): string { return 'createTeam'; } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/teams') + ->desc('Create team') + ->groups(['api', 'teams']) + ->label('event', 'teams.[teamId].create') + ->label('scope', 'teams.write') + ->param('teamId', '', new CustomId(), 'Team ID.') + ->param('name', null, new Text(128), 'Team name.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + string $teamId, + string $name, + Response $response, + Database $dbForProject, + Event $queueForEvents, + ): void { + // implementation + } +} ``` -## Performance Patterns +Common injections: `$response`, `$request`, `$dbForProject`, `$dbForPlatform`, `$user`, `$project`, `$queueForEvents`, `$queueForMails`, `$queueForDeletes`. -### Document Update Optimization +## Conventions -When updating documents, always pass only the changed attributes as a sparse `Document` rather than the full document. This is more efficient because `updateDocument()` internally performs `array_merge($old, $new)`. +- PSR-12 formatting enforced by Pint. PSR-4 autoloading. +- `resourceType` values are always **plural**: `'functions'`, `'sites'`, `'deployments'`. +- When updating documents, pass only changed attributes as a sparse Document: + ```php + // correct + $dbForProject->updateDocument('users', $user->getId(), new Document([ + 'name' => $name, + ])); + // incorrect -- passing full document is inefficient + $user->setAttribute('name', $name); + $dbForProject->updateDocument('users', $user->getId(), $user); + ``` + Exceptions: migrations, `array_merge()` with `getArrayCopy()`, updates where nearly all attributes change, complex nested relationship logic requiring full document state. +- Avoid introducing dependencies outside the `utopia-php` ecosystem. +- Never hardcode credentials -- use environment variables. +- Code changes may require container restart. No central log location -- check relevant containers. -**Correct Pattern:** -```php -// Good: Pass only changed attributes directly -$user = $dbForProject->updateDocument('users', $user->getId(), new Document([ - 'name' => $name, - 'email' => $email, -])); -``` +## Cross-repo context -**Incorrect Pattern:** -```php -$user->setAttribute('name', $name); -$user->setAttribute('email', $email); - -// Bad: Passing full document is inefficient -$user = $dbForProject->updateDocument('users', $user->getId(), $user); -``` - -**Exceptions:** -- Migration files (need full document updates by design) -- Cases already using `array_merge()` with `getArrayCopy()` -- Updates where almost all attributes of the document change at once (sparse update provides little benefit compared to passing the full document) -- Complex nested relationship logic where full document state is required - -## Security Considerations - -### Critical Security Practices - -- **Never hardcode credentials** - Use environment variables -- **Rate limiting** - Respect abuse prevention mechanisms - -## Dependencies - -Avoid introducing new dependencies other than utopia-php. - -## Adding new endpoints - -When adding new endpoints, make sure to use modules and follow its patterns. Find instruction in [Modules AGENTS.md](src/Appwrite/Platform/AGENTS.md) file. - -## Pull Request Guidelines -### Before Submitting - -- Run `composer format` -- Update documentation if adding features -- Add/update tests for your changes -- Check that Docker build succeeds -`docs/specs/authentication.drawio.svg` - -## Known Issues and Gotchas - -- **Hot Reload:** Code changes require container restart in some cases -- **Logging:** There is no central place for logs, so when debugging, ensure to check all possibly relevant containers +Appwrite is the base server for `appwrite/cloud`. Changes to the Action pattern, module structure, DI system, or response models affect cloud. The `feat-dedicated-db` feature spans cloud, edge, and console.