Skip to content

Latest commit

 

History

History
602 lines (445 loc) · 18.8 KB

index.rst

File metadata and controls

602 lines (445 loc) · 18.8 KB

NelmioApiDocBundle

The NelmioApiDocBundle bundle allows you to generate documentation in the OpenAPI (Swagger) format and provides a sandbox to interactively experiment with the API.

What's supported?

This bundle supports Symfony route requirements, Symfony request mapping (symfony_attributes), PHP annotations, Swagger-Php annotations, FOSRestBundle annotations and applications using Api-Platform.

For models, it supports the Symfony serializer , the JMS serializer and the willdurand/Hateoas library. It does also support Symfony form types.

Attributes are supported from version 4.7 and PHP 8.1.

Migrate from 3.x to 4.0

To migrate from 3.x to 4.0, follow our guide.

Installation

Open a command console, enter your project directory and execute the following command to download the latest version of this bundle:

$ composer require nelmio/api-doc-bundle

By default, only routes under /api are documented. Update the regexp at nelmio_api_doc.areas.path_patterns in config/packages/nelmio_api_doc.yaml to change this policy.

Note

If you're not using Flex, then add the bundle to your kernel:

class AppKernel extends Kernel
{
    public function registerBundles(): iterable
    {
        $bundles = [
            // ...

            new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
        ];

        // ...
    }
}

To browse your documentation with an UI, register one of the following route:

# config/routes.yaml
app.swagger_ui:
    path: /api/doc
    methods: GET
    defaults: { _controller: nelmio_api_doc.controller.swagger_ui }
# config/routes.yaml
app.redocly:
    path: /api/doc
    methods: GET
    defaults: { _controller: nelmio_api_doc.controller.redocly }

If you also want to expose it in JSON, register this route:

# config/routes.yaml
app.swagger:
    path: /api/doc.json
    methods: GET
    defaults: { _controller: nelmio_api_doc.controller.swagger }

As you just installed the bundle, you'll likely see routes you don't want in your documentation such as /_profiler/. To fix this, you can filter the routes that are documented by configuring the bundle:

# config/packages/nelmio_api_doc.yaml
nelmio_api_doc:
    areas:
        path_patterns: # an array of regexps (document only routes under /api, except /api/doc)
            - ^/api(?!/doc$)
        host_patterns: # document only routes with a host of the form api.*
            - ^api\.

Tip

Twig and Assets packages are needed to use swagger ui.

How does this bundle work?

It generates an OpenAPI documentation from your Symfony app thanks to Describers. One extracts data from SwaggerPHP annotations, one from your routes, etc.

If you configured the app.swagger_ui route above, you can browse your documentation at http://example.org/api/doc.

Using the bundle

You can configure global information in the bundle configuration documentation.info section (take a look at the OpenAPI 3.0 specification (formerly Swagger) to know the available fields).

nelmio_api_doc:
    documentation:
        servers:
          - url: http://api.example.com/unsafe
            description: API over HTTP
          - url: https://api.example.com/secured
            description: API over HTTPS
        info:
            title: My App
            description: This is an awesome app!
            version: 1.0.0
            x-build: #CommitHash
        components:
            securitySchemes:
                Bearer:
                    type: http
                    scheme: bearer
                    bearerFormat: JWT
        security:
            - Bearer: []

Note

If you're using Flex, this config is there by default under config/packages/nelmio_api_doc.yaml. Don't forget to adapt it to your app!

Tip

This configuration field can more generally be used to store your documentation as yaml. You may find in the .yaml files from SwaggerPHP examples.

To document your routes, you can use the SwaggerPHP annotations and the Nelmio\ApiDocBundle\Annotation\Model annotation in your controllers:

.. configuration-block::
namespace AppBundle\Controller;

use AppBundle\Entity\Reward;
use AppBundle\Entity\User;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Annotation\Security;
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\Annotation\Route;

class UserController
{
    /**
     * List the rewards of the specified user.
     *
     * This call takes into account all confirmed awards, but not pending or refused awards.
     *
     * @Route("/api/{user}/rewards", methods={"GET"})
     * @OA\Response(
     *     response=200,
     *     description="Returns the rewards of an user",
     *     @OA\JsonContent(
     *        type="array",
     *        @OA\Items(ref=@Model(type=Reward::class, groups={"full"}))
     *     )
     * )
     * @OA\Parameter(
     *     name="order",
     *     in="query",
     *     description="The field used to order rewards",
     *     @OA\Schema(type="string")
     * )
     * @OA\Tag(name="rewards")
     * @Security(name="Bearer")
     */
    public function fetchUserRewardsAction(User $user)
    {
        // ...
    }
}
namespace AppBundle\Controller;

