coagus/php-api-builder

Build RESTful APIs in PHP in minutes. Define entities, get automatic CRUD, JWT auth, validation, and OpenAPI docs.

Maintainers

Package info

github.com/coagus/php-api-builder

pkg:composer/coagus/php-api-builder

Statistics

Installs: 216

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

v2.0.0-alpha.18 2026-04-09 00:59 UTC

README

Latest Stable Version Total Downloads License Tests PHP 8.4+ Docker

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

  1. Create a new project:
composer create-project coagus/php-api-builder-skeleton my-api
  1. Initialize and start:
cd my-api && ./api init

Without PHP (Docker only)

  1. Create your project directory:
mkdir my-api && cd my-api
  1. Initialize the project:
docker run --rm -it -v $(pwd):/app coagus/php-api-builder init
  1. Start the services:
docker compose up -d
  1. Verify it works:
curl http://localhost:8080/api/v1/health

Running CLI commands without PHP: Once docker compose up -d is running, enter the container and use the CLI from there:

docker compose exec app bash
php vendor/bin/api make:entity Product

Alternatively, the ./api wrapper auto-detects Docker and works without entering the container.

Try the Demo

Explore all library features with a ready-made Blog API demo:

  1. Install the demo (after init + docker compose up):
./api demo:install
  1. Open Swagger UI at http://localhost:8080/api/v1/docs/swagger

  2. 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, SensitiveDataFilter in 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