coagus / php-api-builder
Build RESTful APIs in PHP in minutes. Define entities, get automatic CRUD, JWT auth, validation, and OpenAPI docs.
Requires
- php: ^8.4
- firebase/php-jwt: ^7.0
- monolog/monolog: ^3.5
- vlucas/phpdotenv: ^5.6
Requires (Dev)
- pestphp/pest: ^3.0
- dev-main
- v2.0.0-alpha.18
- v2.0.0-alpha.17
- v2.0.0-alpha.16
- v2.0.0-alpha.15
- v2.0.0-alpha.14
- v2.0.0-alpha.13
- v2.0.0-alpha.12
- v2.0.0-alpha.11
- v2.0.0-alpha.10
- v2.0.0-alpha.9
- v2.0.0-alpha.8
- v2.0.0-alpha.7
- v2.0.0-alpha.6
- 2.0.0-alpha.1
- 1.3.3
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.8
- 1.2.7
- 1.2.5
- 1.2.4
- 1.2.2
- 1.2.1
- v1.2.0
- v1.1.1
- v1.1.0
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.7.3
- v0.7.2
- v0.7.1
- v0.7.0
- v0.6.0
- v0.5.0
- v0.4.0
- v0.3.0
- v0.2.0
- v0.1.1
- v0.1.0
- dev-feature/v2.0.0
This package is auto-updated.
Last update: 2026-04-09 01:03:06 UTC
README
PHP API Builder v2
Build RESTful APIs in PHP in minutes. Define your entities, get CRUD automatically, and focus on your business logic.
#[Table('products')] #[SoftDelete] class Product extends Entity { #[PrimaryKey] public private(set) int $id; #[Required, MaxLength(100)] public string $name { set => trim($value); } #[Required] public float $price { set { if ($value < 0) throw new \InvalidArgumentException('Price must be positive'); $this->price = round($value, 2); } } #[Required, Email, Unique] public string $email { set => strtolower(trim($value)); } #[Hidden] public string $password { set => password_hash($value, PASSWORD_ARGON2ID); } #[BelongsTo(Category::class)] public int $categoryId; #[HasMany(Review::class)] public array $reviews; }
That's it. You now have a fully functional API with GET, POST, PUT, PATCH, DELETE endpoints, pagination, filtering, sorting, validation, soft deletes, and relationships. No controllers, no routes to configure, no boilerplate.
Features
- Automatic CRUD from entity definitions with zero configuration
- Powerful ORM with Active Record pattern, relationships, and 5-level Query Builder
- PHP 8.4 property hooks, asymmetric visibility, typed properties, attributes as metadata
- Multi-database support via PDO (MySQL, PostgreSQL, SQLite)
- JWT Authentication with OAuth 2.1 security practices (short-lived tokens, refresh rotation, scopes)
- Auto-generated OpenAPI/Swagger documentation from your entity attributes
- Validation via attributes (
#[Required],#[Email],#[MaxLength],#[Unique]) -- no config files - Rate limiting middleware with file-based storage -- no external dependencies
- REST conventions -- lowerCamelCase JSON keys, snake_case query params, RFC 7807 errors
- Security built-in with OWASP headers, CORS, input sanitization, SQL injection protection
- Docker-first workflow -- start a project without PHP installed locally
- CLI scaffolding for entities, services, middleware, and tests
- Error traceability with request ID correlation across all layers
- AI development skill included -- install it and your AI assistant knows the library
Quick Start
With PHP installed
- Create a new project:
composer create-project coagus/php-api-builder-skeleton my-api
- Initialize and start:
cd my-api && ./api init
Without PHP (Docker only)
- Create your project directory:
mkdir my-api && cd my-api
- Initialize the project:
docker run --rm -it -v $(pwd):/app coagus/php-api-builder init
- Start the services:
docker compose up -d
- Verify it works:
curl http://localhost:8080/api/v1/health
Running CLI commands without PHP: Once
docker compose up -dis running, enter the container and use the CLI from there:docker compose exec app bash php vendor/bin/api make:entity ProductAlternatively, the
./apiwrapper auto-detects Docker and works without entering the container.
Try the Demo
Explore all library features with a ready-made Blog API demo:
- Install the demo (after init + docker compose up):
./api demo:install
-
Open Swagger UI at
http://localhost:8080/api/v1/docs/swagger -
When done exploring, clean up:
./api demo:remove
The demo creates a complete Blog API with Users, Posts, Comments, and Tags -- showcasing entities, services, relationships, JWT auth, validation, rate limiting, middleware, and OpenAPI documentation.
Create Your First Entity
./api make:entity User --fields="name:string,email:string,password:string" --soft-delete
This generates entities/User.php:
<?php namespace App\Entities; use Coagus\PhpApiBuilder\ORM\Entity; use Coagus\PhpApiBuilder\Attributes\{Table, PrimaryKey, SoftDelete}; use Coagus\PhpApiBuilder\Validation\Attributes\{Required}; #[Table('users')] #[SoftDelete] class User extends Entity { #[PrimaryKey] public private(set) int $id; #[Required] public string $name { set => trim($value); } #[Required] public string $email; #[Required] public string $password; }
Your endpoints are ready:
GET /api/v1/users # List with pagination, filtering, sorting
GET /api/v1/users/{id} # Get one
POST /api/v1/users # Create (validates automatically)
PUT /api/v1/users/{id} # Full update
PATCH /api/v1/users/{id} # Partial update
DELETE /api/v1/users/{id} # Soft delete
Services (No Database)
Not everything needs a database. Services handle external APIs, health checks, custom logic:
#[PublicResource] #[Route('health')] class Health extends Service { public function get(): void { $this->success([ 'status' => 'healthy', 'timestamp' => date('c'), ]); } }
Hybrid Resources (CRUD + Custom Endpoints)
Combine automatic CRUD with custom business logic:
class UserService extends APIDB { protected string $entity = User::class; // CRUD works automatically: GET, POST, PUT, PATCH, DELETE // Custom: POST /api/v1/users/login public function postLogin(): void { $input = $this->getInput(); $user = User::query()->where('email', $input->email)->first(); if (!$user || !password_verify($input->password, $user->password)) { $this->error('Invalid credentials', 401); return; } $this->success(['token' => Auth::generateAccessToken($user->toArray())]); } }
Query Builder
Five levels of complexity -- use what you need:
// Level 1: Shortcuts $user = User::find(1); $users = User::all(); // Level 2: Fluent $users = User::query() ->where('active', true) ->orderBy('created_at', 'desc') ->limit(10) ->get(); // Level 3: Eager loading (no N+1 queries) $users = User::query() ->with('orders', 'orders.items') ->where('active', true) ->get(); // Level 4: Reusable scopes $users = User::query()->active()->recent(7)->get(); // Level 5: Raw SQL (always parameterized) $results = Connection::getInstance()->query( 'SELECT u.*, COUNT(o.id) as total FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.id HAVING total > ?', [5] );
Validation via Attributes
#[Table('users')] class User extends Entity { #[Required, MaxLength(50)] public string $name { set => trim($value); } #[Required, Email, Unique] public string $email { set => strtolower(trim($value)); } #[Hidden] public string $password { set => password_hash($value, PASSWORD_ARGON2ID); } }
#[Required] validates presence, #[Email] validates format, #[Unique] checks the database, #[Hidden] excludes the field from responses, and property hooks sanitize on assignment.
Auto-Generated API Documentation
Your entity attributes generate OpenAPI 3.1 specs automatically:
GET /api/v1/docs # OpenAPI JSON spec
GET /api/v1/docs/swagger # Swagger UI
GET /api/v1/docs/redoc # ReDoc UI
No extra annotations needed. #[Required] becomes required, #[MaxLength(50)] becomes maxLength: 50, #[Hidden] fields are excluded from response schemas.
CLI Commands
./api init # Initialize new project (interactive) ./api serve # Development server ./api env:check # Verify environment and dependencies ./api make:entity Product # Generate entity + test ./api make:service Payment # Generate service + test ./api make:middleware Auth # Generate middleware ./api keys:generate # Generate JWT key pair ./api docs:generate # Export OpenAPI spec ./api test # Run tests (Pest) ./api test --coverage # Tests with coverage report ./api skill:install # Install AI development skill
The ./api wrapper detects whether to use local PHP or Docker automatically. Teams with mixed setups work seamlessly.
Database Support
Configure in .env:
DB_DRIVER=mysql # mysql | pgsql | sqlite
DB_HOST=localhost
DB_PORT=3306
DB_NAME=my_database
DB_USER=root
DB_PASSWORD=secret
The ORM generates driver-specific SQL through PDO. Switch databases by changing one line.
Security
Built-in by default, following OWASP recommendations:
- SQL Injection: Parameterized queries everywhere (PDO prepared statements)
- Security Headers:
X-Content-Type-Options,X-Frame-Options, HSTS,Referrer-Policy - JWT Auth: Short-lived access tokens (15min), refresh rotation with theft detection
- Input Sanitization: Automatic null byte removal, configurable per-field
- CORS: Configurable via
.env, validates against dangerous misconfigurations - Sensitive Data:
#[Hidden]attributes,SensitiveDataFilterin logs
Project Structure
my-api/
├── api # CLI wrapper (auto-detects PHP vs Docker)
├── .env # Configuration
├── index.php # Entry point
├── entities/ # Database entities (auto CRUD)
│ ├── User.php
│ └── Product.php
├── services/ # Pure services (no DB)
│ └── Health.php
├── middleware/ # Custom middleware
├── tests/ # Pest tests
├── docker-compose.yml # Docker environment
└── log/ # Error logs (auto-generated)
Requirements
- PHP 8.4+
- Composer 2.x
- Or just Docker
Installation
composer require coagus/php-api-builder
Documentation
Full architecture and design documentation is available in resources/docs/01-analisis-y-diseno.md.
AI Development Skill
The library includes an AI skill that teaches Claude Code and Cowork how to work with php-api-builder. Install it and your AI assistant can generate entities, services, queries, and configurations following the library's patterns:
./api skill:install
License
MIT License. See LICENSE for details.
Author
Christian Agustin - christian@agustin.gt