Native API Platform Resources

Edit on GitHub

This document explains how to create API Platform resources using native PHP attributes instead of Spryker’s YAML-based generation pipeline, and how to configure your project to discover these resources.

When to use native resources

Spryker’s YAML schema pipeline (resource.yml files) generates PHP resource classes automatically and provides features like multi-layer merging, validation auto-discovery, and CodeBucket support. For most use cases, the YAML approach is recommended.

Use native API Platform resources when you need features that are not yet supported by the Spryker SchemaParser and ClassGenerator, such as:

Creating a native resource class

A native API Platform resource is a PHP class annotated with #[ApiResource] and related attributes. You can place it in any directory that is configured as a mapping path.

Example resource

<?php

declare(strict_types=1);

namespace Pyz\Glue\Catalog\Api\Backend\Resource;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use Pyz\Glue\Catalog\Api\Backend\Provider\CatalogSearchBackendProvider;

#[ApiResource(
    operations: [
        new GetCollection(),
        new Get(),
    ],
    shortName: 'catalog-search',
    provider: CatalogSearchBackendProvider::class,
    paginationEnabled: true,
    paginationItemsPerPage: 20,
)]
class CatalogSearchBackendResource
{
    #[ApiProperty(identifier: true, writable: false)]
    public ?string $sku = null;

    #[ApiProperty]
    public ?string $name = null;

    #[ApiProperty]
    public ?float $price = null;

    #[ApiProperty(writable: false)]
    public ?string $url = null;
}

This follows the same provider/processor pattern as generated resources. For full attribute documentation, see the API Platform resource configuration reference.

Provider for the native resource

<?php

declare(strict_types=1);

namespace Pyz\Glue\Catalog\Api\Backend\Provider;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;

class CatalogSearchBackendProvider implements ProviderInterface
{
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        // Fetch data from your business layer and return resource objects
    }
}

Configuring custom resource paths

API Platform discovers resource classes from directories listed in the mapping.paths configuration. To make API Platform discover your native resources, add your directory to this list.

$apiPlatform->mapping()->paths([
    '%kernel.project_dir%/src/Generated/Api/Backend',
    '%kernel.project_dir%/src/Pyz/Glue/Catalog/Api/Backend/Resource',
]);

Adding a custom path

Edit the api_platform.php configuration file for the application where your resources should be available.

For GlueBackend resources, edit config/GlueBackend/packages/api_platform.php:

<?php

declare(strict_types=1);

use Symfony\Config\ApiPlatformConfig;

return static function (ApiPlatformConfig $apiPlatform, string $env): void {
    $apiPlatform->mapping()->paths([
        '%kernel.project_dir%/src/Generated/Api/Backend',
        '%kernel.project_dir%/src/Pyz/Glue/*/Api/Backend/Resource',
    ]);

    // ... rest of configuration
};

For GlueStorefront resources, edit config/GlueStorefront/packages/api_platform.php:

$apiPlatform->mapping()->paths([
    '%kernel.project_dir%/src/Generated/Api/Storefront',
    '%kernel.project_dir%/src/Pyz/Glue/*/Api/Storefront/Resource',
]);
Keep the generated path

Always keep the src/Generated/Api/{ApiType} path in the list. Removing it disables all YAML-generated resources.

Directory structure example

src/
├── Generated/
│   └── Api/
│       └── Backend/
│           └── CustomersBackendResource.php       # Generated from YAML
├── Pyz/
│   └── Glue/
│       └── Catalog/
│           └── Api/
│               └── Backend/
│                   ├── Resource/
│                   │   └── CatalogSearchBackendResource.php  # Native resource
│                   └── Provider/
│                       └── CatalogSearchBackendProvider.php

Both the generated and native resources are discovered and served by API Platform.

Coexistence with generated resources

Native resources and YAML-generated resources coexist without conflict. API Platform treats all discovered #[ApiResource] classes equally, regardless of whether they were generated or manually created.

Key points:

  • shortName conflicts: There is no CI validation for duplicate shortName values. When a native resource uses the same shortName as a generated resource, the resource from the last path listed in mapping.paths takes precedence and fully overwrites the earlier one. Because project paths are typically listed after the generated resource path, a project resource with a matching shortName replaces the generated resource entirely.
  • Same provider/processor pattern: Native resources use the same ProviderInterface and ProcessorInterface as generated resources.
  • Same serialization: Native resources use the same JSON:API (or other configured) format.
  • Same security model: Native resources can use the same security expressions. See Security.

Limitations of native resources

Native resources bypass the Spryker generation pipeline, which means the following features are not available:

Feature Available Alternative
Multi-layer schema merging (Core, Feature, Project) No Manage inheritance manually
Validation auto-discovery from .validation.yml No Use #[Assert\*] attributes directly on properties
CodeBucket support Partial Add the CODE_BUCKET constant manually (see below)
api:debug command output No Use glue debug:router instead
api:generate management No Manage files manually

CodeBucket support in native resources

Generated resources automatically include a CODE_BUCKET constant when a CodeBucket is configured in the schema. For native resources, add this constant manually to enable the same CodeBucket resolution mechanism:

#[ApiResource(
    operations: [
        new GetCollection(),
        new Get(),
    ],
    shortName: 'catalog-search',
    provider: CatalogSearchBackendProvider::class,
)]
class CatalogSearchEUBackendResource extends CatalogSearchBackendResource
{
    public const string CODE_BUCKET = 'EU';
}

The API Platform CodeBucket resolver uses the same logic for both generated and native resources: it checks for the CODE_BUCKET constant on the class and matches it against the current APPLICATION_CODE_BUCKET value. For more details, see Code Buckets.

Validation on native resources

Without the YAML validation pipeline, add Symfony Validator constraints directly as attributes:

use Symfony\Component\Validator\Constraints as Assert;

#[ApiResource(/* ... */)]
class CatalogSearchBackendResource
{
    #[ApiProperty(identifier: true)]
    public ?string $sku = null;

    #[Assert\NotBlank]
    #[Assert\Length(min: 1, max: 255)]
    public ?string $name = null;

    #[Assert\PositiveOrZero]
    public ?float $price = null;
}

For the full list of available constraints, see the Symfony Validator documentation.

Next steps