soyuka.me ← Index
api

API Platform 4.4 and 5.0 - Are APIs Still Relevant in the AI Era?


API Platform 4.4 and 5.0 are out!#

We released both versions live on stage at the API Platform Conference in Lille. Here are the slides of my talk, Are APIs still relevant in the AI era?:

The year has been busy on api-platform/core: between January 1st and September 13th we opened 508 pull requests and merged 429 of them, that’s +76% opened and +77% merged compared to 2025. 468 commits landed since 4.3.0 alone. Coding got faster for everyone, review did not, and my review standard on correctness, backward compatibility and maintenance stays exactly the same. Thanks to everyone who contributed!

Are APIs still relevant in the AI era?#

Yes. The interface changes, the API remains.

The first half of the talk is a demo of Mon parc auto, a small garage application where an agent talks to an API Platform backend through MCP: plate lookup, compatible parts from TecDoc, fluid specifications, maintenance history. The same #[ApiResource] serves an HTTP collection and an MCP tool, the input DTO becomes the tool’s JSON Schema, and the business rules, the permissions and the data stay where they always were.

Three ways to expose an existing API to an agent, none of them free:

  1. OpenAPI. It exposes HTTP operations, one tool per operation. On my demo API, @ivotoby/openapi-mcp-server declared 57 tools for 10 018 tokens of context. The agent pays that bill on every turn.
  2. A hypermedia gateway. Discovery starts at the entrypoint and tools are loaded as the agent navigates, @context and /docs.jsonld describing types and operations, notifications/tools/list_changed telling the client to refresh. That’s coopTilleuls/hydra-mcp-bridge, and the reasoning behind it is in the paper Kévin (@dunglas) and I wrote, APIs We Built Are Meant for Computers (HAL-05630480).
  3. Manual tools. new McpTool(description: ..., input: ..., processor: ...), one tool per task instead of one per operation, with a Prompt telling the agent how to chain them. More work, much better results.

Note that list_changed needs a persistent SSE channel, and PHP is not fitted for long running connections. FrankenPHP embeds Mercure, so MCP SSE may run directly through it. There’s a prototype, feedbacks welcome.

4.4 · Migrate your filters#

This is the big one. The legacy #[ApiFilter] filters are deprecated in 4.4, removal is planned for 6.0:

// Deprecated in 4.4
#[ApiFilter(SearchFilter::class, properties: ['name' => 'partial'])]

One parameter, one filter:

#[ApiResource(parameters: [
    'name' => new QueryParameter(filter: new PartialSearchFilter()),
])]

?name=api gives you WHERE name LIKE %api%. You don’t have to rewrite them by hand, there’s a codemod:

php bin/console api:upgrade-filter

New search filters, for Doctrine ORM and MongoDB ODM: StartSearchFilter (title LIKE 'api%'), EndSearchFilter (title LIKE '%platform') and WordStartSearchFilter (title LIKE 'api%' OR title LIKE '% api%').

Filters compose now. ComparisonFilter wraps another filter and adds the operators:

new QueryParameter(
    property: 'quantity',
    filter: new ComparisonFilter(new ExactFilter()),
)

?quantity[gte]=10 and ?quantity[lt]=20. OrFilter and FreeTextQueryFilter map one parameter to several properties:

'q' => new QueryParameter(
    filter: new FreeTextQueryFilter([
        'title' => new OrFilter(new PartialSearchFilter()),
        'isbn' => new OrFilter(new ExactFilter()),
    ]),
)

?q=978 becomes WHERE LOWER(title) LIKE '%978%' OR isbn = '978'. And ChainFilter applies several filters to the same parameter:

'code' => new QueryParameter(
    filter: new ChainFilter([new StartSearchFilter(), new EndSearchFilter()]),
)

4.4 · Doctrine repository methods#

You can point an operation at a repository method and keep filtering and pagination:

#[GetCollection(stateOptions: new Options(repositoryMethod: 'forPublicApi'))]
public function forPublicApi(): QueryBuilder
{
    return $this->createQueryBuilder('v')
        ->andWhere('v.isPublic = :public')
        ->setParameter('public', true);
}

4.4 · Decide when missing means 404#

When a provider returns null, you now decide per operation: true stops with a 404, false lets the processor handle it.

#[Post(uriTemplate: '/feeders/{id}/feed', read: true, throwOnNotFound: true)]

4.4 · HTTP QUERY#

RFC 10008 is supported, an idempotent method that carries the filters in the request body, JSON or form urlencoded:

QUERY /books
Content-Type: application/json

{"name": "api"}

In 5.0 QueryParameter reads the criteria from that body:

new Query(parameters: [
    'name' => new QueryParameter(filter: new PartialSearchFilter()),
])

And because it’s a body, QUERY also works as a command, with an input DTO and a processor:

new Query(
    input: SearchInput::class,
    read: false,
    deserialize: true,
    write: true,
    processor: Search::class,
)

5.0 · JSON:API identifiers#

The id member was an IRI, which is not what the specification says. Opt in during 4.4, it’s the default in 5.0:

api_platform:
    jsonapi: { use_iri_as_id: false }
{"data":{"id":"10","type":"Book","links":{"self":"/books/10"}}}

More features#

  • Documentation: OpenAPI 3.2, Scalar API Reference, withCredentials on Swagger UI.
  • JSON-LD / Hydra: resource level prefixes with jsonldContext, hydra:memberAssertion instead of owl:equivalentClass.
  • HTTP: Parameter attributes on properties, routePriority to control route matching order, container parameters resolved in YAML, XML and attributes.
  • Metadata: ApiTestCase moved to its own api-platform/test package, charset emitted only for the media types that define it.
  • Symfony ^7.4 || ^8.0 on every component, 6.4 is dropped.

5.0 removes the configuration we deprecated earlier: validator.query_parameter_validation, enable_link_security and resource_class_directories. Legacy filter removals are planned for 6.0, so you have time.

Upgrade#

Update to 4.4, fix the deprecations, then update to 5.0:

composer update api-platform/symfony:^4.4
php bin/console api:upgrade-filter

The upgrade guide has the details. If you’re on an agent, this prompt works surprisingly well:

follow the upgrade guide at https://api-platform.com/docs/core/upgrade-guide/ and update API Platform to 4.4, fix deprecations, then to 5.0

There’s also a new installer, if you’d rather start fresh:

curl -fsSL https://api-platform.com/install.sh | sh
api-platform my-api --framework=symfony --with-docker --with-pwa

A major every year#

We’re moving from a major every two years to a major every year. The maintenance policy doesn’t change: bug fixes on stable, security fixes on old-stable. A mature foundation, faster development.

Follow us!#

https://x.com/ApiPlatform https://fosstodon.org/@ApiPlatform

https://phpc.social/@soyuka https://x.com/s0yuka/

And if you want to send your love:

Sponsor me on Github!

comments powered by Disqus