PHPackages                             dskripchenko/laravel-api - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. dskripchenko/laravel-api

ActiveLibrary[API Development](/categories/api)

dskripchenko/laravel-api
========================

The Laravel Api Module.

4.2.0(1mo ago)83.0k↓100%13MITPHPCI passing

Since Apr 2Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/dskripchenko/laravel-api)[ Packagist](https://packagist.org/packages/dskripchenko/laravel-api)[ Docs](https://github.com/dskripchenko/laravel-api)[ RSS](/packages/dskripchenko-laravel-api/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (7)Versions (34)Used By (3)

dskripchenko/laravel-api
========================

[](#dskripchenkolaravel-api)

[![License: MIT](https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667)](LICENSE.md)[![Laravel](https://camo.githubusercontent.com/51376a197a15ef6639b4226a3f14d2b0b0ac6a108110b2e9565b06e23c03d2ff/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d362e782d2d31322e782d7265642e737667)](https://laravel.com)[![PHP](https://camo.githubusercontent.com/fb7c72456e13f7d5ecf8486e29d02a2e6775aaf4d18622a63529976b0ed0740e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e312532422d707572706c652e737667)](https://www.php.net)

🌐 [Русский](docs/README.ru.md) | [Deutsch](docs/README.de.md) | [中文](docs/README.zh.md)

**A Laravel package for versioned API routing, OpenAPI 3.0 auto-documentation, and CRUD scaffolding.**

Build versioned APIs with automatic OpenAPI documentation generated from PHP docblocks — no YAML/JSON schemas to maintain, no annotation libraries to learn.

Table of Contents
-----------------

[](#table-of-contents)

- [Quick Start](#quick-start)
- [Features](#features)
- [Installation](#installation)
- [Architecture](#architecture)
- [API Versioning](#api-versioning)
- [Routing &amp; Middleware](#routing--middleware)
- [OpenAPI 3.0 Documentation](#openapi-30-documentation)
- [CRUD Scaffolding](#crud-scaffolding)
- [Testing](#testing)
- [Configuration](#configuration)
- [Error Handling](#error-handling)
- [Comparison with Alternatives](#comparison-with-alternatives)
- [API Reference](#api-reference)
- [License](#license)

Quick Start
-----------

[](#quick-start)

```
composer require dskripchenko/laravel-api
```

```
// 1. Define your API class
class Api extends \Dskripchenko\LaravelApi\Components\BaseApi
{
    public static function getMethods(): array
    {
        return [
            'controllers' => [
                'user' => [
                    'controller' => UserController::class,
                    'actions' => ['list', 'show', 'create'],
                ],
            ],
        ];
    }
}

// 2. Define your module
class ApiModule extends \Dskripchenko\LaravelApi\Components\BaseModule
{
    public function getApiVersionList(): array
    {
        return ['v1' => Api::class];
    }
}

// 3. Define your ServiceProvider
class ApiServiceProvider extends \Dskripchenko\LaravelApi\Providers\ApiServiceProvider
{
    protected function getApiModule() { return new ApiModule(); }
}

// 4. Write a controller with docblocks
class UserController extends \Dskripchenko\LaravelApi\Controllers\ApiController
{
    /**
     * List users
     * @input integer ?$page Page number
     * @output integer $id User ID
     * @output string $name User name
     */
    public function list(Request $request): JsonResponse
    {
        return $this->success(User::paginate()->toArray());
    }
}
```

**Result:**

- `GET /api/v1/user/list` — API endpoint
- `GET /api/doc` — Auto-generated API documentation (Scalar)

Features
--------

[](#features)

FeatureDescription**Versioned routing**`api/{version}/{controller}/{action}` with inheritance between versions**OpenAPI 3.0**Auto-generated from `@input`/`@output` docblocks — no YAML files**CRUD scaffolding**Complete search/create/read/update/delete with filtering, sorting, pagination**Middleware cascade**Global → controller → action with fine-grained exclusion**Response templates**Reusable `$ref` schemas in `components/schemas`**Security schemes**`@security` tag + `securitySchemes` for Bearer/API key auth**Nested parameters**Dot-notation: `@input string $address.city` → nested JSON schema**File uploads**`@input file $avatar` → auto `multipart/form-data`**Multiple responses**`@response 200 {Success}` / `@response 422 {Error}`**Header parameters**`@header string $Authorization` — aggregated from controller + middleware**Soft deletes**Built-in `restore()` and `forceDelete()` in CrudService**Request tracing**`RequestIdMiddleware` — `X-Request-Id` propagation + log context**Optional output fields**`@output string ?$email` — marks response fields as optional in OpenAPI schema**TypeScript generation**`api:generate-types` — generates TS interfaces from OpenAPI spec**Named routes**Each action registered as a named Laravel route — `route('api.v1.user.list')`**API export**`api:export` — Postman Collection, HTTP Client (.http), Markdown, cURL**Test helpers**`assertApiSuccess()`, `assertApiError()`, `assertApiValidationError()`**Publishable config**`config/laravel-api.php` — prefix, URI pattern, HTTP methodsInstallation
------------

[](#installation)

### Requirements

[](#requirements)

- PHP 8.1+
- Laravel 6.x — 12.x

### Install

[](#install)

```
composer require dskripchenko/laravel-api
```

### Publish config

[](#publish-config)

```
php artisan vendor:publish --tag=laravel-api-config
```

Architecture
------------

[](#architecture)

### Request lifecycle

[](#request-lifecycle)

```
HTTP Request
  └─ ApiServiceProvider (registers route: api/{version}/{controller}/{action})
      └─ BaseApiRequest (parses version, controller, action from URI)
          └─ BaseModule::getApi() (version string → BaseApi subclass)
              └─ BaseApi::make()
                  ├─ getMethods() → resolve controller + action
                  ├─ Middleware cascade (global → controller → action)
                  └─ app()->call(Controller@action)
                      └─ JsonResponse {success: true, payload: {...}}

```

### Response format

[](#response-format)

Every response is wrapped in a standard envelope:

```
// Success
{"success": true, "payload": {"id": 1, "name": "John"}}

// Error
{"success": false, "payload": {"errorKey": "not_found", "message": "User not found"}}

// Validation error
{"success": false, "payload": {"errorKey": "validation", "messages": {"email": ["Required"]}}}
```

### Directory structure

[](#directory-structure)

```
src/
├── Components/        BaseApi, BaseModule, Meta
├── Console/Commands/  ApiInstall, ApiGenerateTypes, BaseCommand
├── Controllers/       ApiController, CrudController, ApiDocumentationController
├── Exceptions/        ApiException, ApiErrorHandler, Handler
├── Facades/        ApiRequest, ApiModule, ApiErrorHandler
├── Interfaces/     CrudServiceInterface, ApiInterface
├── Middlewares/    ApiMiddleware, RequestIdMiddleware
├── Providers/      ApiServiceProvider, BaseServiceProvider
├── Requests/       BaseApiRequest, CrudSearchRequest
├── Resources/      BaseJsonResource, BaseJsonResourceCollection
├── Services/       ApiResponseHelper, CrudService, OpenApiTypeScriptGenerator
└── Traits/
    ├── OpenApiTrait
    └── Testing/       MakesHttpApiRequests

```

API Versioning
--------------

[](#api-versioning)

API versions use **PHP class inheritance** — later versions extend earlier ones:

```
// V1: full API
class ApiV1 extends BaseApi {
    public static function getMethods(): array {
        return ['controllers' => [
            'user' => [
                'controller' => UserControllerV1::class,
                'actions' => ['list', 'show', 'create', 'update', 'delete'],
            ],
        ]];
    }
}

// V2: inherits V1, modifies selectively
class ApiV2 extends ApiV1 {
    public static function getMethods(): array {
        return ['controllers' => [
            'user' => [
                'controller' => UserControllerV2::class,  // upgraded controller
                'actions' => [
                    'delete' => false,                     // removed in v2
                    'archive',                             // new in v2
                ],
            ],
        ]];
    }
}
```

V2 automatically inherits `list`, `show`, `create`, `update` from V1, while overriding the controller and modifying actions.

Routing &amp; Middleware
------------------------

[](#routing--middleware)

### Action configuration

[](#action-configuration)

```
'actions' => [
    'list',                              // simple: method name = action key
    'show' => 'getById',                 // alias: show → calls getById()
    'disabled' => false,                 // disabled action (404)
    'create' => [
        'action' => 'store',             // explicit method name
        'method' => ['post'],            // allowed HTTP methods (default: ['post'])
        'name' => 'orders.store',        // route name: api.{version}.orders.store
        'middleware' => [RateLimit::class],
        'exclude-middleware' => [LogMiddleware::class],
        'exclude-all-middleware' => false,
    ],
]
```

### Middleware cascade

[](#middleware-cascade)

```
Global middleware (getMethods root)
  └─ Controller middleware
      └─ Action middleware

```

Each level can exclude middleware from parent levels using `exclude-middleware` (specific) or `exclude-all-middleware` (all).

OpenAPI 3.0 Documentation
-------------------------

[](#openapi-30-documentation)

Documentation is generated automatically from PHP docblocks. No YAML or JSON files to maintain.

### Basic tags

[](#basic-tags)

```
/**
 * Create an order
 * Detailed description of the endpoint.
 *
 * @input string $title Order title
 * @input string ?$notes Optional notes
 * @input integer(int64) $amount Amount in cents
 * @input string $status Status [draft,pending,confirmed]
 * @input file ?$attachment Optional file
 *
 * @output integer $id Created order ID
 * @output string(date-time) $createdAt Timestamp
 * @output string ?$notes Optional notes
 */
```

### Nested objects (dot-notation)

[](#nested-objects-dot-notation)

```
/** @input object $address Address
 *  @input string $address.city City
 *  @input string $address.zip ZIP code
 *  @input array $items Order items
 *  @input integer $items[].productId Product
 *  @input integer $items[].quantity Quantity */
```

### Headers, security, responses

[](#headers-security-responses)

```
/**
 * @header string $Authorization Bearer token
 * @header string ?$X-Request-Id Trace ID
 * @security BearerAuth
 * @response 200 {OrderResponse}
 * @response 422 {ValidationError}
 * @deprecated Use createV2 instead
 */
```

### Response templates

[](#response-templates)

Enable reusable schemas via `components/schemas`:

```
class Api extends BaseApi {
    public static bool $useResponseTemplates = true;

    public static function getOpenApiTemplates(): array {
        return [
            'OrderResponse' => [
                'id'         => 'integer!',            // required integer
                'title'      => 'string!',             // required string
                'total'      => 'number',              // optional number
                'created_at' => 'string(date-time)',   // with format
                'email'      => 'string(email)!',      // format + required
                'customer'   => '@Customer',           // $ref to another schema
                'items'      => '@OrderItem[]',        // array of $ref
            ],
            'Customer' => [
                'id'   => 'integer!',
                'name' => 'string!',
            ],
            'OrderItem' => [
                'product_id' => 'integer!',
                'quantity'   => 'integer',
                'price'      => 'number',
            ],
        ];
    }

    public static function getOpenApiSecurityDefinitions(): array {
        return [
            'BearerAuth' => ['type' => 'apiKey', 'name' => 'Authorization', 'in' => 'header'],
        ];
    }
}
```

**Shorthand syntax reference:**

SyntaxMeaningExample`type`Optional field`'number'`, `'string'`, `'object'``type!`Required field`'integer!'`, `'string!'``type(format)`With format`'string(date-time)'`, `'string(email)'``type(format)!`Format + required`'string(email)!'``@Model``$ref` to schema`'@Customer'``@Model[]`Array of `$ref``'@OrderItem[]'`Array format (`['type' => 'string', 'required' => true]`) is also supported and can be mixed with shorthand in the same template.

> Full tag reference: [docs/docblock-tags.md](docs/docblock-tags.md) | Cookbook: [docs/cookbook.md](docs/cookbook.md)

CRUD Scaffolding
----------------

[](#crud-scaffolding)

Implement `CrudService` for instant CRUD endpoints:

```
class ProductService extends CrudService {
    public function meta(): Meta {
        return (new Meta())
            ->string('name', 'Name')
            ->number('price', 'Price')
            ->select('status', 'Status', ['active', 'draft'])
            ->crud();
    }
    public function query(): Builder { return Product::query(); }
    public function resource(Model $model): BaseJsonResource { return new BaseJsonResource($model); }
    public function collection(Collection $c): BaseJsonResourceCollection { return BaseJsonResource::collection($c); }
}
```

### Search with filtering, sorting, pagination

[](#search-with-filtering-sorting-pagination)

```
POST /api/v1/product/search
{
  "filter": [
    {"column": "status", "operator": "=", "value": "active"},
    {"column": "price", "operator": "between", "value": [10, 100]},
    {"column": "name", "operator": "like", "value": "phone"},
    {"column": "description", "operator": "is_not_null"}
  ],
  "order": [{"column": "price", "value": "desc"}],
  "page": 1,
  "perPage": 20
}
```

**Available operators:** `=`, `!=`, `>`, `=`, ` ['action' => 'restore', 'method' => ['post']],
'forceDelete' => ['action' => 'forceDelete', 'method' => ['post']],
```

Testing
-------

[](#testing)

```
use Dskripchenko\LaravelApi\Traits\Testing\MakesHttpApiRequests;

class ProductTest extends TestCase
{
    use MakesHttpApiRequests;

    public function test_list(): void
    {
        $response = $this->api('v1', 'product', 'search');
        $this->assertApiSuccess($response);
    }

    public function test_not_found(): void
    {
        $response = $this->api('v1', 'product', 'read', ['id' => 999]);
        $this->assertApiError($response, 'not_found');
    }

    public function test_validation(): void
    {
        $response = $this->api('v1', 'product', 'create', []);
        $this->assertApiValidationError($response, ['name']);
    }
}
```

TypeScript Generation
---------------------

[](#typescript-generation)

Generate TypeScript interfaces from your OpenAPI spec:

```
php artisan api:generate-types                                    # All versions → resources/js/shared/api/types.ts
php artisan api:generate-types --version=v1                       # Specific version
php artisan api:generate-types --output=frontend/src/api/types.ts # Custom path
```

Given `@output integer $id` and `@output string ?$email`, the generator produces:

```
export interface UserShowOutput {
  id: number;
  email?: string;
}
```

Component schemas, operation inputs, and outputs are all generated. See [docs/cookbook.md](docs/cookbook.md#recipe-8-generate-typescript-interfaces) for details.

API Export
----------

[](#api-export)

Export your API spec in multiple formats:

```
php artisan api:export --format=postman    # Postman Collection v2.1
php artisan api:export --format=http       # JetBrains/VS Code .http files
php artisan api:export --format=markdown   # Standalone documentation
php artisan api:export --format=curl       # Bash script with curl commands
```

Options: `--version=v1` (specific version), `--output=path` (custom file). By default, generates per-version files (`v1.json`, `v1.http`, `v1.md`, `v1.sh`).

Configuration
-------------

[](#configuration)

Publish the configuration file:

```
php artisan vendor:publish --tag=laravel-api-config
```

```
// config/laravel-api.php
return [
    'prefix' => 'api',                                          // URL prefix
    'uri_pattern' => '{version}/{controller}/{action}',          // Route pattern
    'available_methods' => ['get', 'post', 'put', 'patch', 'delete'],
    'openapi_path' => 'public/openapi',                           // OpenAPI JSON output
    'doc_middleware' => [],                                       // Middleware for /api/doc
];
```

Error Handling
--------------

[](#error-handling)

### ApiException

[](#apiexception)

```
throw new ApiException('payment_failed', 'Insufficient funds');
// → {"success": false, "payload": {"errorKey": "payment_failed", "message": "Insufficient funds"}}
```

### Custom error handlers

[](#custom-error-handlers)

```
use Dskripchenko\LaravelApi\Facades\ApiErrorHandler;
use Dskripchenko\LaravelApi\Services\ApiResponseHelper;
use Illuminate\Database\Eloquent\ModelNotFoundException;

ApiErrorHandler::addErrorHandler(
    ModelNotFoundException::class,
    fn($e) => ApiResponseHelper::sayError(['errorKey' => 'not_found', 'message' => 'Not found'], 404)
);
```

Handlers support **inheritance**: registering a handler for `Exception` will also catch `RuntimeException` via `class_parents()` traversal.

### RequestIdMiddleware

[](#requestidmiddleware)

Add to your middleware stack for request tracing:

```
// Reads X-Request-Id from request header or generates UUID
// Adds request_id to Log::shareContext()
// Sets X-Request-Id on response header
Dskripchenko\LaravelApi\Middlewares\RequestIdMiddleware::class
```

Comparison with Alternatives
----------------------------

[](#comparison-with-alternatives)

### vs. Classical Laravel approach (manual routes + FormRequest)

[](#vs-classical-laravel-approach-manual-routes--formrequest)

AspectClassical Laravellaravel-api**Route definition**`routes/api.php` — one route per endpoint, manual versioning`getMethods()` — declarative array, versions via class inheritance**Versioning**Manual: route groups, separate controllers, copy-pasteAutomatic: `V2 extends V1`, inherit/override/disable actions**Documentation**Separate process: write OpenAPI YAML manually or use annotationsAuto-generated from `@input`/`@output` docblocks**Response format**Ad-hoc per controller, no standard envelopeStandardized `{success, payload}` envelope everywhere**CRUD boilerplate**Write controller + FormRequest + Resource for each entityImplement `CrudService` (4 methods), get 6+ endpoints**Middleware per action**Route-level middleware or controller middleware groupsFine-grained: global → controller → action with exclusion**Testing**`$this->getJson('/api/v1/users')``$this->api('v1', 'user', 'list')` + assertion helpers**Learning curve**Standard Laravel knowledgeLearn `getMethods()` structure + docblock tags**Flexibility**Full control over everythingConstrained to package conventions**When to choose**Complex APIs with non-standard routing, GraphQL, event-driven APIsREST APIs with versioning, standard CRUD, auto-documentation needs**Advantages of laravel-api:**

- Zero-maintenance documentation — docblocks are the single source of truth
- Version inheritance eliminates code duplication between API versions
- Standardized response format across all endpoints
- CRUD scaffolding reduces boilerplate by 60-80%

**Disadvantages of laravel-api:**

- Fixed URI pattern (`api/{version}/{controller}/{action}`) — not RESTful resource routes
- Opinionated response format — can't easily switch to JSON:API or HAL
- No native support for resource-style URLs (`/users/{id}` vs `/user/show?id=1`)

### vs. L5-Swagger (DarkaOnLine/L5-Swagger)

[](#vs-l5-swagger-darkaonlinel5-swagger)

AspectL5-Swaggerlaravel-api**Approach**OpenAPI-first: write annotations, generate docsCode-first: write docblocks, docs + routing together**Annotation style**Full OpenAPI annotations (`@OA\Get`, `@OA\Schema`, ...)Lightweight custom tags (`@input`, `@output`, `@header`)**Annotation verbosity**High: 15-30 lines per endpoint for full specLow: 3-10 lines per endpoint**Routing**None — documentation only, routes defined separatelyIntegrated — routing + docs from single `getMethods()`**Versioning**Manual — separate annotation groupsBuilt-in — class inheritance**CRUD generation**NoneBuilt-in `CrudService` + `CrudController`**Response format**Any — you define schemasFixed `{success, payload}` envelope**OpenAPI coverage**Full OpenAPI 3.0 spec supportSubset: covers 90% of common use cases**IDE support**Plugin support for `@OA\*` annotationsNo IDE plugin — but simpler syntax**Ecosystem**Large community, swagger-php underneathSmaller, focused package**Spec customization**Full control over every OpenAPI fieldLimited to supported tags**When to choose**API-first design, full OpenAPI compliance needed, existing routesRapid development, versioned APIs, integrated routing + docs**Advantages over L5-Swagger:**

- 3-5x less annotation code per endpoint
- Routing and documentation are always in sync (single source)
- Built-in API versioning with inheritance
- CRUD scaffolding included
- No need to learn the full OpenAPI annotation specification

**Disadvantages compared to L5-Swagger:**

- Less OpenAPI coverage (no callbacks, webhooks, links, discriminator)
- No IDE plugin for custom tags
- Fixed response format
- Smaller community and ecosystem
- Not suitable for API-first (design-first) workflow

### vs. Scramble (dedoc/scramble)

[](#vs-scramble-dedocscramble)

AspectScramblelaravel-api**Approach**Zero-config: infers spec from code (types, FormRequest, routes)Docblock tags: explicit `@input`/`@output` annotations**Route integration**Uses Laravel's native routesCustom routing via `getMethods()`**Documentation source**PHP types, FormRequest rules, return typesDocblock annotations**Manual annotations**Optional, for edge casesRequired for all endpoints**Versioning**None built-inBuilt-in class inheritance**CRUD**NoneBuilt-in CrudService**Setup effort**Minimal — install and it worksModerate — define module, API class, provider**When to choose**Standard Laravel routes, minimal documentation effortCustom routing, versioning, CRUD needs### Summary: When to use laravel-api

[](#summary-when-to-use-laravel-api)

✅ **Choose laravel-api when:**

- You need versioned APIs with inheritance between versions
- You want integrated routing + documentation from a single source
- You need CRUD scaffolding with filtering, sorting, pagination
- You prefer lightweight docblock tags over verbose annotations
- You want a standardized response format across all endpoints

❌ **Choose alternatives when:**

- You need RESTful resource-style URLs (`/users/{id}`)
- You need full OpenAPI 3.0 compliance (callbacks, webhooks, discriminator)
- You follow API-first (design-first) methodology
- You need GraphQL or non-REST APIs
- You want zero-annotation documentation (→ Scramble)

API Reference
-------------

[](#api-reference)

### Controllers

[](#controllers)

ClassMethods`ApiController``success($payload, $status)`, `error($payload, $status)`, `validationError($messages)`, `created($payload)`, `noContent()`, `notFound($message)``CrudController``meta()`, `search(CrudSearchRequest)`, `create(Request)`, `read(Request, int)`, `update(Request, int)`, `delete(int)``ApiDocumentationController``index()`### Services

[](#services)

ClassMethods`CrudService``meta()`, `query()`, `resource()`, `collection()`, `search()`, `create()`, `read()`, `update()`, `delete()`, `restore()`, `forceDelete()``ApiResponseHelper``say($data, $status)`, `sayError($data, $status)`### Components

[](#components)

ClassMethods`BaseApi` (all methods are `static`)`getMethods()`, `make()`, `getOpenApiTemplates()`, `getOpenApiSecurityDefinitions()`, `beforeCallAction()`, `afterCallAction()`, `getMiddleware()``BaseModule``getApi($version)`, `makeApi()`, `getApiVersionList()`, `getApiPrefix()`, `getApiUriPattern()`, `getAvailableApiMethods()`, `getDocMiddleware()``Meta``string($key, $name)`, `integer($key, $name)`, `number($key, $name)`, `boolean($key, $name)`, `hidden($key, $name)`, `select($key, $name, $items)`, `file($key, $name, $src)`, `action($key, $condition)`, `crud()`, `getOpenApiInputs()`, `getColumnKeys()`### Middleware

[](#middleware)

ClassPurpose`ApiMiddleware`Abstract base — catches `ApiException` and generic exceptions`RequestIdMiddleware`Generates/propagates `X-Request-Id`, adds to `Log::shareContext()`### Exceptions

[](#exceptions)

ClassPurpose`ApiException`Exception with `errorKey` string for structured error responses`ApiErrorHandler`Registry of exception handlers by class, with parent class traversal### Facades

[](#facades)

FacadeResolves to`ApiRequest``BaseApiRequest` — version, controller, action, HTTP method`ApiModule``BaseModule` — version resolution, route configuration`ApiErrorHandler``ApiErrorHandler` — exception handler registryLicense
-------

[](#license)

MIT License. See [LICENSE.md](LICENSE.md) for details.

###  Health Score

52

—

FairBetter than 96% of packages

Maintenance90

Actively maintained with recent releases

Popularity28

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity63

Established project with proven stability

 Bus Factor1

Top contributor holds 98.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~58 days

Recently: every ~3 days

Total

32

Last Release

51d ago

Major Versions

1.0.0 → 2.0.02021-04-02

2.5.3 → 3.0.02025-10-08

3.0.7 → v4.x-dev2026-03-07

### Community

Maintainers

![](https://www.gravatar.com/avatar/3aae29ece4986be5d3eea1ba57457547d2ce92fcdee466af45f69ec4079ec9c7?d=identicon)[dskripchenko](/maintainers/dskripchenko)

---

Top Contributors

[![dskripchenko](https://avatars.githubusercontent.com/u/5102028?v=4)](https://github.com/dskripchenko "dskripchenko (80 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

apilaravelswaggerversioning

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/dskripchenko-laravel-api/health.svg)

```
[![Health](https://phpackages.com/badges/dskripchenko-laravel-api/health.svg)](https://phpackages.com/packages/dskripchenko-laravel-api)
```

###  Alternatives

[darkaonline/l5-swagger

OpenApi or Swagger integration to Laravel

2.9k34.0M111](/packages/darkaonline-l5-swagger)[despark/apidoc

Generate api documentation and use swagger to consume it

132.3k](/packages/despark-apidoc)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
