API Platform 3.3
API Platform 3.3 is out and adds nice improvements once again! Many new options added to the Metadata classes, an increased focus on RDF at the heart of API design and a target to make API Platform more Laravel friendly.
Many thanks to every contributor that made API Platform more pleasant to use! Don’t miss our API Platform Conference, we completed the program last week, it’s huge! As the last early bird tickets were sold in a few days ❤️ we added some more to celebrate the release!
This blog post relates the important changes, for a more detail look read our changelog.
# Formats
We decided to deprecated not setting the formats
option on API Platform. We are convinced that when using API Platform you should use an hypermedia format such as JSON-LD or JSON:API and we don’t recommend plain JSON.
# Metadata
Metadata attributes (ApiResource and HTTP Verbs) have a new headers
option that allows setting up HTTP Response headers:
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
#[ApiResource(
headers: ['Location' => '/foobar', 'Hello' => 'World'],
status: 301,
output: false,
operations: [
new Get(uriTemplate: '/redirect_to_foobar'),
],
)]
The Link
option has a security
option allowing to bind values represented by the toProperty
or fromProperty
:
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Link;
#[Get(
uriTemplate: '/employees/{employeeId}/company',
uriVariables: [
'employeeId' => new Link(
fromClass: Employee::class,
toProperty: 'company',
security: "is_granted(some_voter, company)"
),
],
)]
We also improved the security context on ApiProperty
by adding the property name in its context.
If our automatic linking system isn’t matching your needs, you can hook a service that handles links. Its interface is provider-specific and can be faster to set up than a provider.
Last but not least, there’s a new getOperation
Expression Language function that can be used on Mercure topics:
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\UrlGeneratorInterface;
#[ApiResource(
// ...
mercure: [
'topics' => [
'@=iri(object)',
'@=iri(
object,
'.UrlGeneratorInterface::ABS_URL.',
get_operation(object, "/foos/bars/{id}{._format}")
)'
],
]
)]
This improves a lot IRI generation as you can choose the operation you want the IRI from.
# Parameters
A new attribute is introduced named Parameter. It has two variants: a QueryParameter and a HeaderParameter. This allows more flexibility regarding how filters are called on API Platform resources.
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\QueryParameter;
#[GetCollection(
uriTemplate: 'orders',
parameters: [
'order[:property]' => new QueryParameter(filter: 'offer.order_filter'),
]
)
class Offer {
public string $id;
public string $name;
}
Read about all the possibilities of our parameters on the documentation. Note that validation on parameters is using the Symfony Validator, you can either bind constraints or specify a documentation (openapi, hydra, json schema) and it will automatically add constraints according to your input.
# Cache
API Platform has an invalidation mechanism based on IRIs that collects errors automatically during serialization. When you have specific needs, it wasn’t easy to specify what iris you want to invalidate. To do so we introduced a new TagCollectorInterface to help collecting specific IRIs:
use ApiPlatform\Serializer\TagCollectorInterface;
final class TagCollectorDefault implements TagCollectorInterface
{
public function collect(array $context = []): void
{
if (isset($context['property_metadata'])) {
return;
}
$iri = $context['iri'];
if (isset($context['resources']) && isset($iri)) {
$context['resources'][$iri] = $iri;
}
}
}
We recommend to read the code implementation if you want more details.
# Documentation
# Hydra
We now read hydra:property
if it exists in the jsonLdContext
and use its value directly in the documentation:
use ApiPlatform\Metadata\ApiProperty;
#[ApiProperty(jsonLdContext: [
'hydra:property' => [
'rdfs:label' => 'change this value',
'owl:maxCardinality' => 5
]
]);
Previously, only @type
was read.
# OpenAPI
A new option is available to control whether API Platform adds OpenAPI responses automatically or not, you can either use the global configuration api_platform.openapi.overrideResponses
(true by default) or specify an extra property on your operations:
use ApiPlatform\Metadata\Post;
use ApiPlatform\OpenApi\OpenApiFactory;
use ApiPlatform\OpenApi\Model as OpenApi;
#[Post(
uriTemplate: '/override_open_api_responses',
openapi: new OpenApi\Operation(
responses: [
'204' => new OpenApi\Response(
description: 'User activated',
),
]
),
extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false],
)]
No other responses will be added by API Platform, don’t forget to add error responses.
# Deprecations
These namespaces are deprecated:
ApiPlatform\Api
ApiPlatform\Exception
ApiPlatform\Problem
ApiPlatform\Action
ApiPlatform\Util
Public classes are now in the Metadata
namespace.
# To listen or not to listen
Following on from the work done on supporting Laravel, we increased our focus on splitting API Platform into 18 components:
api-platform/openapi
api-platform/json-schema
api-platform/ramsey-uuid
api-platform/jsonld
api-platform/metadata
api-platform/http-cache
api-platform/elasticsearch
api-platform/parameter-validator
api-platform/hydra
api-platform/doctrine-odm
api-platform/doctrine-common
api-platform/doctrine-orm
api-platform/validator
api-platform/graphql
api-platform/serializer
api-platform/documentation
api-platform/symfony
api-platform/state
You can install api-platfom/json-schema
or api-platform/openapi
without the whole framework if you only need that subset of functionnalities.
On top of that, HTTP Kernel listeners are Symfony specific and as our Provider/Processor mechanism is quite powerful, we introduce a way to remove listeners alltogether in favor of only using providers. This is great as we will share more code between all the subsystems (Laravel, GraphQl, etc.).
Event listeners are not deprecated but if you want to use them, or you need Symfony controllers, you’ll need to enable listeners using:
api_platform:
use_symfony_listeners: true
This adds back the listeners. Under the hood the ReadListener
only calls the ReadProvider
, so decorating the provider will still work. This is valid for every events, each event has a provider using the same name.
# Fixes
Many fixes were brought to this minor version to improve the developer experience, such as not serializing the Request object in the Messenger context stamp, or updating the MissingConstructorArgumentsException message to make it more informational.