OpenAPI Generation

The toolkit can scan your application's routes and generate an OpenAPI 3.1.0 specification document automatically. It reads your resources, form requests, and route definitions to build the spec.

Generating the Spec

php artisan api-toolkit:openapi

Options:

# Custom output path
php artisan api-toolkit:openapi --output=docs/openapi.json

# Pretty-printed JSON
php artisan api-toolkit:openapi --pretty

Configuration

In config/api-toolkit.php:

'openapi' => [
    'title'       => env('APP_NAME', 'API'),
    'version'     => '1.0.0',
    'description' => 'My API description.',
    'servers'     => [
        ['url' => env('APP_URL', 'http://localhost') . '/api'],
    ],
    'security_schemes' => [
        'bearerAuth' => [
            'type'         => 'http',
            'scheme'       => 'bearer',
            'bearerFormat' => 'JWT',
        ],
    ],
    'security' => [
        ['bearerAuth' => []],
    ],
],

What Gets Documented

The scanner discovers endpoints by analyzing controllers that use the QueryBuilder or return SuccessResponse/ErrorResponse instances. For each endpoint it generates:

  • Operation — HTTP method, path, summary (derived from route name), and tags (from controller name)
  • Parameters — Query parameters for filters, sorts, includes, and pagination based on the resource's allowedFilters(), allowedSorts(), and allowedIncludes()
  • Request Body — For POST/PUT/PATCH endpoints, inferred from the FormRequest type-hint
  • Responses — Success schema based on the resource's attributes and relationships, plus standard error responses
  • Path Parameters — Extracted from route definitions

Schema Inference

The SchemaBuilder infers attribute types from multiple sources (in priority order):

  1. The resource's schema() method (if defined)
  2. Eloquent model casts (int, float, bool, datetime, json, enum, etc.)
  3. Database column types
  4. Falls back to additionalProperties: true if nothing is available

Example Output

For an endpoint like GET /api/products:

{
  "/api/v1/products": {
    "get": {
      "summary": "List Products",
      "tags": ["Products"],
      "parameters": [
        {
          "name": "filter[name]",
          "in": "query",
          "schema": { "type": "string" },
          "description": "Filter by name"
        },
        {
          "name": "sort",
          "in": "query",
          "schema": { "type": "string", "example": "-created_at" },
          "description": "Sort by field. Prefix with - for descending."
        },
        {
          "name": "include",
          "in": "query",
          "schema": { "type": "string", "example": "category" },
          "description": "Include related resources. Comma-separated."
        }
      ],
      "responses": {
        "200": {
          "description": "Paginated set of `Product`",
          "content": {
            "application/vnd.api+json": {
              "schema": { "$ref": "#/components/schemas/ProductCollection" }
            }
          }
        },
        "401": { "$ref": "#/components/responses/UnauthorizedException" }
      }
    }
  }
}