PHPackages                             sunrise-studio/symfony-openapi - 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. sunrise-studio/symfony-openapi

ActiveLibrary[API Development](/categories/api)

sunrise-studio/symfony-openapi
==============================

OpenAPI Generator for Symfony Routing based on Sunrise Router

v1.2.0(2w ago)10133MITPHPPHP &gt;=8.2

Since Jun 15Pushed 2w ago1 watchersCompare

[ Source](https://github.com/sunrise-studio-development/symfony-openapi)[ Packagist](https://packagist.org/packages/sunrise-studio/symfony-openapi)[ Docs](https://github.com/sunrise-studio-development/symfony-openapi)[ RSS](/packages/sunrise-studio-symfony-openapi/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (3)Dependencies (11)Versions (6)Used By (0)

OpenAPI Generator for Symfony Routing
=====================================

[](#openapi-generator-for-symfony-routing)

[![Scrutinizer Code Quality](https://camo.githubusercontent.com/9dc18a26ea68da043811838a93744514874eb654a8aa6d1ab5a4bfb6b96dfaad/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f73756e726973652d73747564696f2d646576656c6f706d656e742f73796d666f6e792d6f70656e6170692f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/sunrise-studio-development/symfony-openapi/?branch=master)[![Code Coverage](https://camo.githubusercontent.com/c6b13bf53872d11601d07fa54d72d15863773b3b0c52e9ed5b5d3d989aa56fca/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f73756e726973652d73747564696f2d646576656c6f706d656e742f73796d666f6e792d6f70656e6170692f6261646765732f636f7665726167652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/sunrise-studio-development/symfony-openapi/?branch=master)[![Build Status](https://camo.githubusercontent.com/ff560c7ed63b22cd90f19280d957a9575004d7f517c7ca38694d287bce385f40/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f73756e726973652d73747564696f2d646576656c6f706d656e742f73796d666f6e792d6f70656e6170692f6261646765732f6275696c642e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/sunrise-studio-development/symfony-openapi/build-status/master)[![Code Intelligence Status](https://camo.githubusercontent.com/8174ea3ea63f86c4d8d426c48da8d95588c833aefc90de0066cc07bf33e12190/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f73756e726973652d73747564696f2d646576656c6f706d656e742f73796d666f6e792d6f70656e6170692f6261646765732f636f64652d696e74656c6c6967656e63652e7376673f623d6d6173746572)](https://scrutinizer-ci.com/code-intelligence)

Languages: [🇬🇧 English](README.md) | [🇨🇳 简体中文](README-zh-CN.md) | [🇪🇸 Español](README-es.md) | [🇵🇹 Português](README-pt.md) | [🇷🇺 Русский](README-ru.md) | [🇺🇦 Українська](README-uk.md)

This package generates an OpenAPI document from Symfony routes, controller signatures, Symfony HttpKernel attributes, and typed DTO/View classes.

The goal is to keep API documentation close to application code. Normal endpoints should not require large `#[OA\...]` blocks. Routes describe paths and methods, Symfony attributes describe request mapping, DTOs describe input data, view objects describe output data, and route options describe operation metadata. Manual OpenAPI fragments remain available for exceptional cases.

The public API lives in the `Sunrise\Symfony\OpenApi` namespace.

Further reading:

- [Article on Medium](https://medium.com/@a.fenric/openapi-without-oa-how-i-built-a-symfony-documentation-generator-af2ff83bd21c) explains what problem this package solves and how to use it in a Symfony application.
- [PHP Annotations plugin for PhpStorm](https://plugins.jetbrains.com/plugin/7320-php-annotations) is expected to support aliases for this package's attributes in upcoming releases, which should improve IDE completion and navigation.

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

[](#installation)

```
composer require sunrise-studio/symfony-openapi
```

The package requires PHP 8.2 or newer. Supported Symfony component versions are defined in `composer.json`. Symfony 8.1 or newer is only needed if your application wants to use Symfony's native `#[Serialize]` runtime attribute.

Register the bundle:

```
// config/bundles.php
return [
    Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
    Sunrise\Symfony\OpenApi\OpenApiBundle::class => ['all' => true],
];
```

Import the package routes:

```
# config/routes.yaml
openapi:
  resource: '@OpenApiBundle/config/routes.php'
```

This imports two documentation routes:

RouteControllerPurpose`GET /docs``SwaggerController`Serves Swagger UI configured to read `/docs/openapi.json`.`GET /docs/openapi.json``DocumentController`Serves the generated OpenAPI JSON document.These routes are not included in the generated API document because `api: true` is not set and their paths do not start with `/api/`.

If you need only one route, import its file directly:

```
openapi_document:
  resource: '@OpenApiBundle/config/routes/document.php'

swagger_ui:
  resource: '@OpenApiBundle/config/routes/swagger.php'
```

Symfony references:

- [Routing](https://symfony.com/doc/current/routing.html)
- [Bundles](https://symfony.com/doc/current/bundles.html)

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

[](#configuration)

Typical application configuration:

```
# config/packages/openapi.yaml
parameters:
  openapi.initial_document:
    openapi: 3.1.1
    info:
      title: API
      version: 1.0.0

  openapi.default_timestamp_format: !php/const DateTimeInterface::RFC3339_EXTENDED
```

Useful parameters:

ParameterDefaultPurpose`openapi.initial_document`OpenAPI version + `API` titleBase document merged with generated paths and schemas.`openapi.initial_operation``responses: []`Base operation merged into every generated operation.`openapi.document_filename``%kernel.project_dir%/var/openapi.json`Output file used by `openapi:build-document`.`openapi.document_uri``/docs/openapi.json`Public URI of the generated document. Swagger UI uses it to load the document.`openapi.default_timestamp_format``OpenApiConfiguration::DEFAULT_TIMESTAMP_FORMAT`PHP `date()` format used to generate OpenAPI `example` values for date/time schemas.`SwaggerConfiguration` can be replaced as a service if you need custom Swagger UI assets or template variables.

### Custom Route Paths

[](#custom-route-paths)

If only Swagger UI needs a different path, define the route yourself:

```
# config/routes.yaml
swagger_ui:
  path: /swagger.html
  controller: Sunrise\Symfony\OpenApi\Controller\SwaggerController
  methods: [GET]
  options:
    api: false
```

If the OpenAPI document route also changes, update both the route and `openapi.document_uri` so Swagger UI loads the correct document:

```
# config/routes.yaml
openapi_document:
  path: /openapi.json
  controller: Sunrise\Symfony\OpenApi\Controller\DocumentController
  methods: [GET]
  options:
    api: false
```

```
# config/packages/openapi.yaml
parameters:
  openapi.document_uri: /openapi.json
```

Building The Document
---------------------

[](#building-the-document)

Run:

```
php bin/console openapi:build-document
```

The command reads the route collection, keeps routes that should be documented, builds the OpenAPI document, and writes it to `openapi.document_filename`.

After generation, with the default package routes imported:

- `/docs` opens Swagger UI.
- `/docs/openapi.json` returns the generated JSON document.

Route Options
-------------

[](#route-options)

Route options are the default place for operation metadata:

```
use Symfony\Component\Routing\Attribute\Route;

#[Route('/api/pets', options: ['tags' => ['Pets']])]
final readonly class PetController
{
    #[Route('/{id}', methods: ['GET'], options: [
        'summary' => 'Finds pet by ID',
        'description' => 'Returns one pet.',
        'deprecated' => false,
    ])]
    public function show(int $id): PetView
    {
        // ...
    }
}
```

Supported options:

OptionTypePurpose`tag`, `tags``string|string[]`OpenAPI operation tags.`summary``string`OpenAPI operation summary.`description``string`OpenAPI operation description.`deprecated`, `is_deprecated`, `isDeprecated``bool`Marks an operation as deprecated.`api`, `is_api`, `isApi``bool`Includes or excludes the route from the generated document.`response_code``int`Documented response status when `#[Serialize]` does not provide one. Defaults are `200` for a response body and `204` for explicit `void`.`response_format``string`Response format for the documented response body, converted to a media type, for example `json` to `application/json`.`response_formats``string[]`Multiple response formats. Ignored when `response_format` is set.If no API option is set, routes whose path starts with `/api/` are treated as API routes.

If your project does not want to keep tags, summaries, descriptions, and API filtering in route options, replace `RouteMetadataResolverInterface`.

Symfony Attributes
------------------

[](#symfony-attributes)

The package understands the Symfony controller attributes that describe request data. See Symfony's [controller value resolver documentation](https://symfony.com/doc/current/controller/value_resolver.html).

### Path Variables

[](#path-variables)

Symfony path variables are read from compiled routes. Requirements are converted to OpenAPI schema patterns.

```
#[Route('/api/pets/{petId}', requirements: ['petId' => '\d+'])]
public function show(int $petId): PetView
{
    // ...
}
```

Supported reflected parameter types for path variables:

- `bool`
- `int`
- `float`
- `string`
- `BackedEnum`
- `DateTimeInterface`
- `Symfony\Component\Uid\AbstractUid`

Symfony route mapping aliases are supported for simple mappings such as `['id' => 'petId']`. Entity-style mappings such as `{id:pet.id}` are not described as object schemas; the public path variable is documented as a string unless a supported scalar parameter can be found.

### Query Parameter

[](#query-parameter)

`#[MapQueryParameter]` describes scalar, enum, date/time, UID, or array query parameters.

```
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;

public function find(
    #[MapQueryParameter] PetStatus $status,
    #[MapQueryParameter] string ...$tags,
): JsonResponse {
    // ...
}
```

Variadic parameters are described as arrays and are not marked as required.

### Query Object

[](#query-object)

`#[MapQueryString]` describes a query object.

```
use Symfony\Component\HttpKernel\Attribute\MapQueryString;

public function list(#[MapQueryString] PetSearchQuery $query): JsonResponse
{
    // ...
}
```

Without `key`, the parameter name is the PHP parameter name and the object uses `style: form`. With `key`, that key becomes the parameter name and the object uses `style: deepObject`.

Note

Symfony versions earlier than 7.3 do not contain the `MapQueryString::key` argument. See the [Symfony 7.3 HttpKernel changelog](https://github.com/symfony/symfony/blob/7.3/src/Symfony/Component/HttpKernel/CHANGELOG.md) and [Symfony 7.3 DX improvements](https://symfony.com/blog/new-in-symfony-7-3-dx-improvements-part-2#improved-mapquerystring).

### Request Body

[](#request-body)

`#[MapRequestPayload]` creates an OpenAPI `requestBody`.

```
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;

public function create(#[MapRequestPayload(acceptFormat: 'json')] CreatePetRequest $request): PetView
{
    // ...
}
```

Generated request body:

- The PHP parameter type becomes the request schema.
- `acceptFormat` is optional. If omitted, the route `_format` default is used; if `_format` is also missing, `json` is used.
- `acceptFormat` is converted from Symfony request format to media type, for example `json` to `application/json`.
- If the PHP parameter is required, the OpenAPI request body is required.
- For array payloads, `MapRequestPayload(type: SomeDto::class)` describes the item type.

Note

Symfony versions earlier than 7.1 do not contain the `MapRequestPayload::type` argument. See the [Symfony 7.1 HttpKernel changelog](https://github.com/symfony/symfony/blob/7.1/src/Symfony/Component/HttpKernel/CHANGELOG.md).

### Uploaded Files

[](#uploaded-files)

`#[MapUploadedFile]` adds a `multipart/form-data` request body with binary fields.

```
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpKernel\Attribute\MapUploadedFile;

public function upload(#[MapUploadedFile(name: 'photo')] UploadedFile $file): JsonResponse
{
    // ...
}
```

Variadic uploaded files are described as an array of binary strings and are not marked as required.

Note

Symfony versions earlier than 7.1 do not contain the `MapUploadedFile` attribute. See the [Symfony 7.1 HttpKernel changelog](https://github.com/symfony/symfony/blob/7.1/src/Symfony/Component/HttpKernel/CHANGELOG.md) and [Symfony 7.1 MapUploadedFile announcement](https://symfony.com/blog/new-in-symfony-7-1-mapuploadedfile-attribute).

### Date And Time

[](#date-and-time)

`#[MapDateTime(format: ...)]` changes the generated date/time example for controller parameters.

```
use Symfony\Component\HttpKernel\Attribute\MapDateTime;

public function history(#[MapDateTime(format: 'Y-m-d')] DateTimeImmutable $date): JsonResponse
{
    // ...
}
```

The `format` argument is optional. If omitted, the default timestamp format is used.

Responses
---------

[](#responses)

Write controller return types as the public API should look.

Controller return typeGenerated responseView object, DTO, scalar, arrayJSON response body. The schema is read from the method return type. Status is `200` by default.Explicit `void`Empty response. Status is `204` by default.Symfony `Response` subclassResponse body is not generated automatically. Use `#[Operation]` when it must be documented manually.For JSON APIs, no response format option is needed. Use route options only when the defaults do not match the endpoint:

- `response_code` changes the documented status, for example `201` for create actions.
- `response_format` documents one non-default response format.
- `response_formats` documents multiple response formats.

```
#[Route('/api/pets/{id}', methods: ['GET'])]
public function show(int $id): PetView
{
    // ...
}
```

Symfony 8.1 introduced [`#[Serialize]`](https://symfony.com/blog/new-in-symfony-8-1-serialize-attribute), which serializes controller results at runtime. When that attribute is present, this package reads `Serialize::code`; the schema still comes from the PHP return type.

```
use Symfony\Component\HttpKernel\Attribute\Serialize;

#[Route('/api/pets', methods: ['POST'], options: ['response_code' => 201])]
#[Serialize(code: 201)]
public function create(CreatePetRequest $request): PetView
{
    // ...
}
```

Use an explicit `void` return type for actions with no response body:

```
#[Route('/api/pets/{id}', methods: ['DELETE'])]
public function delete(int $id): void
{
    // ...
}
```

This documents the endpoint as an empty `204` response. Symfony itself does not convert a `null` controller result to `204`, so applications using `void` actions should handle that at runtime.

If the application cannot use Symfony 8.1 yet, a small `KernelEvents::VIEW` listener can handle both cases: `null` becomes `204`, and other controller results are serialized as JSON. Symfony's own implementation is [`SerializeControllerResultAttributeListener`](https://github.com/symfony/http-kernel/blob/ad1426284c2e7fe10de65dc68a25a724639e3838/EventListener/SerializeControllerResultAttributeListener.php); a minimal JSON-only listener can be this simple:

```
namespace App\EventListener;

use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Serializer\SerializerInterface;

#[AsEventListener(event: KernelEvents::VIEW)]
final readonly class JsonControllerResultListener
{
    public function __construct(
        private SerializerInterface $serializer,
    ) {
    }

    public function __invoke(ViewEvent $event): void
    {
        $result = $event->getControllerResult();
        if ($result === null) {
            $event->setResponse(new Response(status: 204));
            return;
        }

        $event->setResponse(new JsonResponse(
            $this->serializer->serialize($result, 'json'),
            200,
            json: true,
        ));
    }
}
```

If your project has different rules for response status or formats, replace `ResponseMetadataResolverInterface`.

OpenAPI Attributes
------------------

[](#openapi-attributes)

The package provides small OpenAPI attributes for cases where PHP types are not enough:

AttributeTargetPurpose`#[Operation]`class, methodAdds a manual OpenAPI operation fragment.`#[ItemType]`property, parameterDescribes array item type.`#[SchemaName]`classOverrides component schema name.`#[PropertyName]`propertyOverrides OpenAPI property name.`#[IgnoreProperty]`propertyExcludes a property from object schema.`#[TimestampFormat]`propertyOverrides date/time example format.Array item types are usually read from PHPDoc:

```
/** @var list */
public array $pets;
```

Supported PHPDoc forms include `PetView[]`, `list`, `array`, and `array`. Nullable item types such as `array` are supported. Broad or ambiguous item types such as `array` and `array` are ignored. Use `#[ItemType]` when you need an explicit override or an item limit; it has priority over `@var`.

Manual OpenAPI Fragments
------------------------

[](#manual-openapi-fragments)

Most endpoints should not need manual OpenAPI fragments. For exceptional cases, use `#[Operation]`:

```
use Sunrise\Symfony\OpenApi\Annotation\Operation;
use Sunrise\Symfony\OpenApi\Type;

#[Operation([
    'responses' => [
        200 => [
            'description' => 'A list of pets.',
            'content' => [
                'application/json' => [
                    'schema' => [
                        'type' => 'array',
                        'items' => new Type(PetView::class),
                    ],
                ],
            ],
        ],
    ],
])]
public function list(): JsonResponse
{
    // ...
}
```

The fragment is merged into the generated operation.

### Documenting Errors

[](#documenting-errors)

We recommend keeping API actions predictable: a successful action should return one documented view object, and errors should use a documented error shape instead of being hidden in controller branches.

For a shared error response, describe a `default` response in `#[Operation]` or in `openapi.initial_operation`:

```
# config/packages/openapi.yaml
parameters:
  openapi.initial_operation:
    responses:
      default:
        description: The operation was unsuccessful.
        content:
          application/json:
            schema: App\View\ErrorView
```

```
use App\View\ErrorView;
use Sunrise\Symfony\OpenApi\Annotation\Operation;
use Sunrise\Symfony\OpenApi\Type;

#[Operation([
    'responses' => [
        'default' => [
            'description' => 'The operation was unsuccessful.',
            'content' => [
                'application/json' => [
                    'schema' => new Type(ErrorView::class),
                ],
            ],
        ],
    ],
])]
final readonly class PetController
{
}
```

In YAML/PHP arrays, a schema value may be a PHP type string. It is treated as a PHP type when the string contains `\`. For a class without a namespace, use a leading backslash, for example `\AppErrorView`. In PHP attributes, use `new Type(ErrorView::class)` when you need an explicit type object.

PHP Type Schema Resolvers
-------------------------

[](#php-type-schema-resolvers)

The default schema generation covers common PHP types:

- `BoolPhpTypeSchemaResolver`
- `IntPhpTypeSchemaResolver`
- `FloatPhpTypeSchemaResolver`
- `StringPhpTypeSchemaResolver`
- `ArrayPhpTypeSchemaResolver`
- `ArrayAccessPhpTypeSchemaResolver`
- `BackedEnumPhpTypeSchemaResolver`
- `ObjectPhpTypeSchemaResolver`
- `SymfonyUidPhpTypeSchemaResolver`
- `Sunrise\Symfony\OpenApi\PhpTypeSchemaResolver\TimestampPhpTypeSchemaResolver`

If your project has a custom PHP type that needs a custom schema, implement `OpenApiPhpTypeSchemaResolverInterface`. With autoconfigure enabled, the resolver will be registered automatically. Otherwise, tag it with `openapi.php_type_schema_resolver`.

Object Schemas
--------------

[](#object-schemas)

DTO and View objects are described from typed properties.

It reads PHP classes directly:

- instantiable non-internal classes are supported;
- public, protected, and private properties are reflected;
- property types become OpenAPI property schemas;
- properties without a default value are marked as required;
- scalar and backed enum default values are emitted;
- constructor-promoted property defaults are supported;
- `#[SchemaName]` changes component schema name;
- `#[PropertyName]` changes property name;
- `#[IgnoreProperty]` excludes a property;
- array item types are read from `@var` when possible;
- `#[ItemType]` explicitly describes array properties and has priority over `@var`;
- `#[TimestampFormat]` changes date/time examples.

This resolver does not use Symfony Serializer metadata. It does not read serializer groups, getters, setters, `SerializedName`, name converters, or camelCase/snake\_case conversion rules.

We recommend explicit DTO and View classes with typed properties. If you need a different external shape, create a new View object and map your domain object into it. This keeps search, refactoring, and schema generation simple.

If your team needs first-class Symfony Serializer support, open an issue. We will consider adding it as an optional resolver or strategy.

Symfony Serializer reference: [Serializer](https://symfony.com/doc/current/serializer.html).

Extension Points
----------------

[](#extension-points)

The package is built from replaceable services for projects that need different conventions:

Service/interfacePurposeTag`RouteMetadataResolverInterface`Controls tags, summary, description, deprecation, and API filtering.—`ResponseMetadataResolverInterface`Controls response status codes and response formats.—`OpenApiOperationEnricherInterface`Adds request parameters, request bodies, responses, or custom operation data.`openapi.operation_enricher` (added automatically with autoconfigure)`OpenApiPhpTypeSchemaResolverInterface`Converts PHP types to OpenAPI schemas.`openapi.php_type_schema_resolver` (added automatically with autoconfigure)`OpenApiPathBuilderInterface`Converts Symfony route paths to OpenAPI paths.—Replace these services in the Symfony container when project rules differ from the defaults.

###  Health Score

46

—

FairBetter than 92% of packages

Maintenance96

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity50

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

Every ~12 days

Total

3

Last Release

19d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2872934?v=4)[Anatolii Nekhai](/maintainers/fenric)[@fenric](https://github.com/fenric)

---

Top Contributors

[![fenric](https://avatars.githubusercontent.com/u/2872934?v=4)](https://github.com/fenric "fenric (54 commits)")

---

Tags

openapiopenapi-generatorswaggerswagger-uisymfonysymfony-bundle

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Psalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/sunrise-studio-symfony-openapi/health.svg)

```
[![Health](https://phpackages.com/badges/sunrise-studio-symfony-openapi/health.svg)](https://phpackages.com/packages/sunrise-studio-symfony-openapi)
```

###  Alternatives

[symfony/framework-bundle

Provides a tight integration between Symfony components and the Symfony full-stack framework

3.6k251.7M11.8k](/packages/symfony-framework-bundle)[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.9M401](/packages/easycorp-easyadmin-bundle)[drupal/core

Drupal is an open source content management platform powering millions of websites and applications.

19566.0M1.8k](/packages/drupal-core)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6942.5M425](/packages/drupal-core-recommended)[shopware/storefront

Storefront for Shopware

674.6M248](/packages/shopware-storefront)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M605](/packages/shopware-core)

PHPackages © 2026

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