The international conference on the API Platform Framework
The API Platform Conference 2024 is happening soon, and it's close to selling out.
API Platform 4, Caddy web server, Xdebug, AI... Enjoy two days of inspiring talks with our friendly community and our amazing speakers.
API Platform natively support the OpenAPI API specification format.
The specification of the API is available at the /docs.json
path.
By default, OpenAPI v3 is used.
You can also get an OpenAPI v3-compliant version thanks to the spec_version
query parameter: /docs.json?spec_version=3
It also integrates a customized version of Swagger UI and ReDoc, some nice tools to display the API documentation in a user friendly way.
You can also dump an OpenAPI specification for your API.
OpenAPI, JSON format:
docker-compose exec php \
bin/console api:openapi:export
OpenAPI, YAML format:
docker-compose exec php \
bin/console api:openapi:export --yaml
Create a file containing the specification:
docker-compose exec php \
bin/console api:openapi:export --output=swagger_docs.json
If you want to use the old OpenAPI v2 (Swagger) JSON format, use:
docker-compose exec php \
bin/console api:swagger:export
Symfony allows to decorate services, here we
need to decorate api_platform.openapi.factory
.
In the following example, we will see how to override the title of the Swagger documentation and add a custom filter for
the GET
operation of /foos
path.
# api/config/services.yaml
App\OpenApi\OpenApiFactory:
decorates: 'api_platform.openapi.factory'
arguments: [ '@App\OpenApi\OpenApiFactory.inner' ]
autoconfigure: false
<?php
namespace App\OpenApi;
use ApiPlatform\Core\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\Core\OpenApi\OpenApi;
use ApiPlatform\Core\OpenApi\Model;
class OpenApiFactory implements OpenApiFactoryInterface
{
private $decorated;
public function __construct(OpenApiFactoryInterface $decorated)
{
$this->decorated = $decorated;
}
public function __invoke(array $context = []): OpenApi
{
$openApi = $this->decorated->__invoke($context);
$pathItem = $openApi->getPaths()->getPath('/api/grumpy_pizzas/{id}');
$operation = $pathItem->getGet();
$openApi->getPaths()->addPath('/api/grumpy_pizzas/{id}', $pathItem->withGet(
$operation->withParameters(array_merge(
$operation->getParameters(),
[new Model\Parameter('fields', 'query', 'Fields to remove of the output')]
))
));
$openApi = $openApi->withInfo((new Model\Info('New Title', 'v2', 'Description of my custom API'))->withExtensionProperty('info-key', 'Info value'));
$openApi = $openApi->withExtensionProperty('key', 'Custom x-key value');
$openApi = $openApi->withExtensionProperty('x-value', 'Custom x-value value');
return $openApi;
}
}
The impact on the swagger-ui is the following:
Sometimes you may want to change the information included in your OpenAPI documentation. The following configuration will give you total control over your OpenAPI definitions:
<?php
// api/src/Entity/Product.php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
#[ApiResource]
class Product // The class name will be used to name exposed resources
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
private ?int $id = null;
/**
* @param string $name A name property - this description will be available in the API documentation too.
*
*/
#[ORM\Column]
#[Assert\NotBlank]
#[ApiProperty(
attributes: [
"openapi_context" => [
"type" => "string",
"enum" => ["one", "two"],
"example" => "one",
],
],
)]
public string $name;
#[ORM\Column(type: "datetime")]
#[Assert\DateTime]
#[ApiProperty(
openapi_context: ["type" => "string", "format" => "date-time"]
)]
public $timestamp;
// ...
}
# api/config/api_platform/resources.yaml
resources:
App\Entity\Product:
properties:
name:
attributes:
openapi_context:
type: string
enum: ['one', 'two']
example: one
timestamp:
attributes:
openapi_context:
type: string
format: date-time
<?xml version="1.0" encoding="UTF-8" ?>
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata https://api-platform.com/schema/metadata/metadata-2.0.xsd">
<resource class="App\Entity\Product">
<property name="name">
<attribute name="openapi_context">
<attribute name="type">type</attribute>
<attribute name="enum">
<attribute>one</attribute>
<attribute>two</attribute>
</attribute>
<attribute name="example">one</attribute>
</attribute>
</property>
<property name="timestamp">
<attribute name="openapi_context">
<attribute name="type">string</attribute>
<attribute name="format">date-time</attribute>
</attribute>
</property>
</resource>
</resources>
This will produce the following Swagger documentation:
"components": {
"schemas": {
"GrumpyPizza:jsonld": {
"type": "object",
"description": "",
"properties": {
"@context": {
"readOnly": true,
"type": "string"
},
"@id": {
"readOnly": true,
"type": "string"
},
"@type": {
"readOnly": true,
"type": "string"
},
"timestamp": {
"type": "string",
"format": "date-time"
},
"id": {
"readOnly": true,
"type": "integer"
},
"name": {
"type": "string",
"enum": [
"one",
"two"
],
"example": "one"
}
}
}
}
}
To pass a context to the OpenAPI v2 generator, use the swagger_context
attribute (notice the prefix: swagger_
instead of openapi_
).
API Platform generates a definition name based on the serializer groups
defined
in the (de
)normalization_context
. It’s possible to override the name
thanks to the swagger_definition_name
option:
#[ApiResource(
collectionOperations: [
"post" => [
"denormalization_context" => [
"groups" => ["user:read"],
"swagger_definition_name" => "Read",
],
],
],
)]
class User
{
}
It’s also possible to re-use the (de
)normalization_context
:
#[ApiResource(
collectionOperations: [
"post" => [
"denormalization_context" => User::API_WRITE,
],
],
)]
class User
{
const API_WRITE = [
'groups' => ['user:read'],
'swagger_definition_name' => 'Read',
];
}
You also have full control over both built-in and custom operations documentation.
<?php
// api/src/Entity/Rabbit.php
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Controller\RandomRabbit;
#[ApiResource(collectionOperations: [
'create_rabbit' => [
'method' => 'post',
'path' => '/rabbit/create',
'controller' => RandomRabbit::class,
'openapi_context' => [
'summary' => 'Create a rabbit picture',
'description' => "# Pop a great rabbit picture by color!\n\n![A great rabbit](https://rabbit.org/graphics/fun/netbunnies/jellybean1-brennan1.jpg)",
'requestBody' => [
'content' => [
'application/json' => [
'schema' => [
'type' => 'object',
'properties' =>
[
'name' => ['type' => 'string'],
'description' => ['type' => 'string'],
],
],
'example' => [
'name' => 'Mr. Rabbit',
'description' => 'Pink Rabbit',
],
],
],
],
],
],
]
)]
class Rabbit
{}
resources:
App\Entity\Rabbit:
collectionOperations:
create_rabbit:
method: post
path: '/rabbit/create'
controller: App\Controller\RandomRabbit
openapi_context:
summary: Random rabbit picture
description: >
# Pop a great rabbit picture by color!
![A great rabbit](https://rabbit.org/graphics/fun/netbunnies/jellybean1-brennan1.jpg)
requestBody:
content:
application/json:
schema:
type: object
properties:
name: { type: string }
description: { type: string }
example:
name: Mr. Rabbit
description: Pink rabbit
<?xml version="1.0" encoding="UTF-8" ?>
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata
https://api-platform.com/schema/metadata/metadata-2.0.xsd">
<resource class="App\Entity\Rabbit">
<collectionOperations>
<collectionOperation name="create_rabbit">
<attribute name="path">/rabbit/create</attribute>
<attribute name="method">post</attribute>
<attribute name="controller">App\Controller\RandomRabbit</attribute>
<attribute name="openapi_context">
<attribute name="summary">Create a rabbit picture </attribute>
<attribute name="description"># Pop a great rabbit picture by color!!
![A great rabbit](https://rabbit.org/graphics/fun/netbunnies/jellybean1-brennan1.jpg)</attribute>
<attribute name="content">
<attribute name="application/json">
<attribute name="schema">
<attribute name="type">object</attribute>
<attribute name="properties">
<attribute name="name">
<attribute name="type">string</attribute>
</attribute>
<attribute name="description">
<attribute name="type">string</attribute>
</attribute>
</attribute>
</attribute>
</attribute>
</attribute>
</attribute>
</collectionOperation>
</collectionOperations>
</resource>
</resources>
Sometimes you may want to have the API at one location, and the Swagger UI at a different location. This can be done by disabling the Swagger UI from the API Platform configuration file and manually adding the Swagger UI controller.
# api/config/packages/api_platform.yaml
api_platform:
# ...
enable_swagger_ui: false
enable_re_doc: false
# app/config/routes.yaml
swagger_ui:
path: /docs
controller: api_platform.swagger.action.ui
Change /docs
to the URI you wish Swagger to be accessible on.
Sometimes you may want to use a different Asset Package for the Swagger UI. In this way you’ll have more fine-grained control over the asset URL generations. This is useful i.e. if you want to use different base path, base URL or asset versioning strategy.
Specify a custom asset package name:
# config/packages/api_platform.yaml
api_platform:
asset_package: 'api_platform'
Set or override asset properties per package:
# config/packages/framework.yaml
framework:
# ...
assets:
base_path: '/custom_base_path' # the default
packages:
api_platform:
base_path: '/'
As described in the Symfony documentation, it’s possible to override the Twig template that loads Swagger UI and renders the documentation:
{# templates/bundles/ApiPlatformBundle/SwaggerUi/index.html.twig #}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% if title %}{{ title }} {% endif %}My custom template</title>
{# ... #}
</html>
You may want to copy the one shipped with API Platform and customize it.
AWS API Gateway supports OpenAPI partially, but it requires some changes. API Platform provides a way to be compatible with Amazon API Gateway.
To enable API Gateway compatibility on your OpenAPI docs, add api_gateway=true
as query parameter: http://www.example.com/docs.json?api_gateway=true
.
The flag --api-gateway
is also available through the command-line.
If you implemented OAuth on your API, you should configure OpenApi’s authorization using API Platform’s configuration:
api_platform:
oauth:
# To enable or disable oauth.
enabled: false
# The oauth client id.
clientId: ''
# The oauth client secret.
clientSecret: ''
# The oauth type.
type: 'oauth2'
# The oauth flow grant type.
flow: 'application'
# The oauth token url.
tokenUrl: '/oauth/v2/token'
# The oauth authentication url.
authorizationUrl: '/oauth/v2/auth'
# The oauth scopes.
scopes: []
Note that clientId
and clientSecret
are being used by the SwaggerUI if enabled.
The info object provides metadata about the API like licensing information or a contact. You can specify this information using API Platform’s configuration:
api_platform:
# The title of the API.
title: 'API title'
# The description of the API.
description: 'API description'
# The version of the API.
version: '0.0.0'
openapi:
# The contact information for the exposed API.
contact:
# The identifying name of the contact person/organization.
name:
# The URL pointing to the contact information. MUST be in the format of a URL.
url:
# The email address of the contact person/organization. MUST be in the format of an email address.
email:
# A URL to the Terms of Service for the API. MUST be in the format of a URL.
termsOfService:
# The license information for the exposed API.
license:
# The license name used for the API.
name:
# URL to the license used for the API. MUST be in the format of a URL.
url:
You can also help us improve the documentation of this page.
Made with love by
Les-Tilleuls.coop can help you design and develop your APIs and web projects, and train your teams in API Platform, Symfony, Next.js, Kubernetes and a wide range of other technologies.
Learn more