To customize which fields appear in Sylius API responses, follow these steps for adding, removing, or renaming fields.
How to add an existing field to a response?
To add an existing field, such as createdAt, to the response of a ProductVariant in the shop context, we need to define a serialization group for it in a configuration file.
By adding createdAt with the group sylius:shop:product_variant:show, this field is now part of the response at the /shop/product-variants/{code} endpoint.
Finding the Serialization Group for an Endpoint
If you need to find the serialization group for an endpoint you want to modify, check the configuration files in %kernel.project_dir%/vendor/sylius/sylius/src/Sylius/Bundle/ApiBundle/Resources/config/api_platform/resources.
The group names generally follow this pattern: <context>:<resource>:<operation>, reflecting the endpoint's usage context, resource name, and operation type.
How to add a custom field to a response?
To add a new field, like additionalText, to a Customer response, create a custom serializer called CustomerNormalizer. This serializer will add extra data to the response.
<?php// src/Serializer/CustomerNormalizer.phpdeclare(strict_types=1);namespaceApp\Serializer;useSylius\Component\Core\Model\CustomerInterface;useSymfony\Component\Serializer\Normalizer\NormalizerAwareInterface;useSymfony\Component\Serializer\Normalizer\NormalizerAwareTrait;useSymfony\Component\Serializer\Normalizer\NormalizerInterface;useWebmozart\Assert\Assert;finalclassCustomerNormalizerimplementsNormalizerInterface,NormalizerAwareInterface{useNormalizerAwareTrait;privateconst ALREADY_CALLED ='customer_normalizer_already_called';/** * @paramCustomerInterface $object * @paramarray<string, mixed> $context * * @returnarray<string, mixed> */publicfunctionnormalize(mixed $object,?string $format =null,array $context = []):array {Assert::isInstanceOf($object,CustomerInterface::class);Assert::keyNotExists($context,self::ALREADY_CALLED); $context[self::ALREADY_CALLED] =true; $data =$this->normalizer->normalize($object, $format, $context); $data['additionalText'] ='Custom text or logic that will be added to this field.';return $data; }/** @paramarray<string, mixed> $context */publicfunctionsupportsNormalization(mixed $data,?string $format =null,array $context = []):bool {if (isset($context[self::ALREADY_CALLED])) {returnfalse; }return $data instanceofCustomerInterface; }publicfunctiongetSupportedTypes(?string $format):array {return [CustomerInterface::class=>false]; }}
Then, register this serializer in config/services.yaml:
This serializer class checks if the object being normalized is an instance of CustomerInterface. If it is, it adds the additionalText field to the response, along with any other existing fields.
{//..."id":123,"email":"[email protected]","additionalText":"Custom text or logic that will be added to this field.",//...}
How to add a custom field to a response for Specific Contexts Only (e.g., Shop)?
If you want the custom field to appear only in a specific context, such as in the shop API responses, extend the serializer to check for the current section and serialization groups.
Modify the Serializer for Context-Specific Use
Update the serializer to include the SectionProviderInterface and restrict the added field to the ShopApiSection context.
<?php// src/Serializer/CustomerNormalizer.phpdeclare(strict_types=1);namespaceApp\Serializer;useSylius\Bundle\ApiBundle\SectionResolver\ShopApiSection;useSylius\Bundle\ApiBundle\Serializer\SerializationGroupsSupportTrait;useSylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;useSylius\Component\Core\Model\CustomerInterface;useSymfony\Component\Serializer\Normalizer\NormalizerAwareInterface;useSymfony\Component\Serializer\Normalizer\NormalizerAwareTrait;useSymfony\Component\Serializer\Normalizer\NormalizerInterface;useWebmozart\Assert\Assert;finalclassCustomerNormalizerimplementsNormalizerInterface,NormalizerAwareInterface{useNormalizerAwareTrait;useSerializationGroupsSupportTrait;publicfunction__construct(privatereadonlySectionProviderInterface $sectionProvider,privatereadonlyarray $serializationGroups, ) { }privateconst ALREADY_CALLED ='customer_normalizer_already_called';/** * @paramCustomerInterface $object * @paramarray<string, mixed> $context * * @returnarray<string, mixed> */publicfunctionnormalize(mixed $object,?string $format =null,array $context = []):array {Assert::isInstanceOf($object,CustomerInterface::class);Assert::keyNotExists($context,self::ALREADY_CALLED);Assert::isInstanceOf($this->sectionProvider->getSection(),ShopApiSection::class);Assert::true($this->supportsSerializationGroups($context,$this->serializationGroups)); $context[self::ALREADY_CALLED] =true; $data =$this->normalizer->normalize($object, $format, $context); $data['additionalText'] ='Custom text or logic that will be added to this field.';return $data; }/** @paramarray<string, mixed> $context */publicfunctionsupportsNormalization(mixed $data,?string $format =null,array $context = []):bool {if (isset($context[self::ALREADY_CALLED])) {returnfalse; }return $data instanceofCustomerInterface&&$this->sectionProvider->getSection()instanceofShopApiSection&&$this->supportsSerializationGroups($context,$this->serializationGroups) ; }publicfunctiongetSupportedTypes(?string $format):array {return [CustomerInterface::class=>false]; }}
Register the Service for the Specific Context
Specify that this service applies only in the ShopApiSection and for the sylius:shop:customer:show serialization group.
Unfortunately, we cannot use more than one normalizer per resource, so if you need to add a serializer for a resource that already has one defined, remember to overwrite the base one or define your own with a higher priority.
How to remove a field from a response
Removing a field from a response can be challenging because Symfony combines all serialization group configurations for a resource. The simplest solution is to create a new serialization group that includes only the fields you want in the response, then assign this group to the desired endpoint. This way, fields that are not part of the new group are excluded from the response.
Let’s assume that the Product resource returns such a response:
Define a new serialization group specifically for the fields you want to expose. In this example, we’ll use the GET endpoint for the Product resource in the shop API, assigning a new normalization group named shop:product:custom_show.
Here, we define a GET operation for the Product resource in the shop context with a custom serialization group, shop:product:custom_show. This limits the response to fields that are part of this group.
Define the Fields to Include in the New Group
In the config/serialization directory, specify only the fields you want to include in the shop:product:custom_show group. Fields that are not assigned to this group (such as translations in this example) will be excluded from the response.
<!-- config/serialization/Product.xml --><!--...--><attributename="updatedAt"> <group>shop:product:custom_show</group></attribute><!-- here `translation` attribute would be declared --><attributename="mainTaxon"> <group>shop:product:custom_show</group></attribute><!--...-->
# config/serialization/Product.yamlSylius\Component\Core\Model\Product:attributes:# ...updatedAt:groups: ['shop:product:custom_read']# here `translation` attribute would be declaredmainTaxon:groups: ['shop:product:custom_read']# ...
With this setup, only fields assigned to the shop:product:custom_show group (like updatedAt and mainTaxon in this example) will appear in the API response. The translations field and any other fields not included in shop:product:custom_show are excluded.
The same result could be also achieved by using a custom serializer for the given resource.
How to rename a field of a response
To rename a field in the response, such as changing options to optionValues, update the serialization configuration file to use a serialized-name attribute.
Set a Custom Serialized Name
The simplest method to achieve this is to modify the serialization configuration file that we’ve already created. Let’s add to the config/serialization/Product.xml file config for options with a serialized-name attribute description: