PHPackages                             fissible/forge - 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. fissible/forge

ActiveLibrary[API Development](/categories/api)

fissible/forge
==============

OpenAPI spec generation for PHP — scaffolds specs from routes and infers schemas from validation rules

v1.0.0(3mo ago)01.2kMITPHPPHP ^8.2CI passing

Since Mar 25Pushed 2mo agoCompare

[ Source](https://github.com/fissible/forge)[ Packagist](https://packagist.org/packages/fissible/forge)[ Docs](https://github.com/fissible/forge)[ RSS](/packages/fissible-forge/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (1)Dependencies (5)Versions (2)Used By (0)

fissible/forge
==============

[](#fissibleforge)

OpenAPI spec generation for PHP. Scaffolds a valid OpenAPI 3.0 spec from your application's routes, inferring request body schemas from Laravel FormRequest validation rules.

Part of the [Fissible](https://github.com/fissible) suite. **Depends on:** fissible/accord and fissible/drift (uses route inspection and spec source interfaces).

```
  [forge]  ──────────────────────────────►  [accord]  ◄── [watch] ◄── [fault]
  ← you are here                           validate at      cockpit UI   exception
  generate / update spec                   runtime │        (bolt-on)    tracking
      ▲                                            ▼
      └──────────────────────────────────  [drift]
                                           detect drift, bump version

```

---

Why spec generation helps
-------------------------

[](#why-spec-generation-helps)

Writing an OpenAPI spec by hand for an existing API is tedious and easy to get wrong. Every route needs a path entry, every request body needs a schema, and any mistake means the spec diverges from reality before it's even been published.

**forge** does the mechanical work for you. It reads the routes your application already defines, inspects any FormRequest classes to understand what each endpoint expects, and produces a valid, structured OpenAPI 3.0 spec as a starting point. You fill in the response schemas; forge handles everything else.

The output is a real spec — one you can commit, version, and hand to fissible/accord for validation, or share with API consumers and tooling that understands OpenAPI.

---

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

[](#requirements)

- PHP ^8.2
- fissible/accord ^1.0
- fissible/drift ^1.0

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

[](#installation)

```
composer require fissible/forge
```

### Laravel auto-discovery

[](#laravel-auto-discovery)

The service provider registers automatically. The generate command is registered with Artisan:

```
php artisan accord:generate
```

---

How it works
------------

[](#how-it-works)

Forge enumerates your routes, groups them by path, and builds an OpenAPI operation for each. For `POST`, `PUT`, and `PATCH` routes, it inspects the controller action signature to find any `FormRequest` class, calls `rules()` on it, and converts those rules to a JSON Schema object for the `requestBody`.

Response schemas are scaffolded as empty objects (`{}`) — these are intentionally left for you to fill in, since response structure can't be reliably inferred from routes alone.

---

Console command
---------------

[](#console-command)

### `accord:generate`

[](#accordgenerate)

Generates a spec file from your API routes:

```
php artisan accord:generate
php artisan accord:generate --version=v2
php artisan accord:generate --title="Acme API" --output=docs/openapi.yaml
php artisan accord:generate --force   # overwrite existing file
```

OptionDefaultDescription`--version``v1`URI version to generate for (filters routes matching `/v1/...`)`--title``API`Value for `info.title` in the spec`--output``resources/openapi/{version}.yaml`Output file path`--force`—Overwrite an existing spec fileThe generated file is ready to commit and compatible with fissible/accord for runtime validation.

---

Schema inference
----------------

[](#schema-inference)

Forge maps Laravel validation rules to JSON Schema properties:

RuleSchema effect`integer`, `int`, `numeric``type: integer``boolean`, `bool`, `accepted`, `declined``type: boolean``array``type: array``number`, `decimal``type: number``string` (default)`type: string``email``format: email``url``format: uri``date``format: date``date_format:*``format: date-time``uuid``format: uuid``nullable``nullable: true``min:N``minLength` (string) or `minimum` (numeric)`max:N``maxLength` (string) or `maximum` (numeric)`in:a,b,c``enum: [a, b, c]``digits:N``minLength: N`, `maxLength: N``required`field added to `required` arrayRule objects (e.g. `Rule::in([...])`) are skipped — add those schema details manually after generation.

Dot-notation nested fields (e.g. `address.city`) are currently skipped. The parent field (`address`) is included with `type: array`.

---

Example output
--------------

[](#example-output)

Given a `POST /v1/users` route with this FormRequest:

```
public function rules(): array
{
    return [
        'name'  => 'required|string|max:100',
        'email' => 'required|email',
        'role'  => 'in:admin,editor,viewer',
    ];
}
```

Forge generates:

```
openapi: 3.0.3
info:
  title: API
  version: 1.0.0
paths:
  /v1/users:
    post:
      operationId: v1.users.post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 100
                email:
                  type: string
                  format: email
                role:
                  type: string
                  enum: [admin, editor, viewer]
              required: [name, email]
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object   # ← fill this in
```

---

Laravel
-------

[](#laravel)

### FormRequest inspector

[](#formrequest-inspector)

The bundled `LaravelFormRequestInspector` reflects controller action signatures to find `FormRequest` subclasses:

```
// Automatically wired by ForgeServiceProvider
use Fissible\Forge\Drivers\Laravel\Inspectors\LaravelFormRequestInspector;
```

It handles any controller action of the form `ControllerClass@method`. Closure-based routes are skipped. The inspector uses `ReflectionClass::newInstanceWithoutConstructor()` to call `rules()` without triggering Laravel's request validation lifecycle — this means FormRequests with `required` fields work correctly during spec generation, even outside of an HTTP request context. If `rules()` itself cannot run without a real request (e.g. because it reads `$this->route()`), it falls back to an empty object schema.

### Custom inspectors

[](#custom-inspectors)

Implement `FormRequestInspectorInterface` to extract validation rules from any source:

```
use Fissible\Forge\FormRequestInspectorInterface;
use Fissible\Drift\RouteDefinition;

class MyInspector implements FormRequestInspectorInterface
{
    public function getFormRequestClass(RouteDefinition $route): ?string
    {
        // return the FQCN of the form request for this route, or null
    }

    public function getRules(string $formRequestClass): array
    {
        // return an array of validation rules
    }
}
```

Bind it in your service provider before `ForgeServiceProvider` loads, or override it after:

```
$this->app->singleton(FormRequestInspectorInterface::class, MyInspector::class);
```

---

Core API
--------

[](#core-api)

Use forge programmatically without the console command:

```
use Fissible\Forge\SchemaInferrer;
use Fissible\Forge\SpecGenerator;

$generator = new SpecGenerator(
    schemaInferrer:       new SchemaInferrer(),
    formRequestInspector: $inspector, // optional
);

$spec = $generator->generate($routes, version: 'v1', title: 'My API');
// $spec is a plain PHP array — serialize it however you like
```

---

Recommended workflow
--------------------

[](#recommended-workflow)

1. **Generate** a spec from your existing routes with `accord:generate`
2. **Fill in** the response schemas in the generated YAML
3. **Commit** the spec to version control
4. **Validate** requests and responses at runtime with fissible/accord
5. **Detect drift** as your API evolves with fissible/drift

---

License
-------

[](#license)

MIT

###  Health Score

42

—

FairBetter than 89% of packages

Maintenance84

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

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

90d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ef298fbffac2225e36ab3a6e089af020f76885bc65d5f6afda831c64939e1e4b?d=identicon)[fissible](/maintainers/fissible)

---

Top Contributors

[![fissible](https://avatars.githubusercontent.com/u/1410914?v=4)](https://github.com/fissible "fissible (11 commits)")

---

Tags

laravelscaffoldingopenapigeneratorspec

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/fissible-forge/health.svg)

```
[![Health](https://phpackages.com/badges/fissible-forge/health.svg)](https://phpackages.com/packages/fissible-forge)
```

###  Alternatives

[laravel/sail

Docker files for running a basic Laravel application.

1.9k199.2M1.2k](/packages/laravel-sail)[darkaonline/l5-swagger

OpenApi or Swagger integration to Laravel

2.9k36.4M126](/packages/darkaonline-l5-swagger)[tightenco/jigsaw

Simple static sites with Laravel's Blade.

2.3k449.3k30](/packages/tightenco-jigsaw)[friendsoftypo3/content-blocks

TYPO3 CMS Content Blocks - Content Types API | Define reusable components via YAML

101466.4k45](/packages/friendsoftypo3-content-blocks)[swisnl/openapi-spec-generator

Creates Open API spec for a Laravel JSON:API

2341.2k](/packages/swisnl-openapi-spec-generator)

PHPackages © 2026

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