PHPackages                             botnetdobbs/laravel-luminous - 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. botnetdobbs/laravel-luminous

ActiveLibrary[API Development](/categories/api)

botnetdobbs/laravel-luminous
============================

PHP 8 Attribute-driven OpenAPI 3.1 documentation for Laravel

1.0.0(today)001MITPHPPHP ^8.2CI passing

Since Jun 30Pushed todayCompare

[ Source](https://github.com/botnet-dobbs/laravel-luminous)[ Packagist](https://packagist.org/packages/botnetdobbs/laravel-luminous)[ RSS](/packages/botnetdobbs-laravel-luminous/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (11)Versions (2)Used By (0)

Luminous
========

[](#luminous)

[![build](https://github.com/botnet-dobbs/laravel-luminous/actions/workflows/main.yml/badge.svg)](https://github.com/botnet-dobbs/laravel-luminous/actions/workflows/main.yml) [![Packagist Downloads](https://camo.githubusercontent.com/e2c8c093bef3a38559adef476ab887d4491f182fb1ee7be57c41a3408912169b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f626f746e6574646f6262732f6c61726176656c2d6c756d696e6f7573)](https://camo.githubusercontent.com/e2c8c093bef3a38559adef476ab887d4491f182fb1ee7be57c41a3408912169b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f626f746e6574646f6262732f6c61726176656c2d6c756d696e6f7573)

Generate OpenAPI 3.2.0 docs from PHP 8 Attributes on your Laravel controllers. The docs are the code, so they cannot drift.

No YAML files to maintain. No docblocks to parse. Put a few attributes on your controllers, let your FormRequest `rules()` define the request body, document your API Resources with a single static `schema()` method, and Luminous builds the full spec automatically.

Requirements
------------

[](#requirements)

- PHP 8.2+

Laravel VersionLaravel 11.xLaravel 12.xLaravel 13.x---

Installation
------------

[](#installation)

```
composer require botnetdobbs/laravel-luminous
```

Publish the config file:

```
php artisan vendor:publish --tag=luminous-config
```

Your API docs are live at `/docs`.

---

Quick Look
----------

[](#quick-look)

### The controller

[](#the-controller)

Tag the controller and secure it. Every method inside inherits both automatically.

```
use Botnetdobbs\Luminous\Attributes\ApiTag;
use Botnetdobbs\Luminous\Attributes\ApiSecurity;
use Botnetdobbs\Luminous\Attributes\ApiOperation;
use Botnetdobbs\Luminous\Attributes\ApiParam;
use Botnetdobbs\Luminous\Attributes\ApiQuery;
use Botnetdobbs\Luminous\Attributes\ApiHeader;
use Botnetdobbs\Luminous\Attributes\ApiResponse;
use Botnetdobbs\Luminous\Attributes\ApiIgnore;

#[ApiTag('Payments')]
#[ApiSecurity('bearerAuth')]
class PaymentController extends Controller
{
    #[ApiOperation('List payments')]
    #[ApiQuery('status', 'Filter by status', enum: ['pending', 'succeeded', 'failed'])]
    #[ApiQuery('limit', 'Results per page', type: 'integer', example: 20)]
    #[ApiResponse(200, PaymentResource::class, 'Payments list', paginated: true)]
    public function index(Request $request): JsonResponse {}

    #[ApiOperation('Get a payment')]
    #[ApiParam('payment', 'Payment ID', type: 'integer', example: 42)]
    #[ApiResponse(200, PaymentResource::class, 'Payment retrieved')]
    #[ApiResponse(404, ErrorResource::class, 'Not found')]
    public function show(Payment $payment): JsonResponse {}

    #[ApiOperation('Create a payment', 'Initiates a payment. Requires an idempotency key.')]
    #[ApiHeader('Idempotency-Key', required: true, format: 'uuid')]
    #[ApiResponse(201, PaymentResource::class, 'Payment created')]
    #[ApiResponse(409, ErrorResource::class, 'Idempotency conflict')]
    #[ApiResponse(422, ErrorResource::class, 'Validation failed')]
    public function store(CreatePaymentRequest $request): JsonResponse {}

    #[ApiIgnore]
    public function internalReconcile(): void {}
}
```

Luminous detects `CreatePaymentRequest` from the type hint and reads its `rules()` to build the request body schema. It detects `Payment` as a route model bound parameter and maps it to the `{payment}` segment automatically. No wiring required.

### The FormRequest

[](#the-formrequest)

`rules()` defines the schema. `hints()` adds descriptions and examples for fields that need more context. You only need `hints()` when the field name alone is not enough.

```
use Botnetdobbs\Luminous\Support\Shape;

class CreatePaymentRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'amount'      => ['required', 'integer', 'min:1'],
            'currency'    => ['required', Rule::in(['USD', 'EUR', 'KES'])],
            'description' => ['sometimes', 'string', 'max:500'],
        ];
    }

    public function hints(): array
    {
        return [
            'amount' => Shape::integer()
                ->description('Amount in minor units. 10000 = $100.00')
                ->example(10000),
        ];
    }
}
```

### The API Resource

[](#the-api-resource)

Add `#[ApiShape]` and write a `schema()` method. Luminous calls it when building the response schema.

```
use Botnetdobbs\Luminous\Attributes\ApiShape;
use Botnetdobbs\Luminous\Support\Shape;

#[ApiShape]
class PaymentResource extends JsonResource
{
    public static function schema(): Shape
    {
        return Shape::object([
            'id'          => Shape::integer()->readOnly(),
            'status'      => Shape::enum(PaymentStatus::class)->readOnly(),
            'amount'      => Shape::integer()->readOnly()->description('Amount in minor units'),
            'currency'    => Shape::string()->readOnly()->example('USD'),
            'description' => Shape::string()->nullable()->readOnly(),
            'created_at'  => Shape::dateTime()->readOnly(),
        ]);
    }

    public function toArray(Request $request): array
    {
        return [
            'id'          => $this->id,
            'status'      => $this->status,
            'amount'      => $this->amount,
            'currency'    => $this->currency,
            'description' => $this->description,
            'created_at'  => $this->created_at->toIso8601String(),
        ];
    }
}
```

That is the whole picture. The individual docs below go into every detail.

---

Documentation
-------------

[](#documentation)

- [Configuration](docs/configuration.md)
- [Documenting Controllers](docs/controllers.md)
- [Documenting Form Requests](docs/form-requests.md)
- [Documenting API Resources](docs/resources.md)
- [The Shape Builder](docs/shape-builder.md)
- [Security](docs/security.md)
- [CLI Commands and Deployment](docs/deployment.md)

---

Attribute Reference
-------------------

[](#attribute-reference)

AttributeWhere it goesWhat it does`#[ApiOperation]`MethodSummary, description, and optional operationId`#[ApiTag]`Class or MethodGroup endpoints in the Swagger UI sidebar. Supports `summary`, `parent`, and `kind` for hierarchical grouping`#[ApiBody]`MethodOverride the auto-detected request class or add a description`#[ApiResponse]`MethodDocument a response status code (repeatable)`#[ApiParam]`MethodDocument a path parameter, including route model bound params (repeatable). Supports `deprecated``#[ApiQuery]`MethodDocument a query string parameter (repeatable). Supports `deprecated` and `location` for `in: querystring` parameters`#[ApiStream]`MethodDocument a streaming endpoint (SSE, JSONL). Emits `itemSchema` instead of `schema``#[ApiHeader]`MethodDocument a request header (repeatable)`#[ApiSecurity]`Class or MethodDeclare a required security scheme with optional scopes (repeatable)`#[ApiNoSecurity]`MethodMark an endpoint as requiring no authentication`#[ApiDeprecated]`MethodMark an endpoint as deprecated with a reason and replacement`#[ApiIgnore]`Class or MethodExclude from documentation entirely`#[ApiExample]`MethodNamed request or response example (repeatable). Supports `description` for longer explanatory text alongside `summary``#[ApiComposedOf]`MethodoneOf / anyOf / allOf for polymorphic responses (repeatable)`#[ApiShape]`Resource, FormRequest, or DTO classMarks a class as using the static `schema()` method`#[ApiProperty]`PropertyDocuments a single property on a resource or DTO`#[ApiItems]`PropertyDocuments the item type inside an array property---

Common Questions
----------------

[](#common-questions)

**Do I need to annotate every controller method?**

No. Every route appears in the spec. Unannotated routes get an auto-generated summary from the controller and method name. Annotations make things accurate and readable but are never required.

**What happens if I forget `#[ApiResponse]`?**

Luminous adds a default `500 Internal Server Error` response. Other responses are assumed to return a generic object. Add `#[ApiResponse]` to make the response schemas accurate.

**My FormRequest has constructor dependencies. Will `rules()` work?**

For most cases yes. Luminous instantiates the request without calling the constructor. If your `rules()` calls `$this->user()` or reads injected services, those calls fail silently and Luminous returns a bare schema. Restructure those rules to avoid request-time dependencies, or use `#[ApiShape]` to provide the schema explicitly.

**Can I use a plain PHP class or DTO with `#[ApiResponse]`?**

Yes. `#[ApiResponse]` accepts any class name. If the class has `#[ApiShape]` with a `schema()` method, or has `#[ApiProperty]` on its public properties, Luminous extracts the schema from it just like it would from a `JsonResource`.

---

Credits
-------

[](#credits)

- [Lazarus Odhiambo](https://github.com/botnetdobbs)
- [All Contributors](../../contributors)

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity1

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/35170812?v=4)[Lazarus Odhiambo](/maintainers/botnetdobbs)[@botnetdobbs](https://github.com/botnetdobbs)

---

Top Contributors

[![botnetdobbs](https://avatars.githubusercontent.com/u/35170812?v=4)](https://github.com/botnetdobbs "botnetdobbs (20 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/botnetdobbs-laravel-luminous/health.svg)

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

###  Alternatives

[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77018.2M127](/packages/laravel-mcp)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.1M337](/packages/psalm-plugin-laravel)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.8k](/packages/larastan-larastan)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9772.3M122](/packages/roots-acorn)[propaganistas/laravel-disposable-email

Disposable email validator

6012.9M7](/packages/propaganistas-laravel-disposable-email)[spatie/laravel-export

Create a static site bundle from a Laravel app

672139.5k6](/packages/spatie-laravel-export)

PHPackages © 2026

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