Api Platform conference
Register now
v2.5 Subresources
API Platform Conference
September 18-19, 2025 | Lille & online

The international conference on the API Platform Framework

Get ready for our special anniversary edition!

Lear more about the event, register for the conference, and get ready for two days of inspiration, ideas, and knowledge-sharing with our incredible lineup of renowned specialists and advocates.

This edition is shaping up to be our biggest yet — secure your seat now at the best price before we sell out!

Only a few tickets left!

Subresources

A subresource is a collection or an item that belongs to another resource. API Platform makes it easy to create such operations.

Subresources screencast
Watch the Subresources screencast

The starting point of a subresource must be a relation on an existing resource. For example, let’s create two entities (Question, Answer) and set up a subresource so that /question/42/answer gives us the answer to the question 42:

```php namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM;

/**

  • @ORM\Entity

  • @ApiResource / class Answer { /*

    • @ORM\Column(type=“integer”)
    • @ORM\Id
    • @ORM\GeneratedValue(strategy=“AUTO”) */ private $id;

    /**

    • @ORM\Column */ public $content;

    /**

    • @ORM\OneToOne(targetEntity=“Question”, mappedBy=“answer”) */ public $question;

    public function getId(): ?int { return $this->id; }

    // … }

// api/src/Entity/Question.php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource; use ApiPlatform\Core\Annotation\ApiSubresource; use Doctrine\ORM\Mapping as ORM;

/**

  • @ORM\Entity

  • @ApiResource / class Question { /*

    • @ORM\Column(type=“integer”)
    • @ORM\Id
    • @ORM\GeneratedValue(strategy=“AUTO”) */ private $id;

    /**

    • @ORM\Column */ public $content;

    /**

    • @ORM\OneToOne(targetEntity=“Answer”, inversedBy=“question”)
    • @ORM\JoinColumn(referencedColumnName=“id”, unique=true)
    • @ApiSubresource */ public $answer;

    public function getId(): ?int { return $this->id; }

    // … }


```yaml
# api/config/api_platform/resources.yaml
App\Entity\Answer: ~
App\Entity\Question:
    properties:
        answer:
            subresource:
                resourceClass: 'App\Entity\Answer'
                collection: false

Note that all we had to do is to set up @ApiSubresource on the Question::answer relation. Because the answer is a to-one relation, we know that this subresource is an item. Therefore the response will look like this:

{
  "@context": "/contexts/Answer",
  "@id": "/answers/42",
  "@type": "Answer",
  "id": 42,
  "content": "Life, the Universe, and Everything",
  "question": "/questions/42"
}

If you put the subresource on a relation that is to-many, you will retrieve a collection.

Last but not least, subresources can be nested, such that /questions/42/answer/comments will get the collection of comments for the answer to question 42.

Note: only for GET operations are supported at the moment

# Using Serialization Groups

You may want custom groups on subresources, you can set normalization_context or denormalization_context on that operation. To do so, add a subresourceOperations node. For example:

```php namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;

/**

  • @ApiResource(subresourceOperations={
  • "api_questions_answer_get_subresource"={
    
  •     "method"="GET",
    
  •     "normalization_context"={"groups"={"foobar"}}
    
  • }
    
  • }) */ class Answer { // … }

```yaml
# api/config/api_platform/resources.yaml
App\Entity\Answer:
    subresourceOperations:
        api_questions_answer_get_subresource:
            method: 'GET'
            normalization_context: {groups: ['foobar']}
<?xml version="1.0" encoding="UTF-8" ?>
<!-- api/config/api_platform/resources.xml -->

<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\Answer">
        <subresourceOperations>
            <subresourceOperation name="api_questions_answer_get_subresource">
                <attribute name="method">GET</attribute>
                <attribute name="normalization_context">
                  <attribute name="groups">
                    <attribute>foobar</attribute>
                  </attribute>
                </attribute>
            </subresourceOperation>
        </subresourceOperations>
    </resource>
</resources>

In the previous examples, the method attribute is mandatory, because the operation name doesn’t match a supported HTTP method.

Note that the operation name, here api_questions_answer_get_subresource, is the important keyword. It’ll be automatically set to $resources_$subresource(s)_get_subresource. To find the correct operation name you may use bin/console debug:router.

# Using Custom Paths

You can control the path of subresources with the path option of the subresourceOperations parameter:

<?php
// api/src/Entity/Question.php

/**
 * ...
 * @ApiResource(
 *      subresourceOperations={
 *          "api_questions_answer_get_subresource"={
 *              "method"="GET",
 *              "path"="/questions/{id}/all-answers"
 *          },
 *      },
 * )
 */
class Question
{
}

# Access Control of Subresources

The subresourceOperations attribute also allows you to add an access control on each path with the attribute security.

<?php
// api/src/Entity/Answer.php

/**
 * ...
 * @ApiResource(
 *     subresourceOperations={
 *          "api_questions_answer_get_subresource"= {
 *              "security"="has_role('ROLE_AUTHENTICATED')"
 *          }
 *      }
 * )
 */
 class Answer
 {
 }

# Limiting Depth

You can control depth of subresources with the parameter maxDepth. For example, if the Answer entity also has a subresource such as commentsand you don’t want the route api/questions/{id}/answers/{id}/comments to be generated. You can do this by adding the parameter maxDepth in the ApiSubresource annotation or YAML/XML file configuration.

<?php
// api/src/Entity/Question.php

use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiSubresource;

/**
 * ...
 * @ApiResource
 */
class Question
{
    /**
     * ...
     * @ApiSubresource(maxDepth=1)
     */
    public $answer;

    // ...
}

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

Copyright © 2023 Kévin Dunglas

Sponsored by Les-Tilleuls.coop