use AppBundle\Entity\Reward;
use AppBundle\Entity\User;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Annotation\Security;
use OpenApi\Attributes as OA;
use Symfony\Component\Routing\Annotation\Route;

class UserController
{
    /**
     * List the rewards of the specified user.
     *
     * This call takes into account all confirmed awards, but not pending or refused awards.
     */
    #[Route('/api/{user}/rewards', methods: ['GET'])]
    #[OA\Response(
        response: 200,
        description: 'Returns the rewards of an user',
        content: new OA\JsonContent(
            type: 'array',
            items: new OA\Items(ref: new Model(type: AlbumDto::class, groups: ['full']))
        )
    )]
    #[OA\Parameter(
        name: 'order',
        in: 'query',
        description: 'The field used to order rewards',
        schema: new OA\Schema(type: 'string')
    )]
    #[OA\Tag(name: 'rewards')]
    #[Security(name: 'Bearer')]
    public function fetchUserRewardsAction(User $user)
    {
        // ...
    }
}

The normal PHPDoc block on the controller method is used for the summary and description.

Tip

Examples of using the annotations can be found in SwaggerPHP examples. However, unlike in those examples, when using this bundle you don't need to specify paths and you can easily document models as well as some other properties described below as they can be automatically be documented using the Symfony integration.

Tip

NelmioApiDocBundle understands symfony's controller attributes. Using these attributes inside your controller allows this bundle to automatically create the necessary documentation. More information can be found here: symfony_attributes.

Use Models

As shown in the example above, the bundle provides the @Model annotation. Use it instead of a definition reference and the bundle will deduce your model properties.

Note

A model can be a Symfony form type, a Doctrine ORM entity or a general PHP object.

This annotation has two options:

  • type to specify your model's type:
/**
 * @OA\Response(
 *     response=200,
 *     @Model(type=User::class)
 * )
 */
#[OA\Response(
    response: 200,
    description: 'Successful response',
    content: new Model(type: User::class)
)]
  • groups to specify the serialization groups used to (de)serialize your model:
/**
* @OA\Response(
*     response=200,
*     @Model(type=User::class, groups={"non_sensitive_data"})
* )
*/
#[OA\Response(
    response: 200,
    description: 'Successful response',
    content: new Model(type: User::class, groups: ['non_sensitive_data'])
)]
  • groups may also be used to specify the constraint validation groups used (de)serialize your model, but this must be enabled in configuration:
nelmio_api_doc:
    use_validation_groups: true
    # ...

With this enabled, groups set in the model will apply to both serializer properties and validator constraints. Take the model class below:

use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

class UserDto
{
    /**
     * @Groups({"default", "create", "update"})
     * @Assert\NotBlank(groups={"default", "create"})
     */
    public string $username;
}
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

class UserDto
{
     #[Groups(["default", "create", "update"])]
     #[Assert\NotBlank(groups: ["default", "create"])]
    public string $username;
}

The NotBlank constraint will apply only to the default and create group, but not update. In more practical terms: the username property would show as required for both model create and default, but not update. When using code generators to build API clients, this often translates into client side validation and types. NotBlank adding required will cause that property type to not be nullable, for example.

use OpenApi\Annotations as OA;

 /**
  * shows `username` as `required` in the OpenAPI schema (not nullable)
  * @OA\Response(
  *     response=200,
  *     @Model(type=UserDto::class, groups={"default"})
  * )
  */

 /**
  * Similarly, this will make the username `required` in the create
  * schema
  * @OA\RequestBody(@Model(type=UserDto::class, groups={"create"}))
  */

 /**
  * But for updates, the `username` property will not be required
  * @OA\RequestBody(@Model(type=UserDto::class, groups={"update"}))
  */
use OpenApi\Attributes as OA;

// shows `username` as `required` in the OpenAPI schema (not nullable)
#[OA\Response(response: 200, content: new Model(type: UserDto::class, groups: ["default"]))]

