Search by

snorky22 / symfony-api-forge

snorky22

Generate Symfony services for REST APIs and webhooks from the API's documentation: curl and JSON examples become editable descriptors, from which typed HTTP clients and signed webhook consumers are regenerated. With generated doctors, local webhook testing and a local web interface that guides the w

Package info

github.com/snorky22/symfony-api-forge

Documentation

Type:symfony-bundle

pkg:composer/snorky22/symfony-api-forge

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.2.0 2026-11-09 10:30 UTC

This package is auto-updated.

Last update: 2026-11-09 10:32:04 UTC


README

Symfony bundle that generates services for REST APIs and their webhooks from their documentation: the curl examples and the JSON examples found in the API owner's docs.

curl + JSON ────api-forge:import──────────┐
object JSON ────api-forge:import-object───┤
events table,  ─api-forge:import-webhook──┼──> config/api/<api>/*.yaml ──api-forge:generate──> src/Api/<Namespace>/Generated/
payload JSON                              │    descriptors, edited by hand                     config/packages/api_<api>.yaml
  • Lenient import, strict generation: documentation examples become editable YAML descriptors, with every guess listed for review; the code is regenerated from the descriptors, so manual fixes survive.
  • Typed REST clients: one service per resource on a scoped HttpClient, JSON:API request bodies and pagination, PHPStan / PhpStorm array shapes for every response.
  • Signed webhooks: request parser checking the HMAC signature (Symfony Webhook), typed consumer methods per event (RemoteEvent), asynchronous processing with Messenger.
  • Local webhook testing: api-forge:webhook:send and WebhookTestTrait deliver payloads signed like the API, without a tunnel nor a real event; api-forge:webhook:check finds what breaks the chain; the delivery inbox records what arrived and why it was rejected, api-forge:webhook:replay delivers it again.
  • Generated doctors: one per API, regenerated with its services; api-forge:doctor checks the credentials, calls a harmless probe and diagnoses what fails (unreachable, rejected key, wrong URL…).
  • A local web interface: api-forge:serve runs the whole workflow as forms in the browser (describe the API, a wizard per endpoint, review notes with one-click fixes, generate, run the doctor), with an Ace editor wherever a file is edited. Plain PHP on the built-in server, no framework, Bootstrap and Ace shipped.
  • Documentation pages as text: the field list of an API's docs page, pasted as text, tells which fields are required, nullable, enums, and the types the examples leave unknown.

Requirements

  • PHP 8.2 or later
  • Symfony 7.4

Installation

composer require snorky22/symfony-api-forge

With Symfony Flex, the bundle is registered automatically. Otherwise, add it to config/bundles.php:

return [
    // ...
    Snorky22\ApiForge\ApiForgeBundle::class => ['all' => true],
];

The bundle provides the api-forge:* commands. The generated code only depends on the runtime classes of the package (Snorky22\ApiForge\Core), so the bundle can be enabled in the dev environment only (['dev' => true]).

To receive webhooks, the /webhook/{type} route of the Symfony Webhook component must be imported; the Flex recipe of symfony/webhook adds it (config/routes/webhook.yaml). For asynchronous processing, install symfony/messenger, see Webhooks.

Configuration

All the options are optional; these are the defaults:

# config/packages/api_forge.yaml
api_forge:
    descriptor_dir: '%kernel.project_dir%/config/api'   # descriptors
    namespace: 'App\Api'                                # namespace of the generated code
    source_dir: 'src/Api'                               # its directory, relative to the project
    config_dir: 'config/packages'                       # generated configuration, relative to the project

Quick start

# 1. Import documented requests (and example responses), objects and webhooks into descriptors
# (documentation examples in tests/Fixtures/<api>/, named as `api-forge:guide` shows)
bin/console api-forge:import lemonsqueezy tests/Fixtures/lemonsqueezy/list-customers.curl
bin/console api-forge:import-object lemonsqueezy customers tests/Fixtures/lemonsqueezy/customer-object.json
bin/console api-forge:import-webhook lemonsqueezy --events=tests/Fixtures/lemonsqueezy/webhook-events.md \
    --payload=tests/Fixtures/lemonsqueezy/webhook-order_created.json

# 2. Review config/api/lemonsqueezy/*.yaml, fix the guesses listed under "review"

# 3. Generate the services
bin/console api-forge:generate lemonsqueezy

Secrets go in .env.local, under the names given in the descriptors (e.g. LEMONSQUEEZY_API_KEY, LEMONSQUEEZY_WEBHOOK_SECRET).

public function __construct(private LemonSqueezyApi $lemonSqueezy) {}

foreach ($this->lemonSqueezy->customers->iterate() as $customer) {
    $customer['attributes']['email'];
}

#[AsRemoteEventConsumer('lemonsqueezy')]
final class LemonSqueezyWebhookConsumer extends AbstractLemonSqueezyWebhookConsumer
{
    protected function onOrderCreated(array $payload, RemoteEvent $event): void { /* ... */ }
}

examples/lemonsqueezy/ shows the result for Lemon Squeezy: descriptors, generated code and configuration, and a webhook consumer.

Documentation

Page Content
Architecture Pipeline, design choices, package layout
Workflow Adding an API or an endpoint, step by step; documentation examples (api-forge:guide)
Web interface The workflow as forms in the browser (api-forge:serve)
Descriptors Reference of the descriptor format (config/api/)
Commands Console commands and their options
Generated services Using the generated code, the runtime layer, errors, pagination
Doctor Generated doctors: what they check, the probe
Webhooks Receiving webhooks: setup, consumers, payload types, simulated deliveries, testing
Roadmap Known limitations and next steps

Development

git clone git@github.com:snorky22/symfony-api-forge.git
cd symfony-api-forge
composer install
vendor/bin/phpunit

The tests run a small application (tests/App/TestKernel.php) with the bundle and the Lemon Squeezy example. After changing the generator, regenerate the example, which a test checks:

php tests/console api-forge:generate lemonsqueezy --dir=examples/lemonsqueezy/config/api --output=examples/lemonsqueezy

License

MIT, see LICENSE. The web interface ships two third-party assets under their own licenses, kept next to them: Bootstrap (MIT) and Ace (BSD-3-Clause); see THIRD_PARTY_LICENSES.md.