// Similarly, this will make the username `required` in the create  schema
#[OA\RequestBody(new Model(type: UserDto::class, groups: ["create"]))]

// But for updates, the `username` property will not be required
#[OA\RequestBody(new Model(type: UserDto::class, groups: ["update"]))]

Tip

When used at the root of @OA\Response and @OA\Parameter, @Model is automatically nested in a @OA\Schema.

The media type defaults to application/json.

To use @Model directly within a @OA\Schema, @OA\Items or @OA\Property, you have to use the $ref field:

/**
 * @OA\Response(
 *     @OA\JsonContent(ref=@Model(type=User::class))
 * )
 *
 * or
 *
 * @OA\Response(@OA\XmlContent(
 *     @OA\Schema(type="object",
 *         @OA\Property(property="foo", ref=@Model(type=FooClass::class))
 *     )
 * ))
 */
#[OA\Response(
    content: new OA\JsonContent(ref: new Model(type: User::class))
)]
 /**
  * or
  */
#[OA\Response(
    content: new OA\XmlContent(example: new OA\Schema(
        type: 'object',
        properties: [
            new OA\Property(property: 'foo', ref: new Model(type: FooClass::class))
        ]
    ))
)]

Symfony Form types

You can customize the documentation of a form field using the documentation option:

$builder->add('username', TextType::class, [
    'documentation' => [
        'type' => 'string', // would have been automatically detected in this case
        'description' => 'Your username.',
    ],
]);

See the OpenAPI 3.0 specification to see all the available fields of the documentation option (it accepts the same fields as the OpenApi Property object).

General PHP objects

Tip

If you're not using the JMS Serializer, the Symfony PropertyInfo component is used to describe your models. It supports doctrine annotations, type hints, and even PHP doc blocks. It does also support serialization groups when using the Symfony serializer.

If you're using the JMS Serializer, the metadata of the JMS serializer are used by default to describe your models. Additional information is extracted from the PHP doc block comment, but the property types must be specified in the JMS annotations.

NOTE: If you are using serialization contexts (e.g. Groups) each permutation will be treated as a separate Path. For example if you have the following two variations defined in different places in your code:

/**
 * A nested serializer property with no context group
 *
 * @JMS\VirtualProperty
 * @JMS\Type("ArrayCollection<App\Response\ItemResponse>")
 * @JMS\Since("1.0")
 *
 * @return Collection|ItemResponse[]
 */
public function getItems(): Collection|array
{
    return $this->items;
}
@OA\Schema(ref=@Model(type="App\Response\ItemResponse", groups=["Default"])),
#[OA\Schema(ref: new Model(type: App\Response\ItemResponse::class, groups: ['Default']))]

It will generate two different component schemas (ItemResponse, ItemResponse2), even though Default and blank are the same. This is by design.

In case you prefer using the Symfony PropertyInfo component (you won't be able to use JMS serialization groups), you can disable JMS serializer support in your config:

nelmio_api_doc:
    models: { use_jms: false }

When using the JMS serializer combined with willdurand/Hateoas (and the BazingaHateoasBundle), HATEOAS metadata are automatically extracted

If you want to customize the documentation of an object's property, you can use @OA\Property:

.. configuration-block::
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Annotations as OA;

class User
{
    /**
     * @var int
     * @OA\Property(description="The unique identifier of the user.")
     */
    public $id;

    /**
     * @OA\Property(type="string", maxLength=255)
     */
    public $username;

    /**
     * @OA\Property(ref=@Model(type=User::class))
     */
    public $friend;

    /**
     * @OA\Property(description="This is my coworker!")
     */
    public setCoworker(User $coworker) {
        // ...
    }
}
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Attributes as OA;

class User
{
    /**
     * @var int
     */
    #[OA\Property(description: 'The unique identifier of the user.')]
    public $id;

    #[OA\Property(type: 'string', maxLength: 255)]
    public $username;

    #[OA\Property(ref: new Model(type: User::class))]
    public $friend;

    #[OA\Property(description: 'This is my coworker!')]
    public setCoworker(User $coworker) {
        // ...
    }
}

See the OpenAPI 3.0 specification to see all the available fields of @OA\Property.

Learn more

If you need more complex features, take a look at:

areas alternative_names customization commands faq security symfony_attributes