<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Spryker Documentation</title>
        <description>Spryker documentation center.</description>
        <link>https://docs.spryker.com/</link>
        <atom:link href="https://docs.spryker.com/feed.xml" rel="self" type="application/rss+xml"/>
        <lastBuildDate>Wed, 20 May 2026 08:20:33 +0000</lastBuildDate>
        <generator>Jekyll v4.2.2</generator>
        
        
        <item>
            <title>Back Office Configuration Framework</title>
            <description>&lt;p&gt;The Back Office Configuration Framework is a structured approach to exposing business-relevant configuration options directly in the Spryker Back Office. Instead of defining configuration in code or YAML files that require redeployment, businesses can manage defined behaviors through structured UI pages. Developers define configuration options once in YAML, and the framework automatically renders them as configurable Back Office interfaces.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;business-problems-it-solves&quot;&gt;Business problems it solves&lt;/h2&gt;
&lt;p&gt;Managing configuration in traditional commerce platforms creates friction between technical and business teams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configuration changes require developer involvement, code modifications, and a full deployment cycle.&lt;/li&gt;
&lt;li&gt;Business teams depend on IT to adjust behaviors that are fundamentally business decisions.&lt;/li&gt;
&lt;li&gt;YAML and code-level configuration is inaccessible to non-technical stakeholders and prone to error.&lt;/li&gt;
&lt;li&gt;Each configuration change introduces risk, slows down operations, and increases the total cost of change.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Back Office Configuration Framework addresses these challenges by creating a clear, controlled boundary between what developers define and what business users can adjust.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;key-value-for-your-business&quot;&gt;Key value for your business&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Faster time to change&lt;/strong&gt;
Business users adjust configured behaviors directly in the Back Office. No code change, no pull request, no deployment required.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced operational risk&lt;/strong&gt;
Configuration is exposed through structured, validated UI pages. Business users work within defined options rather than editing raw configuration files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Developer efficiency&lt;/strong&gt;
Developers define configuration options once in YAML. The framework handles rendering, validation, and persistence automatically, eliminating repetitive UI work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistent experience&lt;/strong&gt;
All configuration pages follow the same structure and interaction patterns, making it easier for business users to navigate and manage settings across features.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extensibility by design&lt;/strong&gt;
The framework supports both out of the box Spryker features and project-specific customizations, so teams can introduce new configurable behaviors without building new infrastructure.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;who-benefits-and-how&quot;&gt;Who benefits and how&lt;/h2&gt;
&lt;h3 id=&quot;for-business-leaders&quot;&gt;For business leaders&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lower cost of change&lt;/strong&gt;
Adjusting business behaviors no longer requires a development sprint or deployment window. Teams can respond to market conditions and business needs faster.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced dependency on IT&lt;/strong&gt;
Business teams gain direct control over selected configuration options without needing to involve developers for every adjustment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stable, governed configurability&lt;/strong&gt;
The framework exposes only explicitly defined options. Business users operate within guardrails set by the development team, reducing the risk of misconfiguration.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;for-product-and-commerce-teams&quot;&gt;For product and commerce teams&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Self-service configuration&lt;/strong&gt;
Manage feature behavior directly from the Back Office, using familiar UI patterns. No need to understand YAML syntax or request a deployment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Faster iteration&lt;/strong&gt;
Test different configurations, observe results, and adjust without waiting for a release cycle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visibility into configurable options&lt;/strong&gt;
All available configuration options are surfaced in one place, making it easy to understand what can be changed and what the current settings are.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;for-developers-and-architects&quot;&gt;For developers and architects&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Define once, render automatically&lt;/strong&gt;
Declare configuration options in YAML. The framework generates the corresponding Back Office UI, saving significant development and maintenance effort.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Separation of concerns&lt;/strong&gt;
Configuration definition stays with the developer; configuration management becomes a business user activity. This boundary is enforced by the framework.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Standardized patterns&lt;/strong&gt;
Avoid building custom configuration UIs for each feature. The framework provides consistent infrastructure that all teams benefit from.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;key-capabilities&quot;&gt;Key capabilities&lt;/h2&gt;
&lt;h3 id=&quot;support-for-out-of-the-box-and-custom-features&quot;&gt;Support for out of the box and custom features&lt;/h3&gt;
&lt;p&gt;The framework supports configuration for existing Spryker out of the box features as well as project-specific customizations. Teams can use it to expose configuration options for both standard platform capabilities and bespoke features built for a specific project.&lt;/p&gt;
&lt;h3 id=&quot;structured-and-controlled-business-configurability&quot;&gt;Structured and controlled business configurability&lt;/h3&gt;
&lt;p&gt;Selected business-relevant configuration moves from code level to UI level without exposing low-level technical complexity. The developer controls which options are available, what values are valid, and how they are presented. The business user controls what value is set, within those defined parameters.&lt;/p&gt;
&lt;h3 id=&quot;out-of-the-box-implementations&quot;&gt;Out of the box implementations&lt;/h3&gt;
&lt;p&gt;The Back Office Configuration Framework ships with the following out of the box implementations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;B2B Product Availability Display&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The initial release ships with a configuration UI for B2B Product Availability Display. This implementation demonstrates the framework in a real-world use case, allowing business users to configure how product availability information is presented to B2B buyers directly from the Back Office.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Basic Shop Theme&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The second out of the box implementation provides a configuration UI for shop branding. Business Admins can set theme colors, logo URLs, and custom CSS for the Storefront, Back Office, and Merchant Portal without code changes or redeployment. For details, see &lt;a href=&quot;/docs/pbc/all/back-office/latest/base-shop/basic-shop-theme-feature-overview.html&quot;&gt;Basic Shop Theme feature overview&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;
&lt;p&gt;The Back Office Configuration Framework follows a developer-first definition approach with a business-user-facing runtime experience:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Developer defines configuration options in YAML&lt;/strong&gt;
The developer declares the available configuration options, their data types, validation rules, and default values in a structured YAML definition.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Framework generates the Back Office UI&lt;/strong&gt;
The framework reads the YAML definition and automatically renders the corresponding configuration page in the Back Office, including form fields, labels, and validation feedback.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Business user manages configuration&lt;/strong&gt;
A Back Office user with appropriate permissions accesses the configuration page, adjusts the available options, and saves changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Changes take effect without redeployment&lt;/strong&gt;
Configuration updates are stored and applied at runtime. No code changes or deployments are required.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;comparison-to-traditional-configuration-approaches&quot;&gt;Comparison to traditional configuration approaches&lt;/h2&gt;
&lt;h3 id=&quot;code-level-configuration&quot;&gt;Code-level configuration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Traditional approach:&lt;/strong&gt; Configuration values are hardcoded or set in PHP configuration files. Changing a value requires a code change, review, and deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Back Office Configuration Framework:&lt;/strong&gt; Configuration values are managed in the Back Office UI. Changes take effect immediately, within the boundaries defined by the developer.&lt;/p&gt;
&lt;h3 id=&quot;yaml-based-configuration&quot;&gt;YAML-based configuration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Traditional approach:&lt;/strong&gt; Configuration is declared in YAML files that are part of the codebase. Changing a value requires editing a file, committing the change, and triggering a deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Back Office Configuration Framework:&lt;/strong&gt; YAML is still used, but only by developers to define what options are available. Business users interact with the resulting UI, not with YAML directly.&lt;/p&gt;
&lt;h3 id=&quot;custom-configuration-uis&quot;&gt;Custom configuration UIs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Traditional approach:&lt;/strong&gt; Each feature that requires business-user configuration needs a custom-built UI, resulting in inconsistent experiences and duplicated development effort.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Back Office Configuration Framework:&lt;/strong&gt; All configurable features share the same framework-generated UI infrastructure, reducing development effort and ensuring a consistent experience.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;related-features&quot;&gt;Related features&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configuration Management Development Guide&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/docs/dg/dev/backend-development/configuration-management.html&quot;&gt;Developer guide&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</description>
            <pubDate>Wed, 20 May 2026 08:16:55 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/back-office/latest/base-shop/backoffice-configuration-framework.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/back-office/latest/base-shop/backoffice-configuration-framework.html</guid>
            
            
        </item>
        
        <item>
            <title>Troubleshooting API Platform</title>
            <description>This document provides solutions to common issues when working with API Platform in Spryker.

## Generation issues

### Resources not generating

**Symptom:** Running `docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate` completes but no resources are created.

**Possible causes:**

1. **Schema file location is incorrect**

   ```bash
   ❌ src/Pyz/Glue/Customer/api/customers.resource.yml
   ✅ src/Pyz/Glue/Customer/resources/api/backend/customers.resource.yml
   ```

2. **API type not configured**

   Check `config/{APPLICATION}/packages/spryker_api_platform.php`:

   ```php
   $containerConfigurator-&gt;extension(&apos;spryker_api_platform&apos;, [
       &apos;api_types&apos; =&gt; [
           &apos;backend&apos;,  // Must match directory name
       ],
   ]);
   ```

3. **Bundle not registered**

   Verify `config/{APPLICATION}/bundles.php` includes:

   ```php
   SprykerApiPlatformBundle::class =&gt; [&apos;all&apos; =&gt; true],
   ```

**Solution:**

```bash
# Debug to see what&apos;s being discovered
docker/sdk cli glue  api:debug --list

# Check schema validation
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate --validate-only

# Force regeneration
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate --force
```

### Schema validation errors

**Symptom:** Generation fails with schema validation errors.

**Common errors:**

```bash
# Error: Invalid operation type
❌ operations:
    - type: CREATE

✅ operations:
    - type: Post

# Error: Invalid property type
❌ type: int
✅ type: integer

# Error: Missing resource name
❌ resource:
    shortName: customers

✅ resource:
    name: Customers
    shortName: customers
```

**Solution:**

1. Check schema against examples in documentation
2. Use `--validate-only` flag for detailed validation:

   ```bash
   docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate --validate-only
   ```

3. Inspect merged schema:

   ```bash
   docker/sdk cli glue  api:debug resource-name --show-merged
   ```

## Runtime issues

### Provider/Processor not found

**Symptom:**

```bash
Error: Class &quot;Pyz\Glue\Customer\Api\Backend\Provider\CustomerBackendProvider&quot; not found
```

**Possible causes:**

1. Class doesn&apos;t exist or namespace is wrong
2. Not registered in the Dependency Injection container
3. Typo in the schema file

**Solution:**

1. Verify the class exists and namespace matches:

   ```php
   namespace Pyz\Glue\Customer\Api\Backend\Provider;

   class CustomerBackendProvider implements ProviderInterface
   ```

2. Ensure services are auto-discovered in `ApplicationServices.php`:

   ```php
   $services-&gt;load(&apos;Pyz\\Glue\\&apos;, &apos;../../../src/Pyz/Glue/&apos;);
   ```

3. Check class name in the resource schema file of the module matches exactly:

   ```yaml
   provider: &quot;Pyz\\Glue\\Customer\\Api\\Backend\\Provider\\CustomerBackendProvider&quot;
   ```

### Validation not working

**Symptom:** API accepts invalid data despite validation rules.

**Possible causes:**

1. Validation schema file not found
2. Wrong operation name in validation schema
3. Validation groups not matching

**Solution:**

1. Ensure validation file exists:

   ```bash
   ✅ resources/api/backend/customers.validation.yml
   ```

2. Match operation names to HTTP methods:

   ```yaml
   post:      # For POST /customers
     email:
       - NotBlank

   patch:     # For PATCH /customers/{id}
     email:
       - Optional:
           constraints:
             - Email
   ```

3. Check generated resource class (for example `Generated\Api\Storefront\CustomersStorefrontResource`) has validation attributes:

   ```php
   #[Assert\NotBlank(groups: [&apos;customers:create&apos;])]
   #[Assert\Email(groups: [&apos;customers:create&apos;])]
   public ?string $email = null;
   ```

### API documentation UI not displaying correctly

**Symptom:** When accessing the root URL of your API application, you see:
- Missing styles/CSS
- Broken JavaScript functionality
- Plain HTML without formatting
- &quot;Failed to load resource&quot; errors in browser docker/sdk cli glue 

**Cause:** Assets were not installed after API Platform integration.

**Solution:**

Run the appropriate assets:install command for your application:

### For Glue application

```bash
docker/sdk cli glue assets:install public/Glue/assets  --symlink
```

### For GlueStorefront

```bash
docker/sdk cli GLUE_APPLICATION=GLUE_STOREFRONT glue assets:install public/GlueStorefront/assets/  --symlink
```

### For GlueBackend

```bash
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue assets:install public/GlueBackend/assets/  --symlink
```

Then verify the documentation UI loads correctly by visiting the root URL:
- Storefront: `https://glue-storefront.mysprykershop.com/`
- Backend: `https://glue-backend.mysprykershop.com/`

{% info_block warningBox &quot;Required after integration&quot; %}

The `assets:install` command must be run after integrating API Platform and whenever API Platform assets are updated. This is a required step documented in [How to integrate API Platform](/docs/dg/dev/upgrade-and-migrate/integrate-api-platform.html).

{% endinfo_block %}

### 404 Not Found for API endpoints

**Symptom:** API requests return 404.

**Possible causes:**

1. Router not configured
2. Routes not loaded
3. Wrong URL format

**Solution:**

1. Verify `SymfonyFrameworkRouterPlugin` is registered:

   ```php
   // RouterDependencyProvider
   protected function getRouterPlugins(): array
   {
       return [
           new GlueRouterPlugin(),
           new SymfonyFrameworkRouterPlugin(), // Must be present
       ];
   }
   ```

2. Check API documentation for correct URLs:

   ```bash
   Storefront: https://glue-storefront.mysprykershop.com/
   Backend: https://glue-backend.mysprykershop.com/
   ```

   The interactive API documentation is available at the root URL of each application.

3. Use correct URL format:

   ```bash
   ❌ /api/v1/customers
   ✅ /customers
   ```

### Pagination not working

**Symptom:** All results returned instead of paginated response.

**Solution:**

1. Enable pagination in the schema file of the defining module:

   ```yaml
   resource:
     paginationEnabled: true
     paginationItemsPerPage: 10
   ```

2. Return `PaginatorInterface` from provider:

   ```php
   use ApiPlatform\State\Pagination\TraversablePaginator;

   return new TraversablePaginator(
       new \ArrayObject($results),
       $currentPage,
       $itemsPerPage,
       $totalItems
   );
   ```

3. Use pagination query parameters:

   ```bash
   GET /customers?page=2&amp;itemsPerPage=20
   ```

### Client cannot change items per page

**Symptom:** The `itemsPerPage` query parameter is ignored.

**Solution:**

Enable client-side items-per-page control and set a maximum limit in the resource schema:

```yaml
resource:
  paginationEnabled: true
  paginationItemsPerPage: 10
  paginationClientItemsPerPage: true
  paginationMaximumItemsPerPage: 100
```

Without `paginationClientItemsPerPage: true`, the `itemsPerPage` query parameter has no effect. The `paginationMaximumItemsPerPage` option prevents clients from requesting excessively large pages.

### Client cannot disable pagination

**Symptom:** The `pagination=false` query parameter is ignored and results are still paginated.

**Solution:**

Enable client-side pagination control in the resource schema:

```yaml
resource:
  paginationClientEnabled: true
```

Without `paginationClientEnabled: true`, the `pagination` query parameter has no effect.

For a full reference of all pagination options, see [Resource Schemas — Pagination](/docs/dg/dev/architecture/api-platform/resource-schemas.html#pagination).

## Dependency Injection issues

### Services not autowired

**Symptom:**

```bash
Cannot autowire service &quot;CustomerBackendProvider&quot;: argument &quot;$customerFacade&quot;
references class &quot;CustomerFacadeInterface&quot; but no such service exists.
```

**Solution:**

1. Register facade in the respective applications `ApplicationServices.php`:

   ```php
   use Pyz\Zed\Customer\Business\CustomerFacadeInterface;
   use Pyz\Zed\Customer\Business\CustomerFacade;

   $services-&gt;set(CustomerFacadeInterface::class, CustomerFacade::class);
   ```

2. Ensure constructor uses interface type hints:

   ```php
   public function __construct(
       private CustomerFacadeInterface $customerFacade,  // ✅ Interface
   ) {}
   ```

## Performance issues

### Slow API responses

**Symptom:** API endpoints respond slowly.

**Solution:**

1. Enable Symfony cache:

   ```bash
   docker/sdk cli glue  cache:warmup
   ```

2. Use pagination for collections
3. Optimize database queries in Provider
4. Use API Platform&apos;s built-in caching features

## Development tips

### Debugging schema merging

See which schemas contribute to final resource:

```bash
docker/sdk cli glue  api:debug customers --api-type=backend --show-sources
```

Output:

```bash
Source Files (priority order):
  ✓ vendor/spryker/customer/resources/api/backend/customers.resource.yml (CORE)
  ✓ src/SprykerFeature/CRM/resources/api/backend/customers.resource.yml (FEATURE)
  ✓ src/Pyz/Glue/Customer/resources/api/backend/customers.resource.yml (PROJECT)
```

### Inspecting generated code

View the generated resource class:

```bash
cat src/Generated/Api/Backend/CustomersBackendResource.php
```

Check for:
- Correct property types
- Validation attributes
- API Platform metadata

### Testing with dry-run

Preview generation without writing files:

```bash
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate --dry-run
```

## Getting help

If you encounter issues not covered here:

1. **Check logs:**

   ```bash
   tail -f var/log/application.log
   tail -f var/log/exception.log
   ```

2. **Enable debug mode:**

   ```php
   // config/{APPLICATION}/packages/spryker_api_platform.php
   $containerConfigurator-&gt;extension(&apos;spryker_api_platform&apos;, [
       &apos;debug&apos; =&gt; true,
   ]);
   ```

3. **Validate environment:**

   ```bash
   php -v  # Check PHP version (8.1+)
   composer show | grep api-platform
   docker/sdk cli glue  debug:container | grep -i api
   ```

4. **Common error patterns:**

| Error | Likely cause | Solution |
|-------|--------------|----------|
| `Class not found` | Autoloading issue | Run `composer dump-autoload` |
| `Service not found` | DI configuration | Check `ApplicationServices.php` |
| `Route not found` | Router not configured | Add `SymfonyFrameworkRouterPlugin` |
| `Validation failed` | Schema mismatch | Regenerate with `--force` |
| `Cache is stale` | Outdated cache | Run `cache:clear` |
| API docs UI broken/unstyled | Assets not installed | Run `docker/sdk cli glue /glue assets:install` |

## Next steps

- [API Platform](/docs/dg/dev/architecture/api-platform.html) - Overview and concepts
- [How to integrate API Platform](/docs/dg/dev/upgrade-and-migrate/integrate-api-platform.html) - Setup guide
- [API Platform Enablement](/docs/dg/dev/architecture/api-platform/enablement.html) - Creating resources
- [Resource Schemas](/docs/dg/dev/architecture/api-platform/resource-schemas.html) - Resource schema reference
- [Validation Schemas](/docs/dg/dev/architecture/api-platform/validation-schemas.html) - Validation schema reference
- [API Platform Testing](/docs/dg/dev/architecture/api-platform/testing.html) - Testing guide
</description>
            <pubDate>Tue, 19 May 2026 12:29:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/architecture/api-platform/troubleshooting.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/architecture/api-platform/troubleshooting.html</guid>
            
            
        </item>
        
        <item>
            <title>API Platform Testing</title>
            <description>This document describes how to write and run tests for your API Platform resources in your project.

## Overview

API Platform provides a comprehensive testing infrastructure built on top of:

- **Codeception**: Test framework for PHP
- **API Platform Test Client**: Specialized HTTP client for API testing
- **PHPUnit Assertions**: Rich set of assertion methods
- **Test Helpers**: Custom helpers for test data management

The testing infrastructure supports both Backend and Storefront API types with dedicated base classes and configuration.

## Test architecture

### Test class hierarchy

```bash
AbstractApiTestCase (base class from core)
├── BackendApiTestCase (for Backend API tests)
└── StorefrontApiTestCase (for Storefront API tests)
```

### Key components

| Component | Purpose |
|-----------|---------|
| `AbstractApiTestCase` | Base class providing API Platform integration |
| `BackendApiTestCase` | Pre-configured for Backend API testing |
| `StorefrontApiTestCase` | Pre-configured for Storefront API testing |
| `ApiTestKernel` | Lightweight Symfony kernel for testing |
| `ApiTestAssertionsTrait` | API-specific assertions (from API Platform) |

### Test helper classes

The testing infrastructure provides specialized Codeception helpers to streamline test development:

| Helper Class | Purpose |
|--------------|---------|
| `BootstrapHelper` | Configures application plugin providers for test environments via codeception.yml. Allows different test suites to use different factory implementations without hardcoding dependencies in test infrastructure. |
| `ApiPlatformHelper` | Configures resource generation and cache lifecycle for the test kernel. Has two modes — `project` (default, preserves the compiled container for speed) and `core` (generates fresh resources per suite and cleans the container cache afterwards, used when testing the API Platform module itself). See [ApiPlatformHelper modes](#apiplatformhelper-modes). |
| `ApiPlatformConfigBuilder` | Provides a fluent interface for building test-specific API Platform configurations. Useful for creating isolated test scenarios with custom settings. |
| `ApiResourceGeneratorHelper` | Assists with testing resource generation functionality. Provides methods to generate test resources, validate generation output, and clean up generated files. |

These helpers are automatically available in your test cases through the Codeception actor and provide essential functionality for testing API Platform resources effectively.

## Setting up your test environment

### 1. Configure autoloading for generated test resources

Update your project-level `composer.json` to include the test API namespace:

`composer.json` (project root)

```json
{
    &quot;autoload-dev&quot;: {
        &quot;psr-4&quot;: {
            &quot;PyzTest\\&quot;: &quot;tests/PyzTest/&quot;,
            &quot;Generated\\TestApi\\&quot;: &quot;tests/_data/Api/&quot;
        }
    }
}
```

### 2. Optional: Configure application plugin providers

If your tests require application plugins to be registered (for example, service providers or middleware), configure the `BootstrapHelper` in your suite&apos;s `codeception.yml`:

`tests/PyzTest/Glue/Customer/BackendApi/codeception.yml`

```yaml
modules:
    enabled:
        - \SprykerTest\Shared\Testify\Helper\BootstrapHelper:
            applicationPluginProvider:
                class: Spryker\Glue\GlueBackendApiApplication\GlueBackendApiApplicationFactory
                method: getApplicationPlugins
```

For Storefront API tests, use the appropriate factory:

`tests/PyzTest/Glue/Customer/StorefrontApi/codeception.yml`

```yaml
modules:
    enabled:
        - \SprykerTest\Shared\Testify\Helper\BootstrapHelper:
            applicationPluginProvider:
                class: Spryker\Glue\GlueStorefrontApiApplication\GlueStorefrontApiApplicationFactory
                method: getApplicationPlugins
```

**Configuration options:**

- `class`: The fully qualified class name of the factory that provides application plugins
- `method`: The method name to call on the factory (typically `getApplicationPlugins`)

If no `applicationPluginProvider` is configured, the helper returns an empty array, and tests run without additional application plugins.

### 3. Create test directory structure

```bash
tests/
├── PyzTest/
│   └── Glue/
│       └── Customer/
│           ├── BackendApi/
│           │   ├── codeception.yml
│           │   └── CustomersBackendApiTest.php
│           └── StorefrontApi/
│               ├── codeception.yml
│               └── CustomersStorefrontApiTest.php
└── _data/
    └── Api/
        ├── Backend/
        │   └── CustomersBackendResource.php (generated)
        └── Storefront/
            └── CustomersStorefrontResource.php (generated)
```

### 4. Generate API resources for testing

The resources and the container are automatically generated right before the test suite runs.

#### Automatic resource generation and cleanup

The test infrastructure handles resource lifecycle automatically:

- **Generation** (core mode only): Test-specific API resources are generated into `tests/_data/Api/{ApiType}/` before each suite executes. In project mode, the helper instead validates that the project-generated resources already exist on disk.
- **Cleanup** (core mode only): The `ApiPlatformHelper` clears the compiled Symfony test kernel cache and the generated resources after the suite completes. Project mode deliberately skips this step so the compiled container can be reused across runs.
- **Mode selection**: Choose the mode in `codeception.yml` — see [ApiPlatformHelper modes](#apiplatformhelper-modes) below for the trade-offs.

This automation ensures that:
- Tests always run against the latest schema definitions
- No manual cache clearing is required between test runs
- Test failures related to stale cache are eliminated

## Writing Backend API tests

### Basic test structure

Backend API tests extend `BackendApiTestCase` and use the `BackendApiTester` tester which gets automatically injected into your tests by Codeception.

`tests/PyzTest/Glue/Customer/BackendApi/CustomersBackendApiTest.php`

```php
&lt;?php

namespace PyzTest\Glue\Customer\BackendApi;

use PyzTest\Glue\Customer\BackendApiTester;
use SprykerTest\Shared\ApiPlatform\Test\BackendApiTestCase;

/**
 * @group PyzTest
 * @group Glue
 * @group Customer
 * @group BackendApi
 * @group CustomersBackendApiTest
 */
class CustomersBackendApiTest extends BackendApiTestCase
{
    protected BackendApiTester $tester;

    public function testGivenValidDataWhenCreatingCustomerViaPostThenCustomerIsCreatedSuccessfully(): void
    {
        // Arrange
        $customerData = [
            &apos;email&apos; =&gt; &apos;john.doe@example.com&apos;,
            &apos;firstName&apos; =&gt; &apos;John&apos;,
            &apos;lastName&apos; =&gt; &apos;Doe&apos;,
        ];

        // Act
        static::createClient()-&gt;request(&apos;POST&apos;, &apos;/customers&apos;, [&apos;json&apos; =&gt; $customerData]);

        // Assert
        $this-&gt;assertResponseIsSuccessful();
        $this-&gt;assertResponseStatusCodeSame(201);
        $this-&gt;assertJsonContains([&apos;email&apos; =&gt; &apos;john.doe@example.com&apos;]);
        $this-&gt;assertJsonContains([&apos;firstName&apos; =&gt; &apos;John&apos;]);
        $this-&gt;assertJsonContains([&apos;lastName&apos; =&gt; &apos;Doe&apos;]);
    }
}
```

### Testing GET operations

#### Single resource

```php
public function testGivenExistingCustomerWhenRetrievingViaGetThenCustomerDataIsReturned(): void
{
    // Arrange
    $customerTransfer = $this-&gt;tester-&gt;haveCustomer([
        &apos;email&apos; =&gt; &apos;existing@example.com&apos;,
        &apos;firstName&apos; =&gt; &apos;Jane&apos;,
        &apos;lastName&apos; =&gt; &apos;Smith&apos;,
    ]);

    // Act
    static::createClient()-&gt;request(
        &apos;GET&apos;,
        sprintf(&apos;/customers/%s&apos;, $customerTransfer-&gt;getCustomerReference())
    );

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $this-&gt;assertJsonContains([&apos;email&apos; =&gt; &apos;existing@example.com&apos;]);
    $this-&gt;assertJsonContains([&apos;firstName&apos; =&gt; &apos;Jane&apos;]);
}
```

#### Collection with pagination

```php
public function testGivenMultipleCustomersWhenRetrievingCollectionViaGetThenAllCustomersAreReturned(): void
{
    // Arrange
    $this-&gt;tester-&gt;haveCustomer([&apos;email&apos; =&gt; &apos;customer1@example.com&apos;]);
    $this-&gt;tester-&gt;haveCustomer([&apos;email&apos; =&gt; &apos;customer2@example.com&apos;]);
    $this-&gt;tester-&gt;haveCustomer([&apos;email&apos; =&gt; &apos;customer3@example.com&apos;]);

    // Act
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers&apos;);

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;Collection&apos;]);
    $this-&gt;assertJsonContains([&apos;totalItems&apos; =&gt; 3]);
}

public function testGivenPaginationParamsWhenRetrievingCollectionThenPaginatedResultsAreReturned(): void
{
    // Arrange
    for ($i = 1; $i &lt;= 15; $i++) {
        $this-&gt;tester-&gt;haveCustomer([&apos;email&apos; =&gt; sprintf(&apos;customer%d@example.com&apos;, $i)]);
    }

    // Act
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers?page=2&amp;itemsPerPage=5&apos;);

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;Collection&apos;]);
    $this-&gt;assertJsonContains([&apos;view&apos; =&gt; [&apos;@id&apos; =&gt; &apos;/customers?page=2&amp;itemsPerPage=5&apos;]]);
}
```

### Testing POST operations

#### Successful creation

```php
public function testGivenValidDataWhenCreatingCustomerViaPostThenCustomerIsCreatedSuccessfully(): void
{
    // Arrange
    $customerData = [
        &apos;email&apos; =&gt; &apos;new.customer@example.com&apos;,
        &apos;firstName&apos; =&gt; &apos;New&apos;,
        &apos;lastName&apos; =&gt; &apos;Customer&apos;,
    ];

    // Act
    $response = static::createClient()-&gt;request(&apos;POST&apos;, &apos;/customers&apos;, [
        &apos;json&apos; =&gt; $customerData,
    ]);

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $this-&gt;assertResponseStatusCodeSame(201);
    $this-&gt;assertJsonContains($customerData);
    $this-&gt;assertResponseHeaderSame(&apos;Content-Type&apos;, &apos;application/ld+json; charset=utf-8&apos;);

    // Verify the resource was created and has an ID
    $responseData = $response-&gt;toArray();
    $this-&gt;assertArrayHasKey(&apos;customerReference&apos;, $responseData);
    $this-&gt;assertNotEmpty($responseData[&apos;customerReference&apos;]);
}
```

#### Validation errors

```php
public function testGivenInvalidDataWhenCreatingCustomerViaPostThenValidationErrorIsReturned(): void
{
    // Arrange
    $invalidCustomerData = [
        &apos;email&apos; =&gt; &apos;invalid-email&apos;,  // Invalid email format
        &apos;firstName&apos; =&gt; &apos;&apos;,            // Empty first name
    ];

    // Act
    static::createClient()-&gt;request(&apos;POST&apos;, &apos;/customers&apos;, [
        &apos;json&apos; =&gt; $invalidCustomerData,
    ]);

    // Assert
    $this-&gt;assertResponseStatusCodeSame(422);
    $this-&gt;assertResponseHeaderSame(&apos;Content-Type&apos;, &apos;application/ld+json; charset=utf-8&apos;);
    $this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;ConstraintViolationList&apos;]);
    $this-&gt;assertJsonContains([
        &apos;violations&apos; =&gt; [
            [&apos;propertyPath&apos; =&gt; &apos;email&apos;],
            [&apos;propertyPath&apos; =&gt; &apos;firstName&apos;],
            [&apos;propertyPath&apos; =&gt; &apos;lastName&apos;],
        ],
    ]);
}
```

#### Business rule violations

```php
public function testGivenDuplicateEmailWhenCreatingCustomerViaPostThenErrorIsReturned(): void
{
    // Arrange
    $this-&gt;tester-&gt;haveCustomer([&apos;email&apos; =&gt; &apos;duplicate@example.com&apos;]);

    $duplicateData = [
        &apos;email&apos; =&gt; &apos;duplicate@example.com&apos;,
        &apos;firstName&apos; =&gt; &apos;Duplicate&apos;,
        &apos;lastName&apos; =&gt; &apos;Customer&apos;,
    ];

    // Act
    static::createClient()-&gt;request(&apos;POST&apos;, &apos;/customers&apos;, [
        &apos;json&apos; =&gt; $duplicateData,
    ]);

    // Assert
    $this-&gt;assertResponseStatusCodeSame(422);
    $this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;Error&apos;]);
    $this-&gt;assertJsonContains([&apos;detail&apos; =&gt; &apos;Customer with this email already exists&apos;]);
}
```

### Testing PATCH operations

```php
public function testGivenExistingCustomerWhenUpdatingViaPatchThenCustomerIsUpdatedSuccessfully(): void
{
    // Arrange
    $customerTransfer = $this-&gt;tester-&gt;haveCustomer([
        &apos;email&apos; =&gt; &apos;update@example.com&apos;,
        &apos;firstName&apos; =&gt; &apos;Original&apos;,
        &apos;lastName&apos; =&gt; &apos;Name&apos;,
    ]);

    $updateData = [
        &apos;firstName&apos; =&gt; &apos;Updated&apos;,
        &apos;lastName&apos; =&gt; &apos;Name&apos;,
    ];

    // Act
    static::createClient()-&gt;request(
        &apos;PATCH&apos;,
        sprintf(&apos;/customers/%s&apos;, $customerTransfer-&gt;getCustomerReference()),
        [
            &apos;json&apos; =&gt; $updateData,
            &apos;headers&apos; =&gt; [
                &apos;Content-Type&apos; =&gt; &apos;application/merge-patch+json&apos;,
            ],
        ]
    );

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $this-&gt;assertJsonContains([&apos;firstName&apos; =&gt; &apos;Updated&apos;]);
    $this-&gt;assertJsonContains([&apos;email&apos; =&gt; &apos;update@example.com&apos;]); // Unchanged
}
```

### Testing DELETE operations

```php
public function testGivenExistingCustomerWhenDeletingViaDeleteThenCustomerIsDeletedSuccessfully(): void
{
    // Arrange
    $customerTransfer = $this-&gt;tester-&gt;haveCustomer([
        &apos;email&apos; =&gt; &apos;delete@example.com&apos;,
    ]);

    // Act
    static::createClient()-&gt;request(
        &apos;DELETE&apos;,
        sprintf(&apos;/customers/%s&apos;, $customerTransfer-&gt;getCustomerReference())
    );

    // Assert
    $this-&gt;assertResponseStatusCodeSame(204);
    $this-&gt;assertResponseHasNoContent();
}

public function testGivenNonExistentCustomerWhenDeletingViaDeleteThen404IsReturned(): void
{
    // Act
    static::createClient()-&gt;request(&apos;DELETE&apos;, &apos;/customers/NON-EXISTENT-REFERENCE&apos;);

    // Assert
    $this-&gt;assertResponseStatusCodeSame(404);
}
```

### Testing relationships

The relationships feature enables resources to include related resources via the `?include=` query parameter. For details on configuring relationships, see [Relationships](/docs/dg/dev/architecture/api-platform/relationships.html).

#### Testing include parameter

```php
public function testGivenCustomerWithAddressesWhenRequestingWithIncludeThenAddressesAreIncluded(): void
{
    // Arrange
    $customerTransfer = $this-&gt;tester-&gt;haveCustomer();
    $this-&gt;tester-&gt;haveAddress([&apos;customerReference&apos; =&gt; $customerTransfer-&gt;getCustomerReference()]);
    $this-&gt;tester-&gt;haveAddress([&apos;customerReference&apos; =&gt; $customerTransfer-&gt;getCustomerReference()]);

    // Act
    $response = static::createClient()-&gt;request(
        &apos;GET&apos;,
        sprintf(&apos;/customers/%s?include=addresses&apos;, $customerTransfer-&gt;getCustomerReference())
    );

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $data = $response-&gt;toArray();

    // Assert relationships section exists
    $this-&gt;assertArrayHasKey(&apos;relationships&apos;, $data[&apos;data&apos;]);
    $this-&gt;assertArrayHasKey(&apos;addresses&apos;, $data[&apos;data&apos;][&apos;relationships&apos;]);

    // Assert included section contains addresses
    $this-&gt;assertArrayHasKey(&apos;included&apos;, $data);
    $this-&gt;assertCount(2, $data[&apos;included&apos;]);
}
```

#### Testing JSON:API structure

```php
public function testGivenIncludedResourcesWhenRetrievingThenJsonApiStructureIsValid(): void
{
    // Arrange
    $customerTransfer = $this-&gt;tester-&gt;haveCustomer();
    $this-&gt;tester-&gt;haveAddress([&apos;customerReference&apos; =&gt; $customerTransfer-&gt;getCustomerReference()]);

    // Act
    $response = static::createClient()-&gt;request(
        &apos;GET&apos;,
        sprintf(&apos;/customers/%s?include=addresses&apos;, $customerTransfer-&gt;getCustomerReference())
    );

    // Assert
    $data = $response-&gt;toArray();

    // Verify main resource structure
    $this-&gt;assertArrayHasKey(&apos;data&apos;, $data);
    $this-&gt;assertArrayHasKey(&apos;type&apos;, $data[&apos;data&apos;]);
    $this-&gt;assertArrayHasKey(&apos;id&apos;, $data[&apos;data&apos;]);
    $this-&gt;assertArrayHasKey(&apos;attributes&apos;, $data[&apos;data&apos;]);
    $this-&gt;assertArrayHasKey(&apos;relationships&apos;, $data[&apos;data&apos;]);

    // Verify included resources structure
    foreach ($data[&apos;included&apos;] as $includedResource) {
        $this-&gt;assertArrayHasKey(&apos;type&apos;, $includedResource);
        $this-&gt;assertArrayHasKey(&apos;id&apos;, $includedResource);
        $this-&gt;assertArrayHasKey(&apos;attributes&apos;, $includedResource);
    }

    // Verify relationship linkage
    $relationshipData = $data[&apos;data&apos;][&apos;relationships&apos;][&apos;addresses&apos;][&apos;data&apos;];
    foreach ($relationshipData as $linkage) {
        $this-&gt;assertArrayHasKey(&apos;type&apos;, $linkage);
        $this-&gt;assertArrayHasKey(&apos;id&apos;, $linkage);
    }
}
```

## Writing Storefront API tests

### Basic test structure

Storefront API tests extend `StorefrontApiTestCase` and typically use mocks for read-only operations.

`tests/PyzTest/Glue/Customer/StorefrontApi/CustomersStorefrontApiTest.php`

```php
&lt;?php

namespace PyzTest\Glue\Customer\StorefrontApi;

use Codeception\Stub;
use Pyz\Client\Customer\CustomerClientInterface;
use PyzTest\Glue\Customer\StorefrontApiTester;
use SprykerTest\Shared\ApiPlatform\Test\StorefrontApiTestCase;

/**
 * @group PyzTest
 * @group Glue
 * @group Customer
 * @group StorefrontApi
 * @group CustomersStorefrontApiTest
 */
class CustomersStorefrontApiTest extends StorefrontApiTestCase
{
    protected StorefrontApiTester $tester;

    public function testGivenAuthenticatedCustomerWhenRetrievingProfileViaGetThenCustomerDataIsReturned(): void
    {
        // Arrange
        $customerClientStub = Stub::makeEmpty(CustomerClientInterface::class, [
            &apos;getCustomer&apos; =&gt; (new CustomerTransfer())
                -&gt;setEmail(&apos;customer@example.com&apos;)
                -&gt;setFirstName(&apos;John&apos;)
                -&gt;setLastName(&apos;Doe&apos;),
        ]);

        static::getContainer()-&gt;set(CustomerClientInterface::class, $customerClientStub);

        // Act
        static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers/me&apos;);

        // Assert
        $this-&gt;assertResponseIsSuccessful();
        $this-&gt;assertJsonContains([&apos;email&apos; =&gt; &apos;customer@example.com&apos;]);
    }
}
```

### Testing with service mocks

```php
public function testGivenMultipleCustomersWhenRetrievingCollectionViaGetThenAllCustomersAreReturned(): void
{
    // Arrange
    $customerClientStub = Stub::makeEmpty(CustomerClientInterface::class, [
        &apos;getCustomerCollection&apos; =&gt; [
            (new CustomerTransfer())-&gt;setEmail(&apos;customer1@example.com&apos;),
            (new CustomerTransfer())-&gt;setEmail(&apos;customer2@example.com&apos;),
        ],
    ]);

    static::getContainer()-&gt;set(CustomerClientInterface::class, $customerClientStub);

    // Act
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers&apos;);

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;Collection&apos;]);
}
```

## Available assertions

### HTTP response assertions

```php
// Status codes
$this-&gt;assertResponseIsSuccessful();        // 2xx status code
$this-&gt;assertResponseStatusCodeSame(200);   // Exact status code
$this-&gt;assertResponseStatusCodeSame(201);   // Created
$this-&gt;assertResponseStatusCodeSame(204);   // No content
$this-&gt;assertResponseStatusCodeSame(400);   // Bad request
$this-&gt;assertResponseStatusCodeSame(401);   // Unauthorized
$this-&gt;assertResponseStatusCodeSame(403);   // Forbidden
$this-&gt;assertResponseStatusCodeSame(404);   // Not found
$this-&gt;assertResponseStatusCodeSame(422);   // Validation error

// Headers
$this-&gt;assertResponseHasHeader(&apos;Content-Type&apos;);
$this-&gt;assertResponseHeaderSame(&apos;Content-Type&apos;, &apos;application/ld+json; charset=utf-8&apos;);
$this-&gt;assertResponseHeaderNotSame(&apos;X-Custom-Header&apos;, &apos;value&apos;);

// Content
$this-&gt;assertResponseHasNoContent();        // Empty response body
```

### JSON assertions

```php
// Content matching
$this-&gt;assertJsonContains([&apos;email&apos; =&gt; &apos;test@example.com&apos;]);
$this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;Customer&apos;]);
$this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;Collection&apos;]);

// Array keys
$responseData = $response-&gt;toArray();
$this-&gt;assertArrayHasKey(&apos;customerReference&apos;, $responseData);
$this-&gt;assertArrayNotHasKey(&apos;password&apos;, $responseData);

// Validation violations
$this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;ConstraintViolationList&apos;]);
$this-&gt;assertJsonContains([
    &apos;violations&apos; =&gt; [
        [&apos;propertyPath&apos; =&gt; &apos;email&apos;],
    ],
]);

// Collection metadata
$this-&gt;assertJsonContains([&apos;totalItems&apos; =&gt; 10]);
$this-&gt;assertJsonContains([&apos;view&apos; =&gt; [&apos;@id&apos; =&gt; &apos;/customers?page=1&apos;]]);
```

### Custom API Platform assertions

```php
// JSON-LD context
$this-&gt;assertJsonContains([&apos;@context&apos; =&gt; &apos;/contexts/Customer&apos;]);

// Hydra collections
$this-&gt;assertJsonContains([&apos;hydra:totalItems&apos; =&gt; 5]);
$this-&gt;assertJsonContains([&apos;hydra:member&apos; =&gt; []]);

// IRI matching
$iri = $this-&gt;getIriFromResource($resource);
$this-&gt;assertMatchesRegularExpression(&apos;~^/customers/[A-Z0-9\-]+$~&apos;, $iri);
```

## Test data management

### Using Codeception helpers

Create test data using your project&apos;s tester helpers:

```php
// Create a customer
$customerTransfer = $this-&gt;tester-&gt;haveCustomer([
    &apos;email&apos; =&gt; &apos;test@example.com&apos;,
    &apos;firstName&apos; =&gt; &apos;John&apos;,
    &apos;lastName&apos; =&gt; &apos;Doe&apos;,
]);

// Create multiple customers
for ($i = 1; $i &lt;= 10; $i++) {
    $this-&gt;tester-&gt;haveCustomer([
        &apos;email&apos; =&gt; sprintf(&apos;customer%d@example.com&apos;, $i),
    ]);
}
```

### Cleanup strategies

#### Automatic cleanup (default)

The test kernel automatically cleans up after each test. No manual cleanup needed.

#### Manual cleanup (when needed)

```php
protected function tearDown(): void
{
    // Custom cleanup logic
    $this-&gt;tester-&gt;cleanupCustomers();

    parent::tearDown();
}
```

## Testing different media types

### JSON-LD (default)

```php
public function testJsonLdFormat(): void
{
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers&apos;, [
        &apos;headers&apos; =&gt; [
            &apos;Accept&apos; =&gt; &apos;application/ld+json&apos;,
        ],
    ]);

    $this-&gt;assertResponseHeaderSame(&apos;Content-Type&apos;, &apos;application/ld+json; charset=utf-8&apos;);
    $this-&gt;assertJsonContains([&apos;@context&apos; =&gt; &apos;/contexts/Customer&apos;]);
}
```

### JSON:API

```php
public function testJsonApiFormat(): void
{
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers&apos;, [
        &apos;headers&apos; =&gt; [
            &apos;Accept&apos; =&gt; &apos;application/vnd.api+json&apos;,
        ],
    ]);

    $this-&gt;assertResponseHeaderSame(&apos;Content-Type&apos;, &apos;application/vnd.api+json; charset=utf-8&apos;);
    $this-&gt;assertJsonContains([&apos;data&apos; =&gt; [&apos;type&apos; =&gt; &apos;Customer&apos;]]);
}
```

### HAL+JSON

```php
public function testHalJsonFormat(): void
{
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers&apos;, [
        &apos;headers&apos; =&gt; [
            &apos;Accept&apos; =&gt; &apos;application/hal+json&apos;,
        ],
    ]);

    $this-&gt;assertResponseHeaderSame(&apos;Content-Type&apos;, &apos;application/hal+json; charset=utf-8&apos;);
    $this-&gt;assertJsonContains([&apos;_links&apos; =&gt; [&apos;self&apos; =&gt; [&apos;href&apos; =&gt; &apos;/customers&apos;]]]);
}
```

## Advanced testing patterns

### Testing with filters

```php
public function testGivenFilterParamsWhenRetrievingCollectionThenFilteredResultsAreReturned(): void
{
    // Arrange
    $this-&gt;tester-&gt;haveCustomer([&apos;email&apos; =&gt; &apos;active@example.com&apos;, &apos;status&apos; =&gt; &apos;active&apos;]);
    $this-&gt;tester-&gt;haveCustomer([&apos;email&apos; =&gt; &apos;inactive@example.com&apos;, &apos;status&apos; =&gt; &apos;inactive&apos;]);

    // Act
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers?status=active&apos;);

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $responseData = static::createClient()-&gt;getResponse()-&gt;toArray();
    $this-&gt;assertCount(1, $responseData[&apos;hydra:member&apos;]);
}
```

### Testing sorting

```php
public function testGivenSortParamsWhenRetrievingCollectionThenSortedResultsAreReturned(): void
{
    // Arrange
    $this-&gt;tester-&gt;haveCustomer([&apos;lastName&apos; =&gt; &apos;Zulu&apos;]);
    $this-&gt;tester-&gt;haveCustomer([&apos;lastName&apos; =&gt; &apos;Alpha&apos;]);
    $this-&gt;tester-&gt;haveCustomer([&apos;lastName&apos; =&gt; &apos;Bravo&apos;]);

    // Act
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers?order[lastName]=asc&apos;);

    // Assert
    $this-&gt;assertResponseIsSuccessful();
    $responseData = static::createClient()-&gt;getResponse()-&gt;toArray();
    $members = $responseData[&apos;hydra:member&apos;];

    $this-&gt;assertEquals(&apos;Alpha&apos;, $members[0][&apos;lastName&apos;]);
    $this-&gt;assertEquals(&apos;Bravo&apos;, $members[1][&apos;lastName&apos;]);
    $this-&gt;assertEquals(&apos;Zulu&apos;, $members[2][&apos;lastName&apos;]);
}
```

### Testing error scenarios

```php
public function testGivenMalformedJsonWhenCreatingCustomerViaPostThenBadRequestIsReturned(): void
{
    // Act
    static::createClient()-&gt;request(&apos;POST&apos;, &apos;/customers&apos;, [
        &apos;body&apos; =&gt; &apos;{invalid-json}&apos;,
        &apos;headers&apos; =&gt; [
            &apos;Content-Type&apos; =&gt; &apos;application/json&apos;,
        ],
    ]);

    // Assert
    $this-&gt;assertResponseStatusCodeSame(400);
}

public function testGivenUnauthorizedRequestWhenAccessingProtectedResourceThen401IsReturned(): void
{
    // Act
    static::createClient()-&gt;request(&apos;GET&apos;, &apos;/customers/me&apos;);

    // Assert
    $this-&gt;assertResponseStatusCodeSame(401);
}
```

## Running tests

### Run all project tests (slow, not recommended)

```bash
docker/sdk cli vendor/bin/codecept run
```

### Run specific test suite

```bash
# Run Backend API tests only
docker/sdk cli vendor/bin/codecept run -c path/to/codeception.yml -g BackendApi

# Run Storefront API tests only
docker/sdk cli vendor/bin/codecept run -c path/to/codeception.yml  -g StorefrontApi
```

## Codeception configuration

### Suite configuration

Configure your test suite&apos;s `codeception.yml` to enable the necessary helpers:

`tests/PyzTest/Glue/Customer/BackendApi/codeception.yml`

```yaml
suite_namespace: PyzTest\Glue\Customer\BackendApi

actor: BackendApiTester

modules:
    enabled:
        - \SprykerTest\Shared\Testify\Helper\BootstrapHelper:
            applicationPluginProvider:
                class: Spryker\Glue\GlueBackendApiApplication\GlueBackendApiApplicationFactory
                method: getApplicationPlugins

paths:
    tests: .
    data: ../../../../../_data
    support: _support
    output: ../../../../../_output

settings:
    bootstrap: _bootstrap.php
    colors: true
    memory_limit: 1024M
```

**Key configuration points:**

- **BootstrapHelper**: Provides application plugins for the test kernel. This is optional and can be omitted if your tests do not require application-level dependencies.
- **suite_namespace**: Must match your test suite&apos;s PHP namespace
- **actor**: The tester class name (for example, `BackendApiTester`, `StorefrontApiTester`)

### ApiPlatformHelper modes

`ApiPlatformHelper` runs in one of two modes, selected in the suite&apos;s `codeception.yml`:

```yaml
modules:
    enabled:
        - \SprykerTest\ApiPlatform\Helper\ApiPlatformHelper:
            mode: &apos;project&apos;      # default; or &apos;core&apos; for module-level tests
```

| Mode | Use this when | Before suite | After suite |
|---|---|---|---|
| `project` (default) | Testing your own project&apos;s API resources end-to-end. | Validates that the project-generated resources in `src/Generated/Api/` exist. Skips generation. | Does nothing — the compiled container is preserved across runs for fast subsequent invocations. |
| `core` | Testing the `ApiPlatform` module itself (or any module that ships its own schemas in isolation from a project). | Generates fresh resources into `tests/_data/Api/{ApiType}/`. Requires `apiType` to be set on the helper. | Removes the generated resources and clears the compiled test kernel cache so the next suite starts from a clean slate. |

Use `project` mode for almost all real-world test suites — it is significantly faster because the compiled Symfony container is reused. Reach for `core` mode only when you intentionally want each suite to regenerate resources from scratch (typical when testing schema generation or a single module without a project around it).

When using `core` mode, declare which API type the suite exercises so the helper knows what to generate:

```yaml
modules:
    enabled:
        - \SprykerTest\ApiPlatform\Helper\ApiPlatformHelper:
            mode: &apos;core&apos;
            apiType: &apos;Storefront&apos;   # or &apos;Backend&apos;
```

### Helper classes

Create helper classes to manage test data:

`tests/PyzTest/Glue/Customer/Helper/CustomerHelper.php`

```php
&lt;?php

namespace PyzTest\Glue\Customer\Helper;

use Codeception\Module;
use Generated\Shared\Transfer\CustomerTransfer;
use Pyz\Zed\Customer\Business\CustomerFacadeInterface;

class CustomerHelper extends Module
{
    public function haveCustomer(array $seed = []): CustomerTransfer
    {
        $customerTransfer = (new CustomerTransfer())
            -&gt;fromArray($seed, true)
            -&gt;setEmail($seed[&apos;email&apos;] ?? sprintf(&apos;customer-%s@example.com&apos;, uniqid()))
            -&gt;setFirstName($seed[&apos;firstName&apos;] ?? &apos;Test&apos;)
            -&gt;setLastName($seed[&apos;lastName&apos;] ?? &apos;Customer&apos;);

        return $this-&gt;getCustomerFacade()-&gt;createCustomer($customerTransfer);
    }

    protected function getCustomerFacade(): CustomerFacadeInterface
    {
        return $this-&gt;getModule(&apos;\\PyzTest\\Shared\\Testify\\Helper\\Environment&apos;)
            -&gt;getFacade(&apos;Customer&apos;);
    }
}
```

## Best practices

### 1. Use descriptive test method names

```php
// ✅ Good
public function testGivenInvalidEmailWhenCreatingCustomerViaPostThenValidationErrorIsReturned(): void

// ❌ Bad
public function testCreate(): void
```

### 2. Follow Arrange-Act-Assert pattern

```php
public function testExample(): void
{
    // Arrange - Set up test data and preconditions
    $data = [&apos;email&apos; =&gt; &apos;test@example.com&apos;];

    // Act - Execute the operation being tested
    static::createClient()-&gt;request(&apos;POST&apos;, &apos;/customers&apos;, [&apos;json&apos; =&gt; $data]);

    // Assert - Verify the results
    $this-&gt;assertResponseIsSuccessful();
}
```

### 3. Test one thing per test

```php
// ✅ Good - Tests one specific validation rule
public function testGivenMissingEmailWhenCreatingCustomerThenValidationErrorIsReturned(): void
{
    static::createClient()-&gt;request(&apos;POST&apos;, &apos;/customers&apos;, [&apos;json&apos; =&gt; []]);
    $this-&gt;assertJsonContains([&apos;violations&apos; =&gt; [[&apos;propertyPath&apos; =&gt; &apos;email&apos;]]]);
}

// ❌ Bad - Tests multiple unrelated things
public function testCustomerCreation(): void
{
    // Tests validation, creation, retrieval, update all in one test
}
```

### 4. Use meaningful test data

```php
// ✅ Good
$customerData = [
    &apos;email&apos; =&gt; &apos;john.doe@example.com&apos;,  // Realistic email
    &apos;firstName&apos; =&gt; &apos;John&apos;,               // Realistic name
    &apos;lastName&apos; =&gt; &apos;Doe&apos;,
];

// ❌ Bad
$customerData = [
    &apos;email&apos; =&gt; &apos;a@b.c&apos;,    // Not realistic
    &apos;firstName&apos; =&gt; &apos;x&apos;,     // Not meaningful
    &apos;lastName&apos; =&gt; &apos;y&apos;,
];
```

### 5. Clean up test data appropriately

```php
// For Backend API tests - use tester helpers for setup
$customer = $this-&gt;tester-&gt;haveCustomer([&apos;email&apos; =&gt; &apos;test@example.com&apos;]);

// Cleanup happens automatically via test kernel shutdown
```

### 6. Test error cases

```php
// Always test both success and failure scenarios
public function testSuccessfulCreation(): void { /* ... */ }
public function testValidationErrors(): void { /* ... */ }
public function testDuplicateEmail(): void { /* ... */ }
public function testNotFound(): void { /* ... */ }
```

### 7. Use constants for repeated values

```php
class CustomersBackendApiTest extends BackendApiTestCase
{
    private const TEST_EMAIL = &apos;test@example.com&apos;;
    private const TEST_FIRST_NAME = &apos;John&apos;;

    public function testExample(): void
    {
        $data = [
            &apos;email&apos; =&gt; self::TEST_EMAIL,
            &apos;firstName&apos; =&gt; self::TEST_FIRST_NAME,
        ];
        // ...
    }
}
```

### 8. Group related tests

```php
/**
 * @group PyzTest
 * @group Glue
 * @group Customer
 * @group BackendApi
 * @group CustomersBackendApiTest
 * @group ValidationTests
 */
class CustomersBackendApiTest extends BackendApiTestCase
{
    // Run only validation tests:
    // vendor/bin/codecept run -g ValidationTests
}
```

## Troubleshooting

### Generated resources not found

**Problem:** Test fails with &quot;Class not found&quot; for generated resource.

**Solution:**

1. Verify autoload configuration in `composer.json`:

```json
{
    &quot;autoload-dev&quot;: {
        &quot;psr-4&quot;: {
            &quot;PyzTest\\&quot;: &quot;tests/PyzTest/&quot;,
            &quot;Generated\\TestApi\\&quot;: &quot;tests/_data/Api/&quot;
        }
    }
}
```

2. Run composer dump-autoload:

```bash
docker/sdk cli composer dump-autoload
```

### Test kernel boot failures

**Problem:** Tests fail with kernel boot errors.

**Solution:**

Ensure your test case extends the correct base class:

```php
// For Backend API
use PyzTest\Shared\ApiPlatform\Test\BackendApiTestCase;

class CustomersBackendApiTest extends BackendApiTestCase
{
    // ...
}

// For Storefront API
use PyzTest\Shared\ApiPlatform\Test\StorefrontApiTestCase;

class CustomersStorefrontApiTest extends StorefrontApiTestCase
{
    // ...
}
```

### Assertion failures with JSON-LD

**Problem:** JSON assertions fail with `@context` or `@type` fields.

**Solution:**

Use JSON-LD specific assertions:

```php
// ✅ Correct
$this-&gt;assertJsonContains([&apos;@type&apos; =&gt; &apos;Customer&apos;]);
$this-&gt;assertJsonContains([&apos;@context&apos; =&gt; &apos;/contexts/Customer&apos;]);

// ❌ Wrong
$this-&gt;assertJsonContains([&apos;type&apos; =&gt; &apos;Customer&apos;]);
```

### Tester helper not found

**Problem:** `$this-&gt;tester` property shows as undefined.

**Solution:**

1. Verify your tester class exists in the correct location
2. Check that the tester is properly type-hinted in your test:

```php
class CustomersBackendApiTest extends BackendApiTestCase
{
    protected BackendApiTester $tester;  // Must be declared
}
```

3. Rebuild Codeception actors:

```bash
docker/sdk cli vendor/bin/codecept build
```

## Next steps

- [API Platform Enablement](/docs/dg/dev/architecture/api-platform/enablement.html) - Creating API resources
- [Resource Schemas](/docs/dg/dev/architecture/api-platform/resource-schemas.html) - Resource schema reference
- [Validation Schemas](/docs/dg/dev/architecture/api-platform/validation-schemas.html) - Validation schema reference
- [Troubleshooting](/docs/dg/dev/architecture/api-platform/troubleshooting.html) - Common issues and solutions
- [Codeception Documentation](https://codeception.com/docs/Introduction) - Codeception framework docs
- [API Platform Testing](https://api-platform.com/docs/symfony/testing/) - Official API Platform testing guide
</description>
            <pubDate>Tue, 19 May 2026 12:29:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/architecture/api-platform/testing.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/architecture/api-platform/testing.html</guid>
            
            
        </item>
        
        <item>
            <title>Resource Schemas</title>
            <description>This document explains how to define API Platform resource schemas in Spryker.

## Schema file structure

API Platform uses YAML files to define resource schemas. Resource schemas describe the structure, operations, and behavior of your API resources.

### Schema location

Resource schemas must be placed in the `resources/api/{api-type}/` directory within your module:

```MARKDOWN
src/
├── Spryker/
│   └── {Module}/
│       └── resources/
│           └── api/
│               ├── storefront/
│               │   └── resource-name.resource.yml
│               └── backend/
│                   └── resource-name.resource.yml
├── SprykerFeature/
│   └── {Feature}/
│       └── resources/
│           └── api/
│               └── backend/
│                   └── resource-name.resource.yml
└── Pyz/
    └── Glue/
        └── {Module}/
            └── resources/
                └── api/
                    └── backend/
                        └── resource-name.resource.yml
```

## CodeBucket resources

API Platform supports CodeBucket-specific resource variants that are resolved at runtime based on the `APPLICATION_CODE_BUCKET` environment constant. This enables Code Bucket-specific API resources without requiring separate container compilations.

### CodeBucket schema file naming

Resource schemas follow the pattern: `{resource-name}.resource.yml`
Validation schemas follow the pattern: `{resource-name}.validation.yml`

CodeBuckets are specified inside the schema files, not in the filename.

```MARKDOWN
src/Pyz/Glue/StoresApi/resources/api/backend/
├── stores.resource.yml              # Resource schema (CodeBucket variants defined inside)
└── stores.validation.yml            # Validation schema (CodeBucket variants defined inside)
```

### Generated class naming

The generator creates classes following the pattern: `{ResourceName}{CodeBucket}{ApiType}Resource`

| Schema File | CodeBucket (defined in file) | Generated Class | CODE_BUCKET Constant |
|-------------|------------------------------|----------------|---------------------|
| `stores.resource.yml` | None (base) | `StoresBackendResource` | Not present (base resource) |
| `stores.resource.yml` | EU | `StoresEUBackendResource` | `&apos;EU&apos;` |
| `stores.resource.yml` | AT | `StoresATBackendResource` | `&apos;AT&apos;` |

### How CodeBucket resolution works

1. **Schema definition**: Define CodeBucket variants inside schema files using `codeBucket: EU`
2. **Constant generation**: Generator adds `public const string CODE_BUCKET = &apos;EU&apos;;` to variant classes
3. **Runtime resolution**: System reads `APPLICATION_CODE_BUCKET` and selects matching resource class
4. **Graceful fallback**: If no matching variant exists, base resource is used

### URL consistency

All CodeBucket variants share the same URL path, with the Code Bucket defined in the domain:

- EU variant: `glue-backend.eu.spryker.local/stores` → `StoresEUBackendResource`
- AT variant: `glue-backend.at.spryker.local/stores` → `StoresATBackendResource`
- DE variant: `glue-backend.de.spryker.local/stores` → `StoresBackendResource` (or `StoresDEBackendResource` if variant exists)

The URL path is identical (`/stores`), but the Code Bucket in the domain determines which resource variant is used. Only properties, validations, and business logic differ between variants.

### When to use CodeBucket resources

Use CodeBucket variants when you need:
- Code Bucket-specific properties (EU GDPR fields, tax rates)
- Code Bucket-specific validation rules
- Country-specific business logic
- Feature variations per Code Bucket

For a comprehensive guide including implementation examples, see [CodeBucket Support](/docs/dg/dev/architecture/api-platform/code-buckets.html).

## Resource schema syntax

### Minimal example

```yaml
resource:
  name: Products
  shortName: products
  description: &quot;Product resource&quot;

  operations:
    - type: Get
    - type: GetCollection

  properties:
    id:
      type: integer
      writable: false
      identifier: true

    name:
      type: string
```

{% info_block infoBox &quot;shortName convention&quot; %}

`shortName` is the JSON:API `type` field for the resource and is used as the public URL segment. Use **lowercase kebab-case**, plural for noun-style resources (`products`, `addresses`, `abstract-product-prices`) and singular for action-style endpoints (`catalog-search`, `cart-reorder`). Multi-word names are always hyphenated. This matches every shipped resource in the platform.

{% endinfo_block %}

### Complete example with all options

```yaml
# yaml-language-server: $schema=../../../../../vendor/spryker/api-platform/resources/schemas/api-resource-schema-v1.json

resource:
  # Resource identification
  name: Customers                    # Internal name (used for schema merging)
  shortName: customers               # URL name (becomes /customers); JSON:API type field
  description: &quot;Customer resource&quot;   # OpenAPI description

  # State providers and processors
  provider: &quot;Pyz\\Glue\\Customer\\Api\\Backend\\Provider\\CustomerBackendProvider&quot;
  processor: &quot;Pyz\\Glue\\Customer\\Api\\Backend\\Processor\\CustomerBackendProcessor&quot;

  # Pagination configuration
  paginationEnabled: true
  paginationItemsPerPage: 10
  paginationMaximumItemsPerPage: 100
  paginationClientEnabled: true
  paginationClientItemsPerPage: true

  # Security
  security: &quot;is_granted(&apos;ROLE_ADMIN&apos;)&quot;
  securityPostDenormalize: &quot;is_granted(&apos;EDIT&apos;, object)&quot;

  # Operations
  operations:
    - type: Post                     # Create new resource
    - type: Get                      # Get single resource
    - type: GetCollection            # Get collection with pagination
    - type: Put                      # Replace entire resource
    - type: Patch                    # Update partial resource
    - type: Delete                   # Delete resource

  # Relationships — see Relationships article for full reference
  includes:
    - relationshipName: addresses
      targetResource: CustomersAddresses
      uriVariableMappings:
        customerReference: customerReference

  # Properties
  properties:
    idCustomer:
      type: integer
      description: &quot;The unique identifier of the customer.&quot;
      writable: false                # Read-only property
      readable: true                 # Include in responses (default: true)

    email:
      type: string
      description: &quot;The email address.&quot;
      required: true                 # Required for all operations
      openapiContext:
        example: &quot;john@example.com&quot;
        format: &quot;email&quot;

    firstName:
      type: string
      description: &quot;First name.&quot;
      openapiContext:
        example: &quot;John&quot;
        minLength: 1
        maxLength: 100

    status:
      type: string
      description: &quot;Customer status.&quot;
      openapiContext:
        example: &quot;active&quot;
        schema:
          enum: [&quot;active&quot;, &quot;inactive&quot;, &quot;pending&quot;]

    customerReference:
      type: string
      description: &quot;Unique customer reference.&quot;
      writable: false
      identifier: true               # Use as URL identifier instead of @id

    dateOfBirth:
      type: string
      description: &quot;Date of birth.&quot;
      openapiContext:
        format: &quot;date&quot;
        example: &quot;1990-01-01&quot;

    isActive:
      type: boolean
      description: &quot;Active status.&quot;
      default: true

    creditLimit:
      type: number
      description: &quot;Credit limit.&quot;
      openapiContext:
        format: &quot;float&quot;
        example: 5000.00
```

## Property types

### Supported types

| Type | PHP Type | Example | Description |
|------|----------|---------|-------------|
| `string` | `string` | `&quot;John&quot;` | Text values |
| `integer` | `int` | `42` | Whole numbers |
| `number` | `float` | `3.14` | Decimal numbers |
| `boolean` | `bool` | `true` | True/false values |
| `array` | `array` | `[&quot;a&quot;, &quot;b&quot;]` | Lists of values |
| `object` | `object` | `{&quot;key&quot;: &quot;value&quot;}` | Strictly typed nested objects |
| `map` | `array` | `{&quot;key&quot;: &quot;value&quot;}` | Free-shape associative payloads documented via `openapiContext`. Stored as PHP `array` and rendered as `type: object` in the OpenAPI specification. |
| `mixed` | `mixed` | any | Use only when the payload genuinely has no fixed shape and cannot be described via `openapiContext`. |

Use `map` when the payload is a structured JSON object whose schema you want to describe via
`openapiContext` rather than a strongly typed PHP class. This is the recommended type whenever a
request or response body is a JSON object with a known shape but no dedicated DTO class — it
keeps the property typed as a simple `array` in PHP while still producing rich OpenAPI metadata
and a working &quot;Try Out&quot; body in Swagger UI. See
[Documenting nested properties for OpenAPI and Swagger UI](#documenting-nested-properties-for-openapi-and-swagger-ui)
for the full pattern.

### Property attributes

#### writable

Controls if property can be sent in requests (POST/PUT/PATCH):

```yaml
password:
  type: string
  writable: true    # Can be sent in requests
  readable: false   # Not included in responses
```

#### readable

Controls if property is included in responses:

```yaml
idCustomer:
  type: integer
  writable: false   # Cannot be modified
  readable: true    # Included in responses
```

#### identifier

Marks property as URL identifier:

```yaml
customerReference:
  type: string
  identifier: true  # URL becomes /customers/{customerReference}
```

#### required

Makes property mandatory (use validation schemas for detailed rules):

```yaml
email:
  type: string
  required: true    # Must be present
```

#### default

Sets default value:

```yaml
isActive:
  type: boolean
  default: true     # Defaults to true if not provided
```

## Documenting nested properties for OpenAPI and Swagger UI

Many endpoints accept or return structured JSON payloads — for example, a payment initialization
request that takes `payment`, `quote`, and `customer` sub-objects. Without explicit metadata,
those payloads appear as opaque `object` entries in the OpenAPI document, which means:

- The generated OpenAPI specification does not describe the child fields, their types, or which
  ones are required.
- The Swagger UI &quot;Try Out&quot; button shows an empty request body, forcing consumers to read code or
  external documentation to discover the expected shape.

The `map` property type combined with nested `openapiContext` entries closes both gaps.

### When to use this pattern

Use this pattern when the request or response body is a structured JSON object whose schema you
want to publish through OpenAPI, but you do not want to introduce a dedicated typed PHP class
for it. Typical cases are:

- Request payloads that aggregate fields from multiple transfer objects (for example, payment
  selection plus quote context).
- PSP- or provider-specific response payloads whose shape varies by configuration.

For payloads with a stable, strongly typed shape, prefer `type: object` so the generated PHP
class enforces the structure at the language level.

### Pattern

Combine `type: map` on the property with the following entries inside `openapiContext`:

| Entry | Purpose |
|-------|---------|
| `properties` | Declares each child field with its own `type`, `description`, `format`, and `example`. Used by Swagger UI to render the field-by-field schema. |
| `required` | Lists the child fields that must be present on a request. Drives the &quot;required&quot; markers in Swagger UI and the OpenAPI specification. |
| `example` | A complete sample payload. This is the value Swagger UI prefills into the &quot;Try Out&quot; body, so consumers can execute the request immediately. |

When the property is a `map`, the generator merges `&apos;type&apos; =&gt; &apos;object&apos;` into the emitted
`openapiContext`, so the property appears as an object — with the documented schema — in the
OpenAPI document while staying as a plain PHP `array` in the generated resource class.

### Worked example

The following extract is taken from
`src/Spryker/PaymentsRestApi/resources/api/storefront/payments.resource.yml`. It shows three
common shapes: a flat request object (`payment`), a request object with nested object children
(`quote`), and a response-only object whose contents vary at runtime (`preOrderPaymentData`).

```yaml
properties:
    payment:
        type: map
        writable: true
        readable: false
        required: true
        description: &apos;Payment selection for the pre-order initialization&apos;
        openapiContext:
            required: [&apos;paymentProviderName&apos;, &apos;paymentMethodName&apos;, &apos;amount&apos;]
            properties:
                paymentProviderName:
                    type: string
                    example: &apos;DummyPayment&apos;
                paymentMethodName:
                    type: string
                    example: &apos;Invoice&apos;
                amount:
                    type: integer
                    description: &apos;Amount in minor units (cents)&apos;
                    example: 9999
            example:
                paymentProviderName: &apos;DummyPayment&apos;
                paymentMethodName: &apos;Invoice&apos;
                amount: 9999

    quote:
        type: map
        writable: true
        readable: false
        required: true
        description: &apos;Quote context required to initialize the payment&apos;
        openapiContext:
            required: [&apos;customer&apos;, &apos;billingAddress&apos;, &apos;currency&apos;]
            properties:
                customer:
                    type: object
                    required: [&apos;firstName&apos;, &apos;lastName&apos;, &apos;email&apos;]
                    properties:
                        firstName: { type: string, example: &apos;Sonia&apos; }
                        lastName: { type: string, example: &apos;Wagner&apos; }
                        email: { type: string, format: email, example: &apos;sonia@acme.com&apos; }
                billingAddress:
                    type: object
                    required: [&apos;iso2Code&apos;]
                    properties:
                        iso2Code: { type: string, example: &apos;DE&apos; }
                currency:
                    type: object
                    required: [&apos;code&apos;]
                    properties:
                        code: { type: string, example: &apos;EUR&apos; }
            example:
                customer:
                    firstName: &apos;Sonia&apos;
                    lastName: &apos;Wagner&apos;
                    email: &apos;sonia@acme.com&apos;
                billingAddress:
                    iso2Code: &apos;DE&apos;
                currency:
                    code: &apos;EUR&apos;

    preOrderPaymentData:
        type: map
        writable: false
        readable: true
        required: false
        description: &apos;PSP-specific response payload returned by the payment provider&apos;
        openapiContext:
            example:
                transactionId: &apos;tx_abc123&apos;
                redirectUrl: &apos;https://psp.example.com/pay/tx_abc123&apos;
```

### Read-only versus write-only payloads

- **Write-only request payloads** (`writable: true`, `readable: false`) should declare
  `properties`, `required`, and `example`. The first two drive request validation and the
  generated OpenAPI schema; `example` makes the Swagger UI &quot;Try Out&quot; body usable without
  edits.
- **Read-only response payloads** (`writable: false`, `readable: true`) only need
  `openapiContext.example` when the response shape is dynamic. If the response shape is fixed,
  prefer declaring `properties` (and optionally `required`) so consumers see the full schema.

### Validation note

`openapiContext.required` controls only the OpenAPI documentation. If a request field must be
enforced at runtime, add the matching constraint to the resource&apos;s validation schema — see
[Validation Schemas](/docs/dg/dev/architecture/api-platform/validation-schemas.html).

## Automatic JSON:API request body examples

For JSON:API endpoints (`application/vnd.api+json`), the generator automatically wraps property-level examples in the JSON:API envelope (`data.type` + `data.attributes`) when it builds the OpenAPI request body. You define examples once per property; the generator assembles the envelope for every write operation.

Given:

```yaml
resource:
  name: Customers
  shortName: customers   # becomes the JSON:API &quot;type&quot; field

  properties:
    email:
      type: string
      writable: true
      openapiContext:
        example: &quot;john@example.com&quot;
    firstName:
      type: string
      writable: true
      openapiContext:
        example: &quot;John&quot;
    idCustomer:
      type: integer
      writable: false      # excluded from request body example
      openapiContext:
        example: 42
```

…the generated OpenAPI request body for `POST`, `PATCH`, and `PUT` operations is:

```json
{
  &quot;data&quot;: {
    &quot;type&quot;: &quot;customers&quot;,
    &quot;attributes&quot;: {
      &quot;email&quot;: &quot;john@example.com&quot;,
      &quot;firstName&quot;: &quot;John&quot;
    }
  }
}
```

Rules the generator applies:

- The `shortName` value becomes the `type` field.
- Only **writable** properties are included — anything marked `writable: false` is filtered out (so identifiers and timestamps do not appear in the request example).
- Properties without an `openapiContext.example` are omitted from the example body.
- If no writable property has an example, no `requestBody` example is emitted at all — the operation appears without a prefilled &quot;Try Out&quot; body.

If you need a custom request body example that does not match this shape, override it at the operation level — see [Operations](#operations).

## Operations

Define which HTTP operations are available for the resource:

```yaml
operations:
  - type: Get                      # GET /customers/{id}
  - type: GetCollection            # GET /customers
  - type: Post                     # POST /customers
  - type: Put                      # PUT /customers/{id}
  - type: Patch                    # PATCH /customers/{id}
  - type: Delete                   # DELETE /customers/{id}
```

The operation names map to HTTP methods:
- `post` → POST (create)
- `get` → GET (single resource)
- `getCollection` → GET (collection)
- `put` → PUT (replace)
- `patch` → PATCH (update)
- `delete` → DELETE (remove)

## Pagination

API Platform provides built-in pagination for collection endpoints (`GetCollection`). You can configure pagination behavior per resource using YAML schema options.

### Pagination options

| Option | Type | Description |
|--------|------|-------------|
| `paginationEnabled` | `boolean` | Enables or disables pagination for this resource. When `false`, `GetCollection` returns all results without pagination. Default: inherits from global configuration. |
| `paginationItemsPerPage` | `integer` | Number of items returned per page. Overrides the global default. |
| `paginationMaximumItemsPerPage` | `integer` | Maximum number of items a client can request per page via `itemsPerPage` query parameter. Prevents clients from requesting excessively large pages. |
| `paginationClientEnabled` | `boolean` | Allows clients to enable or disable pagination via the `pagination` query parameter (for example, `?pagination=false`). |
| `paginationClientItemsPerPage` | `boolean` | Allows clients to set the number of items per page via the `itemsPerPage` query parameter (for example, `?itemsPerPage=50`). |

The global default for `paginationItemsPerPage` is defined in the project&apos;s `api_platform.php` configuration file. To override it for a specific resource, set `paginationItemsPerPage` in the resource schema.

### Minimal pagination example

```yaml
resource:
  name: Products
  shortName: products

  paginationEnabled: true
  paginationItemsPerPage: 10

  operations:
    - type: GetCollection
```

### Full pagination example

```yaml
resource:
  name: Products
  shortName: products

  paginationEnabled: true
  paginationItemsPerPage: 20
  paginationMaximumItemsPerPage: 100
  paginationClientEnabled: true
  paginationClientItemsPerPage: true

  operations:
    - type: GetCollection
    - type: Get
```

With this configuration, clients can use the following query parameters:

```bash
# Default pagination (20 items per page)
GET /products

# Navigate to page 3
GET /products?page=3

# Request 50 items per page (up to maximum of 100)
GET /products?itemsPerPage=50

# Disable pagination to get all results
GET /products?pagination=false
```

### Generated output

The pagination options are rendered as named parameters in the `#[ApiResource]` attribute:

```php
#[ApiResource(
    operations: [new GetCollection(), new Get()],
    shortName: &apos;products&apos;,
    provider: ProductsBackendProvider::class,
    paginationItemsPerPage: 20,
    paginationEnabled: true,
    paginationMaximumItemsPerPage: 100,
    paginationClientEnabled: true,
    paginationClientItemsPerPage: true
)]
```

### Provider requirements

For pagination to work, your Provider must return a `TraversablePaginator` instance for collection operations:

```php
use ApiPlatform\State\Pagination\TraversablePaginator;

return new TraversablePaginator(
    new \ArrayObject($resources),
    $currentPage,
    $itemsPerPage,
    $totalItems
);
```

If `paginationEnabled` is `true` but the Provider returns a plain array, API Platform wraps the result in a `PartialPaginatorInterface`, which may not include total count or page metadata.

### Global pagination defaults

Global pagination defaults can be configured in the application configuration file. Per-resource settings override the global defaults. See [API Platform Configuration](/docs/dg/dev/architecture/api-platform/configuration.html) for details.

## Relationships

Define relationships between resources to enable including related resources via the `?include=` query parameter.

### includes section

Declares what relationships this resource can include. Each entry describes one relationship that clients can request via the `?include=` query parameter on this resource.

```yaml
includes:
  - relationshipName: addresses
    targetResource: CustomersAddresses
    uriVariableMappings:
      customerReference: customerReference
```

**Entry fields:**

| Field | Required | Description |
|-------|----------|-------------|
| `relationshipName` | Yes | Name used in the `?include=` parameter and as the JSON:API relationship key. |
| `targetResource` | Yes | The `name` of the included resource as declared in its `resource.yml` (for example, `CustomersAddresses`). |
| `uriVariableMappings` | Conditional | Maps properties from the parent resource to the URI variables of the included resource. Required when the included resource is routed by URI variables. Format: `parentProperty: childUriVariable`. |
| `uriTemplate` | Optional | Explicit URI template for the included resource when it has multiple operations and the relationship must target a specific path (for example, `/abstract-products/{abstractProductSku}/abstract-product-prices`). |
| `resolverClass` | Optional | Fully qualified class name of a relationship resolver. Use when the relationship cannot be expressed via URI variables — the resolver receives the parent resources and the request context, and returns the related resources directly. When `resolverClass` is set, `uriVariableMappings` and `uriTemplate` are not used for routing. |

#### URI-variable mapping example

For relationships routed by sub-resource URLs, map parent properties to child URI variables:

```yaml
includes:
  - relationshipName: abstract-product-prices
    targetResource: AbstractProductPrices
    uriTemplate: /abstract-products/{abstractProductSku}/abstract-product-prices
    uriVariableMappings:
      sku: abstractProductSku
```

#### Resolver-based example

For relationships whose targets cannot be derived from URI variables (for example, derived from order state or aggregated across multiple sources), reference a resolver class:

```yaml
includes:
  - relationshipName: order-shipments
    targetResource: OrderShipments
    resolverClass: Spryker\Glue\ShipmentsRestApi\Api\Storefront\Relationship\OrderShipmentsRelationshipResolver
```

### includableIn section

Declares where this resource can be included:

```yaml
includableIn:
  - resource: Customers
    relationshipName: addresses
    uriVariableMappings:
      customerReference: customerReference
```

Both declarations must match for validation to pass.

**Further reading:** [Relationships](/docs/dg/dev/architecture/api-platform/relationships.html) — full reference for declaring, resolving, and troubleshooting relationships between API Platform resources.

## Resource generation process

### Generation workflow

The resource generation process is organized into distinct phases, each producing result objects for comprehensive error tracking and reporting:

```MARKDOWN
1. Preparation Phase
   ↓
2. Schema Parsing Phase → ParseResult
   - Load validation schemas
   - Parse validation rules
   - Load resource schemas
   - Parse resource definitions
   ↓
3. Schema Merging Phase → MergeResult
   - Merge schemas (Core → Feature → Project)
   - Track contributing source files
   ↓
4. Validation Phase → ValidationResult
   - Validate merged schemas
   - Apply validation rules
   ↓
5. Code Generation Phase
   - Generate PHP resource classes
   - Write files to output directory
   ↓
6. Cache Update
```

### Result objects

Each phase produces result objects that encapsulate both successful outcomes and failures:

- **ParseResult**: Contains grouped schemas and tracks failed validation files and schema files that could not be parsed
- **MergeResult**: Contains successfully merged schemas and tracks resources that failed to merge
- **ValidationResult**: Contains validated schemas and tracks resources that failed validation with detailed error messages

This structured approach ensures that errors in one resource do not block the generation of other valid resources, and provides clear feedback about what succeeded and what failed.

### Multi-layer schema merging

Spryker automatically merges schemas from multiple layers:

**Core layer** (lowest priority):

**vendor/spryker/customer/resources/api/backend/customer.resource.yml**

```yaml
resource:
  name: Customers
  properties:
    email:
      type: string
    firstName:
      type: string
```

**Feature layer** (medium priority):

**src/SprykerFeature/CRM/resources/api/backend/customer.resource.yml**

```yaml
resource:
  name: Customers
  properties:
    phone:
      type: string      # Added property
```

**Project layer** (highest priority):

**src/Pyz/Glue/Customer/resources/api/backend/customer.resource.yml**

```yaml
resource:
  name: Customers
  properties:
    email:
      required: true    # Override core definition
    customField:
      type: string      # Project-specific field
```

**Merged result:**

```yaml
resource:
  name: Customers
  properties:
    email:
      type: string
      required: true    # From project layer
    firstName:
      type: string      # From core layer
    phone:
      type: string      # From feature layer
    customField:
      type: string      # From project layer
```

### Generated resource class

The generator creates a complete PHP class with API Platform attributes:

```php
&lt;?php

declare(strict_types=1);
namespace Generated\Api\Backend;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\ApiProperty;
use Symfony\Component\Validator\Constraints as Assert;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Delete;

#[ApiResource(
    operations: [new Post(), new Get(), new GetCollection(), new Patch(), new Delete()],
    shortName: &apos;customers&apos;,
    provider: CustomerBackendProvider::class,
    processor: CustomerBackendProcessor::class,
    paginationItemsPerPage: 10,
    paginationEnabled: true,
    paginationMaximumItemsPerPage: 100,
    paginationClientEnabled: true,
    paginationClientItemsPerPage: true
)]
final class CustomersBackendResource
{
    #[ApiProperty(writable: false)]
    public ?int $idCustomer = null;

    #[ApiProperty(openapiContext: [&apos;example&apos; =&gt; &apos;john@example.com&apos;])]
    #[Assert\NotBlank(groups: [&apos;customers:create&apos;])]
    #[Assert\Email(groups: [&apos;customers:create&apos;])]
    public ?string $email = null;

    #[ApiProperty(identifier: true, writable: false)]
    public ?string $customerReference = null;

    public ?bool $isActive = true;

    // Getters, setters, toArray(), fromArray() methods...
}
```

## Debugging schemas

### Debug commands

```bash
# List all resources
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:debug --list

# Show specific resource
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:debug customers --api-type=backend

# Show merged schema
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:debug customers --api-type=backend --show-merged

# Show contributing source files
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:debug customers --api-type=backend --show-sources

# Validate schemas without generating
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate --validate-only
```

### Common schema errors

The generator validates schemas and provides detailed error messages:

```bash
# Missing required fields
Error: Resource &quot;customers&quot; is missing required field &quot;name&quot;

# Invalid operation type
Error: Invalid operation type &quot;INVALID&quot;. Must be one of: Get, Post, Put, Patch, Delete, GetCollection

# Invalid property type
Error: Property &quot;age&quot; has invalid type &quot;int&quot;. Must be one of: string, integer, number, boolean, array, object

# Provider class not found
Error: Provider class &quot;Pyz\Glue\Customer\Api\Backend\Provider\MissingProvider&quot; does not exist
```

## Advanced schema features

### Custom URL paths

Operations support `uriTemplate` and `uriVariables` to define custom URL paths, including sub-resource URLs like `/customers/{customerReference}/addresses`.

#### Sub-resource with full CRUD

Define a child resource with nested URLs by adding `uriTemplate` and `uriVariables` to each operation:

**customers-addresses.resource.yml**

```yaml
resource:
  name: CustomersAddresses
  shortName: customers-addresses

  operations:
    - type: GetCollection
      uriTemplate: &apos;/customers/{customerReference}/addresses&apos;
      uriVariables:
        customerReference:
          toProperty: &apos;customer&apos;
          fromClass: CustomersStorefrontResource

    - type: Get
      uriTemplate: &apos;/customers/{customerReference}/addresses/{uuid}&apos;
      uriVariables:
        customerReference:
          toProperty: &apos;customer&apos;
          fromClass: CustomersStorefrontResource
        uuid:
          fromClass: CustomersAddressesStorefrontResource

    - type: Post
      uriTemplate: &apos;/customers/{customerReference}/addresses&apos;
      uriVariables:
        customerReference:
          toProperty: &apos;customer&apos;
          fromClass: CustomersStorefrontResource
```

**`uriVariables` properties:**
- `fromClass`: The generated resource class the variable originates from
- `toProperty`: The property on the current resource that links to the parent resource

#### Action-style sub-resource

For single-action endpoints nested under a parent resource:

**customers-confirm-registration.resource.yml**

```yaml
resource:
  name: CustomersConfirmRegistration
  shortName: customers-confirm-registration

  operations:
    - type: Post
      uriTemplate: /customers/{customerReference}/confirm-registration
```

For more details on `uriTemplate`, `uriVariables`, and sub-resource patterns, see the [API Platform sub-resources documentation](https://api-platform.com/docs/core/subresources/).

### Security expressions

Security expressions protect resources and operations using [Symfony&apos;s ExpressionLanguage](https://symfony.com/doc/current/security/expressions.html). They require the SecurityBundle to be configured. See [How to integrate API Platform Security](/docs/dg/dev/upgrade-and-migrate/integrate-api-platform-security.html) for setup instructions.

{% info_block infoBox &quot;Where roles come from&quot; %}

Roles like `ROLE_CUSTOMER` in security expressions come from OAuth scopes that are automatically mapped to Symfony roles. The mapping convention is as follows: a scope name is uppercased and prefixed with `ROLE_`. For example, the `customer` scope becomes `ROLE_CUSTOMER`.

Scopes are provided by scope provider plugins registered in `OauthDependencyProvider::getScopeProviderPlugins()`. The following table lists the out-of-the-box scope provider plugins and the scopes they provide:

| Plugin | Scopes |
|--------|--------|
| `CustomerOauthScopeProviderPlugin` | `customer` |
| `CompanyUserOauthScopeProviderPlugin` | `company_user` |
| `AgentOauthScopeProviderPlugin` | `agent` |
| `CustomerImpersonationOauthScopeProviderPlugin` | `customer_impersonation`, `customer` |
| `UserOauthScopeProviderPlugin` | `user`, plus UserType sub-plugins |
| `WarehouseOauthScopeProviderPlugin` | `warehouse` |

For details on how the mapping works, see [Security — Roles and OAuth scope mapping](/docs/dg/dev/architecture/api-platform/security.html). For instructions on setting up scopes, see [Integrate the authorization scopes](/docs/integrations/spryker-glue-api/backend-api/integrate-backend-api/integrate-the-authorization-scopes.html).

{% endinfo_block %}

Three types of security expressions are supported:

| Expression | Evaluated | Use case | When to use |
|-----------|-----------|----------|-------------|
| `security` | Before the request is processed | Check user roles or authentication status | For role or authentication checks that do not depend on the request body. |
| `securityPostDenormalize` | After the request body is deserialized | Check authorization based on submitted data | When authorization depends on the deserialized resource `object`, for example, to verify the user owns the resource being modified. |
| `securityPostValidation` | After validation passes | Check authorization based on validated data | When authorization depends on validated data, for example, to verify a value is within the user&apos;s authorized limit after validation confirms the data is structurally correct. |

#### Resource-level security

Applies to all operations on the resource:

```yaml
resource:
  name: Customers
  shortName: customers
  security: &quot;is_granted(&apos;ROLE_USER&apos;)&quot;
```

#### Operation-level security

Applies to a specific operation, overriding resource-level security:

```yaml
resource:
  name: Customers
  shortName: customers

  operations:
    - type: Post
      # No security — public registration

    - type: Get
      security: &quot;is_granted(&apos;ROLE_USER&apos;)&quot;

    - type: Patch
      security: &quot;is_granted(&apos;ROLE_USER&apos;)&quot;
```

#### Post-denormalize security

Evaluated after the request body has been deserialized. The `object` variable contains the resource instance:

```yaml
resource:
  name: Orders
  shortName: orders
  security: &quot;is_granted(&apos;ROLE_USER&apos;)&quot;
  securityPostDenormalize: &quot;is_granted(&apos;EDIT&apos;, object)&quot;
```

{% info_block infoBox &quot;Custom voter attributes&quot; %}

`EDIT` in the example is a **custom voter attribute** — it is an application-defined string, not a built-in Symfony or Spryker constant. For `is_granted(&apos;EDIT&apos;, object)` to work, you must register a custom Symfony [Voter](https://symfony.com/doc/current/security/voters.html) that supports the `EDIT` attribute and implements the authorization logic, for example, checking that the authenticated user owns the resource.

Use `securityPostDenormalize` when the authorization decision depends on the **submitted request data** (the deserialized `object`), such as verifying resource ownership.

{% endinfo_block %}

#### Post-validation security

Evaluated after validation has passed:

```yaml
resource:
  name: Payments
  shortName: payments
  securityPostValidation: &quot;is_granted(&apos;PROCESS&apos;, object)&quot;
```

{% info_block infoBox &quot;Custom voter attributes&quot; %}

`PROCESS` in the example is a **custom voter attribute** — it is an application-defined string, not a built-in Symfony or Spryker constant. For `is_granted(&apos;PROCESS&apos;, object)` to work, you must register a custom Symfony [Voter](https://symfony.com/doc/current/security/voters.html) that supports the `PROCESS` attribute.

Use `securityPostValidation` when the authorization decision depends on **validated data**, for example, to verify a payment amount is within the user&apos;s authorized limit after validation confirms the data is structurally correct.

{% endinfo_block %}

For detailed information about the authentication flow, role mapping, and accessing the authenticated user in providers, see [Security](/docs/dg/dev/architecture/api-platform/security.html).

## Generation commands

### Basic generation

```bash
# Generate all configured API types
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate

# Generate specific API type
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate backend
docker/sdk cli GLUE_APPLICATION=GLUE_STOREFRONT glue api:generate storefront

# Generate with options
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate --dry-run           # Preview without writing
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate --validate-only     # Only validate schemas
docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate --resource=customers  # Generate single resource
```

### Output

```bash
Generating API resources for ApiType: backend

Discovering schema files...
Validating schemas... OK
Merging schemas... OK

Generating resources:
 10/10 [============================] 100%

Generated: 10 file(s)
Cache updated

Done!
```

## Schema validation rules

The generator enforces these rules:

### Required fields

Every resource must have:
- `name` - Internal resource name
- `shortName` - URL-friendly name
- At least one `operation`
- At least one `property`

### Valid operation types

Only these operation types are allowed:
- `Get` - Retrieve single resource
- `GetCollection` - Retrieve collection
- `Post` - Create resource
- `Put` - Replace entire resource
- `Patch` - Update partial resource
- `Delete` - Delete resource

### Valid property types

Only these property types are allowed:
- `string`
- `integer`
- `number`
- `boolean`
- `array`
- `object`
- `map`
- `mixed`

### Provider/Processor validation

- Provider/Processor classes must exist
- Classes must implement correct interfaces
- Namespaces must be valid PHP namespaces

## Best practices

### 1. Use semantic naming

```yaml
# ✅ Good
resource:
  name: Customers              # PascalCase plural — used for schema merging
  shortName: customers         # lowercase kebab-case plural — JSON:API type + URL segment

# ✅ Good — multi-word
resource:
  name: AbstractProductPrices
  shortName: abstract-product-prices

# ❌ Bad — wrong shortName casing/form
resource:
  name: Customers
  shortName: Customer          # Should be lowercase plural

# ❌ Bad — abbreviated, unclear
resource:
  name: CustomerData
  shortName: cust
```

### 2. Document all properties

```yaml
# ✅ Good
email:
  type: string
  description: &quot;The customer&apos;s email address used for login and notifications&quot;

# ❌ Bad
email:
  type: string
```

### 3. Leverage schema merging

Core — define base properties:

**src/Spryker/Customer/resources/api/backend/customer.resource.yml**

```yaml
resource:
  name: Customers
  properties:
    email:
      type: string
```

Project — only override what is needed:

**src/Pyz/Glue/Customer/resources/api/backend/customer.resource.yml**

```yaml
resource:
  name: Customers
  properties:
    email:
      required: true  # ← Only the difference
```

### 4. Use readable/writable correctly

```yaml
# Read-only fields (IDs, timestamps)
idCustomer:
  type: integer
  writable: false

# Write-only fields (passwords)
password:
  type: string
  readable: false

# Read-write fields (normal data)
email:
  type: string
  writable: true
  readable: true
```

## Next steps

- [API Platform](/docs/dg/dev/architecture/api-platform.html) - Architecture overview
- [Validation Schemas](/docs/dg/dev/architecture/api-platform/validation-schemas.html) - Define validation rules
- [CodeBucket Support](/docs/dg/dev/architecture/api-platform/code-buckets.html) - Code Bucket-specific resources
- [API Platform Enablement](/docs/dg/dev/architecture/api-platform/enablement.html) - Creating resources
- [API Platform Testing](/docs/dg/dev/architecture/api-platform/testing.html) - Writing and running tests
- [Troubleshooting](/docs/dg/dev/architecture/api-platform/troubleshooting.html) - Common issues
- [API Platform Documentation](https://api-platform.com/docs/) - Official API Platform docs
</description>
            <pubDate>Tue, 19 May 2026 12:29:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/architecture/api-platform/resource-schemas.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/architecture/api-platform/resource-schemas.html</guid>
            
            
        </item>
        
        <item>
            <title>Relationships</title>
            <description>The API Platform relationship system enables resources to include related resources via the `?include=` query parameter in JSON:API format.

## Quick start

### 1. Define relationships in parent resource

Add an `includes` section to your parent resource YAML:

```yaml
# src/Spryker/Customer/resources/api/storefront/customers.resource.yml
resource:
  name: Customers
  shortName: customers

  includes:
    - relationshipName: addresses
      targetResource: CustomersAddresses
      uriVariableMappings:
        customerReference: customerReference
```

### 2. Define reverse relationship in child resource

Add an `includableIn` section to your child resource YAML:

```yaml
# src/Spryker/Customer/resources/api/storefront/customers-addresses.resource.yml
resource:
  name: CustomersAddresses
  shortName: customers-addresses

  includableIn:
    - resource: Customers
      relationshipName: addresses
      uriVariableMappings:
        customerReference: customerReference
```

Both declarations must match for validation to pass.

### 3. Regenerate container

```bash
docker/sdk testing -x GLUE_APPLICATION=GLUE_STOREFRONT glue cache:clear
```

### 4. Use relationships

```bash
# Single include
GET /customers/customer--35?include=addresses

# Multiple includes
GET /customers/customer--35?include=addresses,orders
```

## Configuration reference

### includes section

Declares what relationships this resource can include.

**Required properties:**
- `relationshipName`: Name used in `?include=` parameter (for example, `addresses`)
- `targetResource`: Name of the resource to include (for example, `CustomersAddresses`)

**Optional properties:**
- `uriVariableMappings`: Maps properties from parent to child provider URI variables

**Example:**

```yaml
includes:
  - relationshipName: addresses
    targetResource: CustomersAddresses
    uriVariableMappings:
      customerReference: customerReference
```

### includableIn section

Declares where this resource can be included.

**Required properties:**
- `resource`: Name of the parent resource
- `relationshipName`: Must match parent&apos;s includes declaration

**Optional properties:**
- `uriVariableMappings`: Must match parent&apos;s includes declaration

**Example:**

```yaml
includableIn:
  - resource: Customers
    relationshipName: addresses
    uriVariableMappings:
      customerReference: customerReference
```

## URI variable mapping

URI variable mapping passes context from parent resource to child provider.

**Example flow:**

1. Parent resource (Customer) has property `customerReference = &apos;DE--123&apos;`
2. Configuration maps `customerReference: customerReference`
3. Child provider receives `[&apos;customerReference&apos; =&gt; &apos;DE--123&apos;]` in URI variables
4. Child provider uses this to filter results

**Multiple mappings:**

```yaml
uriVariableMappings:
  customerReference: customerReference
  storeId: storeId
  locale: locale
```

## Auto-generated properties

When you define an `includes` relationship, the corresponding property is automatically generated with these defaults:

| Attribute | Value | Rationale |
|-----------|-------|-----------|
| `type` | `array` | Relationships are collections |
| `writable` | `false` | Relationships are read-only |
| `readable` | `true` | Must be readable for responses |
| `required` | `false` | Relationships are optional |
| `description` | `&quot;Related {targetResource} resources&quot;` | Auto-generated description |

You can override defaults by manually defining the property:

```yaml
properties:
  addresses:
    type: array
    writable: false
    readable: true
    required: false
    description: &quot;Customer billing and shipping addresses&quot;
```

## Validation

The system validates relationships during code generation:

**Bi-directional consistency:**
- Parent&apos;s `includes` must match child&apos;s `includableIn`
- Relationship names must match
- URI variable mappings must match

**Resource existence:**
- Target resource must exist
- Referenced properties should exist

**Example error:**

```text
Validation Error in customers.resource.yml:
  - includes[0].targetResource: Resource &quot;CustomersAddresses&quot; declares
    includableIn for &quot;Customers&quot; but uses different relationshipName
    &quot;customerAddresses&quot;. Expected: &quot;addresses&quot;
```

## Response format

**Request:**

```http
GET /customers/customer--35?include=addresses
```

**Response:**

```json
{
  &quot;data&quot;: {
    &quot;type&quot;: &quot;customers&quot;,
    &quot;id&quot;: &quot;customer--35&quot;,
    &quot;attributes&quot;: {
      &quot;email&quot;: &quot;john@example.com&quot;,
      &quot;firstName&quot;: &quot;John&quot;
    },
    &quot;relationships&quot;: {
      &quot;addresses&quot;: {
        &quot;data&quot;: [
          {&quot;type&quot;: &quot;addresses&quot;, &quot;id&quot;: &quot;addr-123&quot;},
          {&quot;type&quot;: &quot;addresses&quot;, &quot;id&quot;: &quot;addr-456&quot;}
        ]
      }
    }
  },
  &quot;included&quot;: [
    {
      &quot;type&quot;: &quot;addresses&quot;,
      &quot;id&quot;: &quot;addr-123&quot;,
      &quot;attributes&quot;: {
        &quot;address1&quot;: &quot;123 Test St&quot;,
        &quot;city&quot;: &quot;Test City&quot;
      }
    },
    {
      &quot;type&quot;: &quot;addresses&quot;,
      &quot;id&quot;: &quot;addr-456&quot;,
      &quot;attributes&quot;: {
        &quot;address1&quot;: &quot;456 Other St&quot;,
        &quot;city&quot;: &quot;Other City&quot;
      }
    }
  ]
}
```

## How it works

1. **RelationshipProviderDecorator** wraps all providers automatically
2. Parses `?include=` parameter from request
3. **ApiPlatformRelationshipResolver** loads relationships via container configuration
4. Maps URI variables from parent to child
5. Calls child provider with mapped variables
6. **JsonApiRelationshipNormalizer** builds JSON:API response with `relationships` and `included` sections

Providers require no code changes - the system works automatically through decoration.

## Custom relationship resolvers

The default provider-based resolution maps URI variables from the parent resource to the child provider. When that is not enough — for example, the related data lives on the parent&apos;s `context` payload, is aggregated from several sources, or needs custom denormalization — declare a resolver class instead.

Reference the resolver in the parent&apos;s `includes` entry via `resolverClass`. When `resolverClass` is set, `uriVariableMappings` and `targetResource` are not used for routing; the resolver class is invoked directly with the parent resources and request context:

```yaml
# src/Spryker/OrdersRestApi/resources/api/storefront/orders.resource.yml
resource:
  name: Orders
  shortName: orders

  includes:
    - relationshipName: order-amendments
      targetResource: OrderAmendments
      resolverClass: Spryker\Glue\OrderAmendmentsRestApi\Api\Storefront\Relationship\OrderAmendmentsRelationshipResolver
```

The resolver class must implement `Spryker\ApiPlatform\Relationship\RelationshipResolverInterface`. In practice, extend `Spryker\ApiPlatform\Relationship\AbstractRelationshipResolver`, which gives you helpers for accessing the request, locale, store, and customer transfers:

```php
namespace Spryker\Glue\OrderAmendmentsRestApi\Api\Storefront\Relationship;

use Generated\Api\Storefront\OrderAmendmentsStorefrontResource;
use Spryker\ApiPlatform\Relationship\AbstractRelationshipResolver;
use Spryker\Service\Serializer\SerializerServiceInterface;

class OrderAmendmentsRelationshipResolver extends AbstractRelationshipResolver
{
    public function __construct(protected SerializerServiceInterface $serializer)
    {
    }

    /**
     * @return array&lt;\Generated\Api\Storefront\OrderAmendmentsStorefrontResource&gt;
     */
    protected function resolveRelationship(): array
    {
        $resources = [];

        foreach ($this-&gt;getParentResources() as $orderResource) {
            $contextData = $orderResource-&gt;context ?? null;
            $amendmentData = is_array($contextData) &amp;&amp; isset($contextData[&apos;salesOrderAmendment&apos;])
                ? $contextData[&apos;salesOrderAmendment&apos;]
                : null;

            if (!is_array($amendmentData) || $amendmentData === []) {
                continue;
            }

            $resources[] = $this-&gt;serializer-&gt;denormalize(
                $amendmentData,
                OrderAmendmentsStorefrontResource::class,
            );
        }

        return $resources;
    }
}
```

The `RelationshipConfigurationPass` compiler pass registers the class as an autowired public service automatically — no manual service definition is required. If the referenced class does not exist when the container compiles, the relationship is silently skipped and a compiler log entry is emitted.

Use a custom resolver when:

- The related data is already attached to the parent (for example, embedded in a transfer&apos;s `context` array) and a separate child provider would re-fetch it unnecessarily.
- The relationship aggregates data from several sources that no single provider exposes.
- The link from parent to child cannot be expressed as a simple property-to-URI-variable mapping.

## Performance

Relationships are resolved per parent resource. For a collection of N parent resources with an `?include=` request, the child provider is called N times — one call per parent — which can produce an N+1 query pattern if the child provider hits the database per call.

When you expect collection endpoints to be requested with `?include=`, optimize the child provider:

- **Batch internally**: have the child provider detect repeated single-key lookups and coalesce them into one underlying query. For example, accept a `customerReference` URI variable but maintain an in-request cache of previously fetched results.
- **Paginate the parent**: keep parent collection page sizes small (`paginationItemsPerPage`) so the per-include cost stays bounded.
- **Profile real traffic**: enable Doctrine query logging or use Blackfire/Xdebug to confirm the N+1 hypothesis before optimizing — sometimes the parent&apos;s own query dominates and the includes are negligible.

## Troubleshooting

### Relationships are not returned

The `?include=` parameter is silently ignored or returns no `relationships` block.

Run through the following checks in order:

1. **Clear the cache.** Relationship configuration is built into the compiled container; YAML changes do not take effect until the container is rebuilt.

    ```bash
    docker/sdk cli GLUE_APPLICATION=GLUE_STOREFRONT glue cache:clear
    ```

2. **Confirm the parent declares the relationship.** Runtime resolution only reads `includes` on the parent — that declaration must be present and the names/`uriVariableMappings` must match what the request uses. A matching `includableIn` on the child is optional but recommended for discoverability; it does not affect runtime behavior. See the [Configuration reference](#configuration-reference).

3. **Inspect the compiled relationship registry.** API Platform exposes the merged configuration as a container parameter:

    ```bash
    docker/sdk cli GLUE_APPLICATION=GLUE_STOREFRONT glue debug:container --parameter=api_platform.relationships
    ```

    The output lists every registered relationship keyed by `{parentResource}.{relationshipName}` (for example, `customers.addresses`). If your relationship is missing, the YAML was not picked up — re-check file location and run `api:generate`.

4. **Verify the child provider is registered.** The child resource needs a provider that API Platform can resolve:

    ```bash
    docker/sdk cli GLUE_APPLICATION=GLUE_STOREFRONT glue debug:container | grep &lt;ChildProviderClass&gt;
    ```

### Validation error: bi-directional consistency

Resource generation fails with an error like:

```text
Validation Error in customers.resource.yml:
  - includes[0].targetResource: Resource &quot;CustomersAddresses&quot; declares includableIn
    for &quot;Customers&quot; but uses different relationshipName &quot;customerAddresses&quot;
    Expected: &quot;addresses&quot;
```

The parent&apos;s `includes[].relationshipName` and the child&apos;s `includableIn[].relationshipName` must be identical strings. The same applies to `uriVariableMappings` — every mapping declared on the parent must appear on the child with the same source/target names.

### `relationships` block is present but `data` is empty

The relationship is wired up but no related resources come back.

1. **The child provider is returning `null` or `[]`.** Call the child provider directly (or hit its standalone collection endpoint with the same URI variable values) to confirm it returns data.
2. **URI variable mapping does not produce a value.** A property on the parent that resolves to `null` is omitted from the URI variables passed to the child — verify the mapped property is populated on every parent resource in the response. Use `api:debug &lt;resource&gt; --show-merged` to confirm the property is declared.
3. **The child filters too aggressively.** Inspect the child provider&apos;s filtering logic with the URI variable values produced by the mapping.

### Invalid include names are ignored

Unknown values in `?include=` (for example, a typo or a relationship the parent does not declare) are silently dropped — the response succeeds without that relationship and no error is raised. If a deployment appears to lose a relationship after a release, suspect a typo or a missing `includableIn` in the child before assuming a runtime failure.
</description>
            <pubDate>Tue, 19 May 2026 12:29:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/architecture/api-platform/relationships.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/architecture/api-platform/relationships.html</guid>
            
            
        </item>
        
        <item>
            <title>IDE integration for API Platform schemas</title>
            <description>This guide shows how to enable IDE support for the YAML files that define API Platform resources (`*.resource.yml`) and their validation rules (`*.validation.yml`). Once configured, your IDE provides:

- **Autocomplete** for resource properties, operation types, and validation constraints.
- **Real-time validation** against the JSON Schema, flagging invalid configurations as you type.
- **Inline documentation** for every property and attribute.
- **Type safety**: values are checked against the declared types.

## Prerequisites

The `ApiPlatform` module ships the canonical JSON Schema at:

```text
vendor/spryker/api-platform/resources/schemas/api-resource-schema-v1.json
```

A hosted copy is also available at `https://static.spryker.com/api-resource-schema-v1.json`.

## Configure PHPStorm

PHPStorm has built-in JSON Schema support — no plugin is required.

1. Open **Settings &gt; Languages &amp; Frameworks &gt; Schemas and DTDs &gt; JSON Schema Mappings**.
2. Click the **+** to add a new mapping with:
    - **Name:** `Spryker API Resource Schema`
    - **Schema file or URL:** `$PROJECT_DIR$/vendor/spryker/api-platform/resources/schemas/api-resource-schema-v1.json` (or the hosted URL above).
    - **Schema version:** `JSON Schema version 7`
3. Under **File path pattern**, add `**/resources/api/*/*.resource.yml`. Add additional patterns if your project keeps resource YAMLs in other locations.
4. Apply and open any `*.resource.yml` file. Typing `resource:` should now produce autocomplete suggestions. Press `Ctrl+Q` (Windows/Linux) or `F1` (macOS) on any property to see its documentation.

## Configure VSCode

VSCode requires the [Red Hat YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml).

1. Install the extension from the Extensions panel (`Ctrl+Shift+X` / `Cmd+Shift+X`).
2. Add the schema mapping to your project&apos;s `.vscode/settings.json`:

    ```json
    {
      &quot;yaml.schemas&quot;: {
        &quot;./vendor/spryker/api-platform/resources/schemas/api-resource-schema-v1.json&quot;: [
          &quot;**/resources/api/*/*.resource.yml&quot;
        ]
      }
    }
    ```

3. Reload the window (`Developer: Reload Window` from the command palette) and open a `*.resource.yml` file. Hover over any property to see its documentation; invalid values are underlined.

{% info_block infoBox &quot;Workspace settings vs. user settings&quot; %}

Project-level `.vscode/settings.json` is recommended so the configuration travels with the repository. The same `yaml.schemas` block also works in user settings if you prefer a global setup.

{% endinfo_block %}

## Per-file schema reference

You can also pin the schema inline at the top of any YAML file with the `yaml-language-server` directive. This is useful for one-off files that live outside the configured patterns:

```yaml
# yaml-language-server: $schema=../../../../../vendor/spryker/api-platform/resources/schemas/api-resource-schema-v1.json

resource:
  name: Customers
  shortName: customers
  description: Customer resource for the storefront API
  operations:
    - type: Get
    - type: GetCollection
  properties:
    idCustomer:
      type: integer
      description: The unique identifier
      identifier: true
      writable: false
```

The path is relative to the YAML file&apos;s location. Adjust the depth (`../`) accordingly, or use the hosted URL if your environment can reach it.

## Quick reference

The JSON Schema covers the full resource and validation surface. The most common fields:

### Resource root

| Field | Required | Description |
|---|---|---|
| `shortName` | yes | Resource identifier used in URLs. |
| `name` | no | Full resource name (defaults to `shortName`). |
| `description` | no | Human-readable description. |
| `operations` | yes | One or more operations exposed by the resource. |
| `properties` | yes | The resource&apos;s properties. |
| `provider` | no | Fully qualified Provider class name. |
| `processor` | no | Fully qualified Processor class name. |
| `paginationEnabled` | no | Enables pagination for collection operations. |
| `paginationItemsPerPage` | no | Default page size. |

### Operation types

`Get`, `GetCollection`, `Post`, `Put`, `Patch`, `Delete`.

### Property types

`string`, `integer`, `boolean`, `array`, `object`, `mixed`. Short aliases `int`, `bool`, `str`, and `arr` are accepted and normalized.

### Property attributes

| Attribute | Description |
|---|---|
| `type` | One of the property types above. Required. |
| `identifier` | Marks the property as the resource identifier. At least one identifier per resource is required. |
| `required` | Required when creating the resource. |
| `writable` | Whether the property can be written via the API (default: `true`). |
| `readable` | Whether the property can be read via the API (default: `true`). |
| `openapiContext` | Additional OpenAPI attributes (example, format, etc.). |

For the full attribute list, see the [resource schemas reference](/docs/dg/dev/architecture/api-platform/resource-schemas.html).

## Troubleshooting

### Autocomplete or validation not active

- **Verify the schema path.** Open the schema file from the IDE to confirm it exists and is valid JSON.
- **Verify the file pattern.** PHPStorm and VSCode both require the YAML file&apos;s path to match the configured pattern. The default `**/resources/api/*/*.resource.yml` covers the standard Spryker layout (`src/{Org}/{Module}/resources/api/{api-type}/`).
- **Restart the IDE.** PHPStorm: `File &gt; Invalidate Caches / Restart`. VSCode: run `Developer: Reload Window` from the command palette.
- **Check the extension** (VSCode only): ensure the Red Hat YAML extension is installed and enabled.

### False validation errors

- **Property names** must match `^[a-zA-Z_][a-zA-Z0-9_]*$`. Hyphens and other special characters are rejected.
- **Booleans** must be lowercase (`true` / `false`) in YAML.
- **Types** must be one of the allowed values or aliases listed above. Custom strings are rejected.

### Schema changes not reflected

- **PHPStorm:** `File &gt; Invalidate Caches / Restart &gt; Invalidate and Restart`.
- **VSCode:** `Developer: Reload Window`. If the schema is referenced via a hosted URL, also disable any local HTTP cache or switch to the local file path while iterating.
</description>
            <pubDate>Tue, 19 May 2026 12:29:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/architecture/api-platform/ide-integration.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/architecture/api-platform/ide-integration.html</guid>
            
            
        </item>
        
        <item>
            <title>API Platform Enablement</title>
            <description>&lt;p&gt;This document describes how to create and enable API Platform resources in your Spryker project.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before creating API resources, ensure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Integrated API Platform as described in &lt;a href=&quot;/docs/dg/dev/upgrade-and-migrate/integrate-api-platform.html&quot;&gt;How to integrate API Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Configured your application’s bundle files&lt;/li&gt;
&lt;li&gt;Configured API types as described in &lt;a href=&quot;/docs/dg/dev/architecture/api-platform/configuration.html&quot;&gt;API Platform Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;creating-your-first-api-resource&quot;&gt;Creating your first API resource&lt;/h2&gt;
&lt;h3 id=&quot;define-the-resource-schema&quot;&gt;1. Define the resource schema&lt;/h3&gt;
&lt;p&gt;Create a schema file that defines your API resource structure. Schemas should be placed in &lt;code&gt;resources/api/{api-type}/&lt;/code&gt; directory within your module.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example: Customer resource for Back Office API&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/Customer/resources/api/backend/customers.resource.yml&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# yaml-language-server: $schema=../../../../../vendor/spryker/api-platform/resources/schemas/api-resource-schema-v1.json&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Customers&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;shortName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;customers&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API&quot;&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CustomerBackendProvider&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Processor&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CustomerBackendProcessor&quot;&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;paginationEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paginationItemsPerPage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Post&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Get&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GetCollection&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Patch&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Delete&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;idCustomer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;integer&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;customer.&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;

        &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;contact.&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;openapiContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;test@spryker.com&quot;&lt;/span&gt;

        &lt;span class=&quot;na&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;person.&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;openapiContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;John&quot;&lt;/span&gt;

        &lt;span class=&quot;na&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;customer.&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;openapiContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Doe&quot;&lt;/span&gt;

        &lt;span class=&quot;na&quot;&gt;customerReference&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;reference&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;customer.&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;create-validation-schema&quot;&gt;2. Create validation schema&lt;/h3&gt;
&lt;p&gt;Define validation rules in a separate validation schema file:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/Customer/resources/api/backend/customers.validation.yml&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Email&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Email&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;implement-the-provider&quot;&gt;3. Implement the Provider&lt;/h3&gt;
&lt;p&gt;The Provider is responsible for fetching data (GET operations). Implement the &lt;code&gt;ProviderInterface&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/Customer/Api/Backend/Provider/CustomerBackendProvider.php&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pyz\Glue\Customer\Api\Backend\Provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\Metadata\Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\State\Pagination\TraversablePaginator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\State\ProviderInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pyz\Glue\Customer\Business\CustomerFacadeInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Generated\Api\Backend\CustomersBackendResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerBackendProvider&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProviderInterface&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomerFacadeInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerFacade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;cd&quot;&gt;/**
     * @param \ApiPlatform\Metadata\Operation $operation
     * @param array&amp;lt;string, mixed&amp;gt; $uriVariables
     * @param array&amp;lt;string, mixed&amp;gt; $context
     *
     * @return object|array&amp;lt;object&amp;gt;|null
     */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Single resource (GET /customers/{id})&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;customerReference&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;customerReference&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Collection (GET /customers)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getCustomers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;?CustomersBackendResource&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customerFacade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;findCustomerByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$customerReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Map to API resource&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomersBackendResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getCustomers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TraversablePaginator&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$filters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;filters&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;page&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$itemsPerPage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;itemsPerPage&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$customerCollection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customerFacade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getCustomerCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$resources&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$customerCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getCustomers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomersBackendResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

            &lt;span class=&quot;nv&quot;&gt;$resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TraversablePaginator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ArrayObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$customerCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getTotalCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;implement-the-processor&quot;&gt;4. Implement the Processor&lt;/h3&gt;
&lt;p&gt;The Processor handles data modifications (POST, PUT, PATCH, DELETE). Implement the &lt;code&gt;ProcessorInterface&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/Customer/Api/Backend/Processor/CustomerBackendProcessor.php&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pyz\Glue\Customer\Api\Backend\Processor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\Metadata\Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\Metadata\Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\Metadata\Patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\Metadata\Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\State\ProcessorInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pyz\Glue\Customer\Business\CustomerFacadeInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Generated\Api\Backend\CustomersBackendResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerBackendProcessor&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProcessorInterface&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomerFacadeInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerFacade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;cd&quot;&gt;/**
     * @param mixed $data
     * @param \ApiPlatform\Metadata\Operation $operation
     * @param array&amp;lt;string, mixed&amp;gt; $uriVariables
     * @param array&amp;lt;string, mixed&amp;gt; $context
     *
     * @return mixed
     */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;mixed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;mixed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customerFacade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deleteCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;customerReference&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mapToTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$savedCustomer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customerFacade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;createCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mapToResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$savedCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mapToTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setCustomerReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;customerReference&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$updatedCustomer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customerFacade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;updateCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mapToResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$updatedCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapToTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;CustomersBackendResource&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomerTransfer&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$transfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$transfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$transfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapToResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;CustomerTransfer&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$transfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomersBackendResource&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomersBackendResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$transfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;generate-the-resource&quot;&gt;5. Generate the resource&lt;/h3&gt;
&lt;p&gt;Run the generation command to create the API resource class:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk cli &lt;span class=&quot;nv&quot;&gt;GLUE_APPLICATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;GLUE_BACKEND glue api:generate backend
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This generates:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Generated/Api/Backend/CustomersBackendResource.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The generated class includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API Platform attributes (&lt;code&gt;#[ApiResource]&lt;/code&gt;, &lt;code&gt;#[ApiProperty]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Validation constraints (&lt;code&gt;#[Assert\NotBlank]&lt;/code&gt;, &lt;code&gt;#[Assert\Email]&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Public properties for all defined fields&lt;/li&gt;
&lt;li&gt;Getters and setters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;toArray()&lt;/code&gt; and &lt;code&gt;fromArray()&lt;/code&gt; methods&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;register-services-in-the-dependency-injection-container&quot;&gt;6. Register services in the Dependency Injection container&lt;/h3&gt;
&lt;p&gt;Make your Provider and Processor available through dependency injection:
&lt;strong&gt;config/Glue/ApplicationServices.php&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ContainerConfigurator&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$configurator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$services&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$configurator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autowire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoconfigure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Auto-discover services from your project modules&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$services&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Pyz\\Glue\\&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;../../../src/Pyz/Glue/&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;test-your-api&quot;&gt;7. Test your API&lt;/h3&gt;
&lt;p&gt;After generation, your API is immediately available:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# List all customers&lt;/span&gt;
GET /customers

&lt;span class=&quot;c&quot;&gt;# Get single customer&lt;/span&gt;
GET /customers/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;customerReference&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Create customer&lt;/span&gt;
POST /customers
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;email&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;john@example.com&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;firstName&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;John&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;lastName&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;Doe&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Update customer&lt;/span&gt;
PATCH /customers/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;customerReference&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;firstName&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;Jane&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Delete customer&lt;/span&gt;
DELETE /customers/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;customerReference&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;creating-codebucket-specific-resources&quot;&gt;Creating CodeBucket-specific resources&lt;/h2&gt;
&lt;p&gt;CodeBucket support enables you to create Code Bucket-specific API resource variants that are resolved at runtime based on the &lt;code&gt;APPLICATION_CODE_BUCKET&lt;/code&gt; environment constant.&lt;/p&gt;
&lt;h3 id=&quot;when-to-use-codebucket-resources&quot;&gt;When to use CodeBucket resources&lt;/h3&gt;
&lt;p&gt;Create CodeBucket variants when you need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code Bucket-specific properties (EU GDPR fields, tax rates, compliance data)&lt;/li&gt;
&lt;li&gt;Code Bucket-specific validation rules (country-specific requirements)&lt;/li&gt;
&lt;li&gt;Country-specific business logic&lt;/li&gt;
&lt;li&gt;Feature variations per Code Bucket&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;quick-example&quot;&gt;Quick example&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Base resource:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/Customer/resources/api/backend/customers.resource.yml&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Customers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shortName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;customers&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Get&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Post&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;customerReference&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;EU-specific variant:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/CustomerEU/resources/api/backend/customers.resource.yml&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Customers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shortName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;customers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;codeBucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EU&lt;/span&gt;
  
  &lt;span class=&quot;na&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Get&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Post&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;customerReference&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# EU-specific properties&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;gdprConsentDate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;GDPR&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;consent&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;dataPrivacyOfficerEmail&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;privacy&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;officer&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;contact&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Generate resources:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk cli &lt;span class=&quot;nv&quot;&gt;GLUE_APPLICATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;GLUE_BACKEND glue api:generate backend
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This generates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CustomersBackendResource.php&lt;/code&gt; (base)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CustomersEUBackendResource.php&lt;/code&gt; (with &lt;code&gt;CODE_BUCKET = &apos;EU&apos;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Runtime behavior:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Request to &lt;code&gt;glue.eu.spryker.local/customers&lt;/code&gt; → Uses &lt;code&gt;CustomersEUBackendResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Request to &lt;code&gt;glue.de.spryker.local/customers&lt;/code&gt; → Uses &lt;code&gt;CustomersBackendResource&lt;/code&gt; (fallback)&lt;/li&gt;
&lt;li&gt;Request to &lt;code&gt;glue.at.spryker.local/customers&lt;/code&gt; → Uses &lt;code&gt;CustomersBackendResource&lt;/code&gt; (or &lt;code&gt;CustomersATBackendResource&lt;/code&gt; if variant exists)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a comprehensive guide including Provider implementation and advanced scenarios, see &lt;a href=&quot;/docs/dg/dev/architecture/api-platform/code-buckets.html&quot;&gt;CodeBucket Support&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;api-types-and-use-cases&quot;&gt;API types and use cases&lt;/h2&gt;
&lt;p&gt;Spryker supports multiple API types for different use cases:&lt;/p&gt;
&lt;h3 id=&quot;storefront-api-glue&quot;&gt;Storefront API (Glue)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Type:&lt;/strong&gt; &lt;code&gt;storefront&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Module location:&lt;/strong&gt; &lt;code&gt;src/Spryker/{Module}/resources/api/storefront/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generated namespace:&lt;/strong&gt; &lt;code&gt;Generated\Api\Storefront&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Customer-facing APIs, mobile apps, PWAs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;back-office-api-glue&quot;&gt;Back Office API (Glue)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Type:&lt;/strong&gt; &lt;code&gt;backend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Module location:&lt;/strong&gt; &lt;code&gt;src/Pyz/Glue/{Module}/resources/api/backend/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generated namespace:&lt;/strong&gt; &lt;code&gt;Generated\Api\Backend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Admin panels, internal tools, ERP integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;merchant-portal-api&quot;&gt;Merchant Portal API&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Type:&lt;/strong&gt; &lt;code&gt;merchant-portal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Module location:&lt;/strong&gt; &lt;code&gt;src/Spryker/{Module}/resources/api/merchant-portal/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generated namespace:&lt;/strong&gt; &lt;code&gt;Generated\Api\MerchantPortal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Marketplace merchant interfaces&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;schema-layering-and-inheritance&quot;&gt;Schema layering and inheritance&lt;/h2&gt;
&lt;p&gt;API Platform supports multi-layer schema definitions with automatic merging:&lt;/p&gt;
&lt;h3 id=&quot;core-layer&quot;&gt;Core layer&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;vendor/spryker/customer/resources/api/backend/customer.resource.yml&lt;/code&gt; - Base definition&lt;/p&gt;
&lt;h3 id=&quot;feature-layer&quot;&gt;Feature layer&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/SprykerFeature/CustomerRelationManagement/resources/api/backend/customer.resource.yml&lt;/code&gt; - Feature enhancements&lt;/p&gt;
&lt;h3 id=&quot;project-layer&quot;&gt;Project layer&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/Customer/resources/api/backend/customer.resource.yml&lt;/code&gt; - Project customizations&lt;/p&gt;
&lt;p&gt;The generator automatically merges these schemas with project layer taking precedence.&lt;/p&gt;
&lt;h2 id=&quot;debugging-resources&quot;&gt;Debugging resources&lt;/h2&gt;
&lt;p&gt;Use the debug command to inspect resources:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# List all resources&lt;/span&gt;
docker/sdk cli glue  api:debug &lt;span class=&quot;nt&quot;&gt;--list&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Show resource details&lt;/span&gt;
docker/sdk cli glue  api:debug customers &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend

&lt;span class=&quot;c&quot;&gt;# Show merged schema&lt;/span&gt;
docker/sdk cli glue  api:debug customers &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend &lt;span class=&quot;nt&quot;&gt;--show-merged&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Show source files&lt;/span&gt;
docker/sdk cli glue  api:debug customers &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend &lt;span class=&quot;nt&quot;&gt;--show-sources&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/resource-schemas.html&quot;&gt;Resource Schemas&lt;/a&gt; - Deep dive into resource schema syntax&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/validation-schemas.html&quot;&gt;Validation Schemas&lt;/a&gt; - Define validation rules for your resources&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/code-buckets.html&quot;&gt;CodeBucket Support&lt;/a&gt; - Create Code Bucket-specific resources&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/testing.html&quot;&gt;API Platform Testing&lt;/a&gt; - Learn how to write tests for your API resources&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/troubleshooting.html&quot;&gt;Troubleshooting&lt;/a&gt; - Common issues and solutions&lt;/li&gt;
&lt;/ul&gt;
</description>
            <pubDate>Tue, 19 May 2026 12:29:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/architecture/api-platform/enablement.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/architecture/api-platform/enablement.html</guid>
            
            
        </item>
        
        <item>
            <title>CodeBucket Support in API Platform</title>
            <description>&lt;p&gt;This document explains how to create and use CodeBucket-specific API resources in API Platform.&lt;/p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;CodeBucket support enables API Platform to serve different resource variants based on the runtime &lt;code&gt;APPLICATION_CODE_BUCKET&lt;/code&gt; environment constant. This allows you to maintain Code Bucket-specific API resources without requiring separate container compilations for each Code Bucket.&lt;/p&gt;
&lt;h3 id=&quot;use-cases&quot;&gt;Use cases&lt;/h3&gt;
&lt;p&gt;Use CodeBucket resources when you need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Code Bucket-specific properties&lt;/strong&gt;: EU-specific GDPR fields, tax rates, compliance data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code Bucket-specific validation&lt;/strong&gt;: Different validation rules per Code Bucket or country&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Localized business logic&lt;/strong&gt;: Country-specific processing requirements&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;key-benefits&quot;&gt;Key benefits&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Single container&lt;/strong&gt;: Compile once, serve hundreds of CodeBuckets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime resolution&lt;/strong&gt;: Automatic selection of correct resource variant&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Graceful fallback&lt;/strong&gt;: Base resources used when CodeBucket variant doesn’t exist&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No duplication&lt;/strong&gt;: Share common logic while extending for specific needs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-codebucket-resolution-works&quot;&gt;How CodeBucket resolution works&lt;/h2&gt;
&lt;h3 id=&quot;architecture-overview&quot;&gt;Architecture overview&lt;/h3&gt;
&lt;p&gt;API Platform uses a decorator pattern to intercept resource resolution and select the appropriate CodeBucket variant at runtime:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;Request → Symfony Routing → API Platform
                              ↓
                    CodeBucket Decorators
                              ↓
           ┌──────────────────┴──────────────────┐
           │                                     │
    Resource Class Resolver         Resource Name Collection Factory
    (Runtime resolution)            (Compile-time filtering)
           │                                     │
           └──────────────────┬──────────────────┘
                              ↓
                    Selected Resource Variant
                    (Base or CodeBucket-specific)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;runtime-resolution-flow&quot;&gt;Runtime resolution flow&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;1. Request: GET glue-backend.eu.spryker.local/stores
   APPLICATION_CODE_BUCKET = &apos;EU&apos;

2. CodeBucketResourceNameCollectionFactory
   → Filters resource collection at compile time
   → Includes only EU variants or base resources (no conflicts)

3. CodeBucketResourceClassResolver
   → Reads APPLICATION_CODE_BUCKET = &apos;EU&apos;
   → Base class: StoresBackendResource
   → Builds variant name: StoresEUBackendResource
   → Checks: class_exists(&apos;Generated\Api\Backend\StoresEUBackendResource&apos;)
   → Validates: StoresEUBackendResource::CODE_BUCKET === &apos;EU&apos;
   → Returns: StoresEUBackendResource

4. API Platform executes with EU-specific resource
   → Properties: base + EU-specific fields
   → Validations: base + EU-specific rules
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;codebucket-detection-mechanism&quot;&gt;CodeBucket detection mechanism&lt;/h3&gt;
&lt;p&gt;The system identifies CodeBucket resources using three checks:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Class existence&lt;/strong&gt;: &lt;code&gt;class_exists($codeBucketClassName)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Constant check&lt;/strong&gt;: &lt;code&gt;defined(&amp;quot;$className::CODE_BUCKET&amp;quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Value match&lt;/strong&gt;: &lt;code&gt;constant(&amp;quot;$className::CODE_BUCKET&amp;quot;) === $currentCodeBucket&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;generated-codebucket-constant&quot;&gt;Generated CODE_BUCKET constant&lt;/h3&gt;
&lt;p&gt;CodeBucket resources automatically get a &lt;code&gt;CODE_BUCKET&lt;/code&gt; constant during generation:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// StoresEUBackendResource.php (EU variant)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StoresEUBackendResource&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CODE_BUCKET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;EU&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Properties...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// StoresBackendResource.php (base - no constant)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StoresBackendResource&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Properties... (no CODE_BUCKET constant)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;resource-naming-conventions&quot;&gt;Resource naming conventions&lt;/h2&gt;
&lt;h3 id=&quot;file-naming-pattern&quot;&gt;File naming pattern&lt;/h3&gt;
&lt;p&gt;Resource schemas follow the pattern: &lt;code&gt;{resource-name}.resource.yml&lt;/code&gt;
Validation schemas follow the pattern: &lt;code&gt;{resource-name}.validation.yml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Each YAML file contains exactly one resource definition. To create a CodeBucket variant, place its schema in a &lt;strong&gt;separate module directory&lt;/strong&gt; named after the variant (for example, &lt;code&gt;StoresApiEU&lt;/code&gt;, &lt;code&gt;StoresApiAT&lt;/code&gt;) and set the &lt;code&gt;codeBucket:&lt;/code&gt; property inside the file. The filename stays the same across all variants — only the parent module directory and the &lt;code&gt;codeBucket:&lt;/code&gt; value differ.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;src/Pyz/Glue/StoresApi/resources/api/backend/           # Base variant module
├── stores.resource.yml                                  # No codeBucket property
└── stores.validation.yml

src/Pyz/Glue/StoresApiEU/resources/api/backend/         # EU variant module
├── stores.resource.yml                                  # codeBucket: EU
└── stores.validation.yml                                # codeBucket: EU

src/Pyz/Glue/StoresApiAT/resources/api/backend/         # AT variant module
├── stores.resource.yml                                  # codeBucket: AT
└── stores.validation.yml                                # codeBucket: AT
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;generated-class-naming-pattern&quot;&gt;Generated class naming pattern&lt;/h3&gt;
&lt;p&gt;The generator creates classes following: &lt;code&gt;{ResourceName}{CodeBucket}{ApiType}Resource&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Schema File&lt;/th&gt;
&lt;th&gt;CodeBucket (defined in file)&lt;/th&gt;
&lt;th&gt;Generated Class&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stores.resource.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None (base)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StoresBackendResource&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stores.resource.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;EU&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StoresEUBackendResource&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stores.resource.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StoresATBackendResource&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stores.resource.yml&lt;/code&gt; (Storefront)&lt;/td&gt;
&lt;td&gt;EU&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StoresEUStorefrontResource&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;url-consistency&quot;&gt;URL consistency&lt;/h3&gt;
&lt;p&gt;All CodeBucket variants share the same URL path, with the Code Bucket defined in the domain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EU: &lt;code&gt;glue-backend.eu.spryker.local/stores&lt;/code&gt; → &lt;code&gt;StoresEUBackendResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;AT: &lt;code&gt;glue-backend.at.spryker.local/stores&lt;/code&gt; → &lt;code&gt;StoresATBackendResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;DE: &lt;code&gt;glue-backend.de.spryker.local/stores&lt;/code&gt; → &lt;code&gt;StoresBackendResource&lt;/code&gt; (or &lt;code&gt;StoresDEBackendResource&lt;/code&gt; if variant exists)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The URL path is identical (&lt;code&gt;/stores&lt;/code&gt;), but the Code Bucket in the domain determines which resource variant is used. Only the properties, validations, and business logic differ between variants.&lt;/p&gt;
&lt;h2 id=&quot;creating-codebucket-resources&quot;&gt;Creating CodeBucket resources&lt;/h2&gt;
&lt;h3 id=&quot;basic-example&quot;&gt;Basic example&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Create base resource&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/StoresApi/resources/api/backend/stores.resource.yml&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shortName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;resource&quot;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoresApi&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoreBackendProvider&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoresApi&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Processor&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoreBackendProcessor&quot;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Get&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GetCollection&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;idStore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;integer&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&quot;&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;timezone&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Define the CodeBucket variant in a separate module&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Create a new module directory whose name carries the CodeBucket suffix (&lt;code&gt;StoresApiEU&lt;/code&gt;) and place the variant’s schema there. The filename stays &lt;code&gt;stores.resource.yml&lt;/code&gt;; the &lt;code&gt;codeBucket:&lt;/code&gt; property inside the file declares which variant it belongs to.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/StoresApiEU/resources/api/backend/stores.resource.yml&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shortName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;EU-specific&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;resource&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;codeBucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EU&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Same provider and processor as the base resource&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoresApi&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoreBackendProvider&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoresApi&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Processor&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoreBackendProcessor&quot;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Get&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GetCollection&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;idStore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;integer&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&quot;&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;timezone&quot;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# EU-specific properties&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;taxRate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;number&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;EU&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tax&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;rate&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;openapiContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;19.0&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;gdprContactEmail&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;GDPR&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;contact&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;email&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;openapiContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;privacy@example.com&quot;&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;vatRegistrationNumber&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VAT&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;registration&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;number&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;section class=&apos;info-block &apos;&gt;&lt;i class=&apos;info-block__icon icon-info&apos;&gt;&lt;/i&gt;&lt;div class=&apos;info-block__content&apos;&gt;&lt;div class=&quot;info-block__title&quot;&gt;One resource per file&lt;/div&gt;
&lt;p&gt;Each schema file must contain exactly one top-level &lt;code&gt;resource:&lt;/code&gt; block. CodeBucket variants live in their own files in their own module directories — they cannot be stacked inside a single YAML document.&lt;/p&gt;
&lt;/div&gt;&lt;/section&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Create the variant’s validation schema&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Place the matching validation file alongside the variant resource, in the same module directory. Set &lt;code&gt;codeBucket:&lt;/code&gt; at the root so the parser pairs it with the EU resource variant.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/StoresApiEU/resources/api/backend/stores.validation.yml&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;codeBucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EU&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;taxRate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;gdprContactEmail&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Email&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;vatRegistrationNumber&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/^[A-Z]{2}[0-9]{8,12}$/&apos;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Invalid&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;VAT&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;format&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Step 4: Generate resources&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk cli &lt;span class=&quot;nv&quot;&gt;GLUE_APPLICATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;GLUE_BACKEND glue api:generate backend
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This generates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/Generated/Api/Backend/StoresBackendResource.php&lt;/code&gt; (base)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/Generated/Api/Backend/StoresEUBackendResource.php&lt;/code&gt; (with &lt;code&gt;CODE_BUCKET = &apos;EU&apos;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Step 5: Implement Provider with CodeBucket awareness&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Pyz/Glue/StoresApi/Api/Backend/Provider/StoreBackendProvider.php&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pyz\Glue\StoresApi\Api\Backend\Provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\Metadata\Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\State\ProviderInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pyz\Glue\StoresApi\Business\StoreFacadeInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StoreBackendProvider&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProviderInterface&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StoreFacadeInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeFacade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;idStore&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;idStore&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getStores&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$idStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;?object&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storeFacade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;findStoreById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$idStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Add EU-specific data if this is an EU resource&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;::CODE_BUCKET&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CODE_BUCKET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;EU&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;taxRate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getTaxRate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gdprContactEmail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getGdprContactEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vatRegistrationNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getVatRegistrationNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getStores&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stores&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storeFacade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getAllStores&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$resources&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$stores&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;::CODE_BUCKET&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CODE_BUCKET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;EU&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;taxRate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getTaxRate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gdprContactEmail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getGdprContactEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vatRegistrationNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeTransfer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getVatRegistrationNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;nv&quot;&gt;$resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;schema-layering-with-codebuckets&quot;&gt;Schema layering with CodeBuckets&lt;/h2&gt;
&lt;p&gt;CodeBucket variants are a &lt;strong&gt;project-level concept only&lt;/strong&gt; — they never exist in the core (vendor) or feature layers. Core and feature layers contribute properties to the &lt;strong&gt;base&lt;/strong&gt; resource. The project-level CodeBucket variant then inherits from that already-merged base and adds its own properties on top.&lt;/p&gt;
&lt;section class=&apos;info-block &apos;&gt;&lt;i class=&apos;info-block__icon icon-info&apos;&gt;&lt;/i&gt;&lt;div class=&apos;info-block__content&apos;&gt;&lt;div class=&quot;info-block__title&quot;&gt;Where CodeBucket files live&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;codeBucket:&lt;/code&gt; property is only ever set in files under &lt;code&gt;src/Pyz/Glue/{Module}{CodeBucketName}/...&lt;/code&gt;. A schema file in &lt;code&gt;vendor/spryker/...&lt;/code&gt; or &lt;code&gt;src/SprykerFeature/...&lt;/code&gt; must not declare a &lt;code&gt;codeBucket:&lt;/code&gt; — those layers always describe the base resource.&lt;/p&gt;
&lt;/div&gt;&lt;/section&gt;
&lt;h3 id=&quot;example-base-resource-built-across-layers--project-level-eu-variant&quot;&gt;Example: base resource built across layers + project-level EU variant&lt;/h3&gt;
&lt;p&gt;The base &lt;code&gt;Stores&lt;/code&gt; resource is assembled from the core, feature, and project layers in that precedence order. The EU variant lives only in the project layer, in its own variant module.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core layer&lt;/strong&gt; (vendor) — base only, no &lt;code&gt;codeBucket&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# vendor/spryker/store/resources/api/backend/stores.resource.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shortName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Feature layer&lt;/strong&gt; — base only, no &lt;code&gt;codeBucket&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# src/SprykerFeature/CRM/resources/api/backend/stores.resource.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;countries&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Project base layer&lt;/strong&gt; — base only, no &lt;code&gt;codeBucket&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# src/Pyz/Glue/StoresApi/resources/api/backend/stores.resource.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# tighten the core definition&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Project CodeBucket variant&lt;/strong&gt; — only this file carries &lt;code&gt;codeBucket:&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# src/Pyz/Glue/StoresApiEU/resources/api/backend/stores.resource.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;codeBucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EU&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;taxRate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;number&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# EU-specific addition&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;companyVatId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# EU-specific addition&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Merged result&lt;/strong&gt; (&lt;code&gt;StoresEUBackendResource&lt;/code&gt;) — base properties inherited from the core → feature → project merge, EU-specific properties added on top:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;codeBucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EU&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;# from core&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;# from core, tightened by project base&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;countries&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# from feature&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;taxRate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;# from project EU variant&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;number&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;companyVatId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# from project EU variant&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;fallback-behavior&quot;&gt;Fallback behavior&lt;/h2&gt;
&lt;h3 id=&quot;graceful-degradation&quot;&gt;Graceful degradation&lt;/h3&gt;
&lt;p&gt;When &lt;code&gt;APPLICATION_CODE_BUCKET&lt;/code&gt; is set but no matching variant exists, the system falls back to the base resource:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;Scenario: APPLICATION_CODE_BUCKET = &apos;AT&apos;
Available resources:
  - StoresBackendResource (base)
  - StoresEUBackendResource (EU variant)

Result: StoresBackendResource is used (graceful fallback)
No errors, system continues to work
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;no-codebucket-set&quot;&gt;No CodeBucket set&lt;/h3&gt;
&lt;p&gt;When &lt;code&gt;APPLICATION_CODE_BUCKET&lt;/code&gt; is not set:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;Available resources:
  - StoresBackendResource (base)
  - StoresEUBackendResource (EU variant)
  - StoresATBackendResource (AT variant)

Result: StoresBackendResource is used
All CodeBucket variants are excluded from resource collection
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;resource-collection-filtering&quot;&gt;Resource collection filtering&lt;/h2&gt;
&lt;h3 id=&quot;codebucketresourcenamecollectionfactory&quot;&gt;CodeBucketResourceNameCollectionFactory&lt;/h3&gt;
&lt;p&gt;This decorator filters the resource collection at compile time to prevent conflicts between base and CodeBucket variants.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When APPLICATION_CODE_BUCKET is not set:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Include all base resources (no CODE_BUCKET constant)&lt;/li&gt;
&lt;li&gt;Exclude all CodeBucket variants&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When APPLICATION_CODE_BUCKET is set (example: ‘EU’):&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Group resources by base class name&lt;/li&gt;
&lt;li&gt;For each group:
&lt;ul&gt;
&lt;li&gt;If EU variant exists → include ONLY the EU variant&lt;/li&gt;
&lt;li&gt;If no EU variant exists → include base resource (fallback)&lt;/li&gt;
&lt;li&gt;Exclude all non-matching CodeBucket variants&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Example filtering:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;Available classes:
  - StoresBackendResource (base)
  - StoresEUBackendResource (EU)
  - StoresATBackendResource (AT)
  - CustomersBackendResource (base, no variants)

With APPLICATION_CODE_BUCKET = &apos;EU&apos;:
  ✓ StoresEUBackendResource (EU variant exists)
  ✗ StoresBackendResource (excluded, EU variant preferred)
  ✗ StoresATBackendResource (excluded, wrong CodeBucket)
  ✓ CustomersBackendResource (no variant, base used)

With APPLICATION_CODE_BUCKET = &apos;DE&apos;:
  ✓ StoresBackendResource (no DE variant, fallback to base)
  ✗ StoresEUBackendResource (excluded, wrong CodeBucket)
  ✗ StoresATBackendResource (excluded, wrong CodeBucket)
  ✓ CustomersBackendResource (no variant, base used)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;benefits-of-filtering&quot;&gt;Benefits of filtering&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OpenAPI Generation&lt;/strong&gt;: Only the correct variant appears in OpenAPI documentation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Route Registration&lt;/strong&gt;: Only the correct variant routes are registered&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Conflicts&lt;/strong&gt;: Base and CodeBucket variants never coexist in routing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Filtering happens once and is cached&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;container-compilation&quot;&gt;Container compilation&lt;/h2&gt;
&lt;h3 id=&quot;single-container-for-all-codebuckets&quot;&gt;Single container for all CodeBuckets&lt;/h3&gt;
&lt;p&gt;API Platform compiles a single container that contains ALL resources (base and all CodeBucket variants). Runtime resolution selects the correct variant per request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;At compile time:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Discover all resource classes in &lt;code&gt;src/Generated/Api/{ApiType}/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Build metadata for ALL resources (base + all CodeBucket variants)&lt;/li&gt;
&lt;li&gt;Register routes for ALL resources (all map to same URLs)&lt;/li&gt;
&lt;li&gt;Cache everything&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;At runtime:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CodeBucket decorators intercept resolution&lt;/li&gt;
&lt;li&gt;Select correct resource based on &lt;code&gt;APPLICATION_CODE_BUCKET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Only selected resource is used for that request&lt;/li&gt;
&lt;li&gt;Different CodeBuckets served from same container&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;performance-characteristics&quot;&gt;Performance characteristics&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One-time cost&lt;/strong&gt;: Container compilation happens once&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No overhead&lt;/strong&gt;: Runtime resolution uses local cache per request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Supports hundreds of CodeBuckets without performance impact&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;debugging-codebucket-resources&quot;&gt;Debugging CodeBucket resources&lt;/h2&gt;
&lt;h3 id=&quot;verify-codebucket-constant-generation&quot;&gt;Verify CODE_BUCKET constant generation&lt;/h3&gt;
&lt;p&gt;After regenerating resources, verify the constant was added:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Check EU resource has constant&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CODE_BUCKET&quot;&lt;/span&gt; src/Generated/Api/Backend/StoresEUBackendResource.php
&lt;span class=&quot;c&quot;&gt;# Expected: public const string CODE_BUCKET = &apos;EU&apos;;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Verify base resource has no constant&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CODE_BUCKET&quot;&lt;/span&gt; src/Generated/Api/Backend/StoresBackendResource.php
&lt;span class=&quot;c&quot;&gt;# Expected: no match (empty output)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;debug-resource-resolution&quot;&gt;Debug resource resolution&lt;/h3&gt;
&lt;p&gt;Use the debug command to inspect which resources are available:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# List all resources&lt;/span&gt;
docker/sdk cli &lt;span class=&quot;nv&quot;&gt;GLUE_APPLICATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;GLUE_BACKEND glue api:debug &lt;span class=&quot;nt&quot;&gt;--list&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Show specific resource&lt;/span&gt;
docker/sdk cli &lt;span class=&quot;nv&quot;&gt;GLUE_APPLICATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;GLUE_BACKEND glue api:debug stores &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend

&lt;span class=&quot;c&quot;&gt;# Show merged schema&lt;/span&gt;
docker/sdk cli &lt;span class=&quot;nv&quot;&gt;GLUE_APPLICATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;GLUE_BACKEND glue api:debug stores &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend &lt;span class=&quot;nt&quot;&gt;--show-merged&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Show all contributing source files&lt;/span&gt;
docker/sdk cli &lt;span class=&quot;nv&quot;&gt;GLUE_APPLICATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;GLUE_BACKEND glue api:debug stores &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend &lt;span class=&quot;nt&quot;&gt;--show-sources&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;verify-runtime-resolution&quot;&gt;Verify runtime resolution&lt;/h3&gt;
&lt;p&gt;Test different CodeBuckets by accessing different domains:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Test EU CodeBucket&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; GET http://glue-backend.eu.spryker.local/stores
&lt;span class=&quot;c&quot;&gt;# Should include: taxRate, gdprContactEmail, vatRegistrationNumber&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Test AT CodeBucket (if no AT variant exists)&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; GET http://glue-backend.at.spryker.local/stores
&lt;span class=&quot;c&quot;&gt;# Should return base resource (fallback)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Test DE CodeBucket&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; GET http://glue-backend.de.spryker.local/stores
&lt;span class=&quot;c&quot;&gt;# Should return base resource or DE-specific variant if available&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;best-practices&quot;&gt;Best practices&lt;/h2&gt;
&lt;h3 id=&quot;keep-base-resources-complete&quot;&gt;1. Keep base resources complete&lt;/h3&gt;
&lt;p&gt;The base resource should contain all common properties that work across all CodeBuckets:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ✅ Good - Base is complete&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# src/Pyz/Glue/StoresApi/resources/api/backend/stores.resource.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;idStore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;integer&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ✅ EU variant lives in its own module directory&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# src/Pyz/Glue/StoresApiEU/resources/api/backend/stores.resource.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stores&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;codeBucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EU&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;idStore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;integer&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;taxRate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# EU-specific addition&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;use-codebuckets-for-true-regional-differences&quot;&gt;2. Use CodeBuckets for true regional differences&lt;/h3&gt;
&lt;p&gt;Only create CodeBucket variants when there are genuine regional requirements:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ✅ Good use cases:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EU GDPR compliance fields&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Code Bucket-specific tax calculations&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Country-specific validation rules&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Code Bucket-specific business logic&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ❌ Bad use cases:q&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Language translations (use locales instead)&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Minor field variations (use base resource)&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Temporary feature flags (use feature toggles)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;share-provider-and-processor-logic&quot;&gt;3. Share Provider and Processor logic&lt;/h3&gt;
&lt;p&gt;Use the same Provider and Processor classes for base and CodeBucket variants when possible:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Both use same implementation&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoresApi&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoreBackendProvider&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoresApi&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Processor&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;StoreBackendProcessor&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Detect the resource type inside the Provider using the &lt;code&gt;CODE_BUCKET&lt;/code&gt; constant:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;::CODE_BUCKET&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$codeBucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$resourceClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CODE_BUCKET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Apply CodeBucket-specific logic&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;document-codebucket-specific-fields&quot;&gt;4. Document CodeBucket-specific fields&lt;/h3&gt;
&lt;p&gt;Clearly document which fields are CodeBucket-specific in your schema descriptions:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;gdprContactEmail&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;GDPR&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;contact&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(EU-specific&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;requirement&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;GDPR&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;compliance)&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;test-fallback-behavior&quot;&gt;5. Test fallback behavior&lt;/h3&gt;
&lt;p&gt;Always test that your application works correctly when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CodeBucket is not set (uses base resource)&lt;/li&gt;
&lt;li&gt;CodeBucket is set but variant doesn’t exist (falls back to base)&lt;/li&gt;
&lt;li&gt;CodeBucket variant exists (uses variant)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;
&lt;p&gt;CodeBucket resources can be tested using the standard API Platform testing infrastructure. The test environment automatically generates resources before tests execute.&lt;/p&gt;
&lt;p&gt;For detailed testing guidance, see &lt;a href=&quot;/docs/dg/dev/architecture/api-platform/testing.html&quot;&gt;API Platform Testing&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h2&gt;
&lt;h3 id=&quot;resource-not-found-for-codebucket&quot;&gt;Resource not found for CodeBucket&lt;/h3&gt;
&lt;p&gt;If you get a “Resource not found” error for a specific CodeBucket:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Verify the schema file naming: &lt;code&gt;{resource-name}.resource.yml&lt;/code&gt; for resources, &lt;code&gt;{resource-name}.validation.yml&lt;/code&gt; for validations&lt;/li&gt;
&lt;li&gt;Verify the CodeBucket is defined inside the schema file&lt;/li&gt;
&lt;li&gt;Regenerate resources: &lt;code&gt;docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:generate backend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Verify the CODE_BUCKET constant exists in the generated class&lt;/li&gt;
&lt;li&gt;Check that &lt;code&gt;APPLICATION_CODE_BUCKET&lt;/code&gt; matches the CodeBucket defined in your schema file&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;wrong-resource-variant-used&quot;&gt;Wrong resource variant used&lt;/h3&gt;
&lt;p&gt;If the wrong variant is being used:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Verify &lt;code&gt;APPLICATION_CODE_BUCKET&lt;/code&gt; is set correctly in your environment&lt;/li&gt;
&lt;li&gt;Clear caches: &lt;code&gt;docker/sdk cli rm -rf data/cache/*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check that the resource class name follows the pattern: &lt;code&gt;{Name}{CodeBucket}{ApiType}Resource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Verify the CODE_BUCKET constant value matches &lt;code&gt;APPLICATION_CODE_BUCKET&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For more troubleshooting guidance, see &lt;a href=&quot;/docs/dg/dev/architecture/api-platform/troubleshooting.html&quot;&gt;API Platform Troubleshooting&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/resource-schemas.html&quot;&gt;Resource Schemas&lt;/a&gt; - Deep dive into schema syntax&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/validation-schemas.html&quot;&gt;Validation Schemas&lt;/a&gt; - Define validation rules&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/enablement.html&quot;&gt;API Platform Enablement&lt;/a&gt; - Creating resources&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/testing.html&quot;&gt;API Platform Testing&lt;/a&gt; - Testing your resources&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/troubleshooting.html&quot;&gt;Troubleshooting&lt;/a&gt; - Common issues&lt;/li&gt;
&lt;/ul&gt;
</description>
            <pubDate>Tue, 19 May 2026 12:29:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/architecture/api-platform/code-buckets.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/architecture/api-platform/code-buckets.html</guid>
            
            
        </item>
        
        <item>
            <title>API Platform</title>
            <description>&lt;p&gt;Spryker’s API Platform integration provides schema-based API resource generation with automatic OpenAPI documentation. This allows you to define your API resources using YAML schemas and automatically generate fully functional API endpoints with validation, pagination, and serialization.&lt;/p&gt;
&lt;p&gt;This document describes the API Platform architecture and how it integrates with Spryker.&lt;/p&gt;
&lt;h2 id=&quot;what-is-api-platform&quot;&gt;What is API Platform&lt;/h2&gt;
&lt;p&gt;API Platform is a framework for building modern APIs based on web standards and best practices. In Spryker, it complements the existing Glue API infrastructure by providing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Schema-based resource generation&lt;/strong&gt;: Define resources in YAML, generate PHP classes automatically&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatic OpenAPI documentation&lt;/strong&gt;: Interactive API documentation generated from schemas&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in validation&lt;/strong&gt;: Symfony Validator integration with operation-specific rules&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pagination support&lt;/strong&gt;: Standardized pagination with configurable defaults&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State management&lt;/strong&gt;: Separate providers (read) and processors (write) for clean architecture&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read more about the API Platform project at &lt;a href=&quot;https://api-platform.com/&quot;&gt;api-platform.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;architecture-overview&quot;&gt;Architecture overview&lt;/h2&gt;
&lt;h3 id=&quot;resource-generation-workflow&quot;&gt;Resource generation workflow&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;Schema Files (YAML)
    ↓
Schema Discovery &amp;amp; Validation
    ↓
Multi-layer Schema Merging (Core → Feature → Project → [Code Buckets])
    ↓
Resource Class Generation
    ↓
API Platform Resource (with attributes)
    ↓
API Endpoints
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;core-components&quot;&gt;Core components&lt;/h3&gt;
&lt;h4 id=&quot;schema-files&quot;&gt;1. Schema files&lt;/h4&gt;
&lt;p&gt;Resources are defined in YAML files located in module directories:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;src/Spryker/{Module}/resources/api/{api-type}/{resource-name}.resources.yml
src/Spryker/{Module}/resources/api/{api-type}/{resource-name}.validation.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example resource schema &lt;code&gt;src/Spryker/{Module}/resources/api/{api-type}/{resource-name}.resources.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Customers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shortName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;customers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API&quot;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CustomerBackendProvider&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Pyz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Glue&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Processor&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CustomerBackendProcessor&quot;&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;paginationEnabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Post&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Get&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GetCollection&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Patch&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Delete&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;address&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;customerReference&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Example validation schema &lt;code&gt;src/Spryker/{Module}/resources/api/{api-type}/{resource-name}.validation.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NotBlank&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;First name is required&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;64&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;minMessage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;First name must be at least 2 characters&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;maxMessage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;First name cannot exceed 64 characters&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;64&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;minMessage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;First name must be at least 2 characters&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;maxMessage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;First name cannot exceed 64 characters&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;generated-resources&quot;&gt;2. Generated resources&lt;/h4&gt;
&lt;p&gt;The generator creates PHP classes with API Platform attributes:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/Generated/Api/Backend/CustomersBackendResource.php&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Generated\Api\Backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\Metadata\ApiResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiPlatform\Metadata\ApiProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Symfony\Component\Validator\Constraints&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;#[ApiResource(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GetCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shortName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;customers&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerBackendProvider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerBackendProcessor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomersBackendResource&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#[ApiProperty(identifier: true, writable: false)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;?string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerReference&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;#[ApiProperty]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#[Assert\NotBlank(groups: [&apos;customers:create&apos;])]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#[Assert\Email(groups: [&apos;customers:create&apos;])]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;?string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Getters, setters, toArray(), fromArray()...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;state-providers-and-processors&quot;&gt;3. State providers and processors&lt;/h4&gt;
&lt;p&gt;Detailed information about the API-Platform Provider and Resources can be found on the public docs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://api-platform.com/docs/core/state-providers/&quot;&gt;API Platform Providers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api-platform.com/docs/core/state-processors/&quot;&gt;API Platform Processors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Provider (read operations):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerBackendProvider&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProviderInterface&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Fetch and return data from your business layer&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Processor (write operations):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerBackendProcessor&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProcessorInterface&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;mixed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uriVariables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;mixed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Persist changes through your business layer&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$updatedResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;api-types&quot;&gt;API types&lt;/h2&gt;
&lt;p&gt;Any of the &lt;a href=&quot;https://docs.spryker.com/docs/integrations/spryker-glue-api/getting-started-with-apis/getting-started-with-apis&quot;&gt;existing APIs&lt;/a&gt; can be extended using API Platform.&lt;/p&gt;
&lt;p&gt;Spryker supports multiple API types for different use cases:&lt;/p&gt;
&lt;h3 id=&quot;glue-api&quot;&gt;Glue API&lt;/h3&gt;
&lt;p&gt;This API is configured to serve the JSON:API format by default, which can be configured per project. Projects migrating their APIs can provide new APIs as well as supporting the existing ones while migrating.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Type:&lt;/strong&gt; &lt;code&gt;storefront&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application:&lt;/strong&gt; Glue&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base URL:&lt;/strong&gt; &lt;code&gt;http://glue.eu.spryker.local/&lt;/code&gt; - Configurable per project&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Customer-facing APIs, mobile apps, PWAs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;gluestorefront-api&quot;&gt;GlueStorefront API&lt;/h3&gt;
&lt;p&gt;Thie API is configured to serve the JSON+LD format by default, which can be configured per project.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Type:&lt;/strong&gt; &lt;code&gt;storefront&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application:&lt;/strong&gt; Glue&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base URL:&lt;/strong&gt; &lt;code&gt;http://glue-storefront.eu.spryker.local/&lt;/code&gt; - Configurable per project&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Customer-facing APIs, mobile apps, PWAs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;gluebackend-api&quot;&gt;GlueBackend API&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Type:&lt;/strong&gt; &lt;code&gt;backend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application:&lt;/strong&gt; Zed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base URL:&lt;/strong&gt; &lt;code&gt;http://glue-backend.eu.spryker.local/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Admin panels, internal tools, ERP integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;merchant-portal-api&quot;&gt;Merchant Portal API&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Type:&lt;/strong&gt; &lt;code&gt;merchant-portal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application:&lt;/strong&gt; MerchantPortal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base URL:&lt;/strong&gt; &lt;code&gt;http://mp.glue.eu.spryker.local/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases:&lt;/strong&gt; Marketplace merchant interfaces&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;/products&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;multi-layer-schema-merging&quot;&gt;Multi-layer schema merging&lt;/h2&gt;
&lt;p&gt;One of the key features is support for multi-layer schema definitions that automatically merge:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core layer&lt;/strong&gt; (vendor/spryker):&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Customers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Feature layer&lt;/strong&gt; (src/SprykerFeature):&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Customers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;loyaltyPoints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;integer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Project layer&lt;/strong&gt; (src/Pyz):&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Customers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Override core&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;customField&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Project-specific&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: A single merged resource with all properties, project code-bucket layer taking precedence.&lt;/p&gt;
&lt;h2 id=&quot;integration-with-spryker-architecture&quot;&gt;Integration with Spryker architecture&lt;/h2&gt;
&lt;h3 id=&quot;dependency-injection&quot;&gt;Dependency Injection&lt;/h3&gt;
&lt;p&gt;API Platform fully integrates with Symfony Dependency Injection:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// config/Zed/ApplicationServices.php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$services&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Pyz\\Zed\\&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;../../../src/Pyz/Zed/&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Providers and Processors are automatically discovered and can use constructor injection:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerBackofficeProvider&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProviderInterface&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomerFacadeInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerFacade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomerRepositoryInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;facade-integration&quot;&gt;Facade integration&lt;/h3&gt;
&lt;p&gt;Resources can leverage existing Spryker facades:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerBackendProcessor&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProcessorInterface&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CustomerFacadeInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$customerFacade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;mixed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;mixed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mapToTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customerFacade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;createCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$customerTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mapToResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getCustomerTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;resource-generation&quot;&gt;Resource generation&lt;/h2&gt;
&lt;h3 id=&quot;console-commands&quot;&gt;Console commands&lt;/h3&gt;
&lt;p&gt;All the following commands can be used with a specific GLUE_APPLICATION by prefixing them with &lt;code&gt;GLUE_APPLICATION=GLUE_BACKEND&lt;/code&gt; environment variable. For example: &lt;code&gt;docker/sdk cli GLUE_APPLICATION=GLUE_BACKEND glue api:debug --list&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Generate resource classes for all configured API types at once. Usually used during deployment/installation.&lt;/span&gt;
docker/sdk cli glue api:generate

&lt;span class=&quot;c&quot;&gt;# Generate API type specific resource classes. Usually used during development.&lt;/span&gt;
docker/sdk cli glue api:generate backend

&lt;span class=&quot;c&quot;&gt;# Validate schemas only to see if there is any issue in the definitions&lt;/span&gt;
docker/sdk cli glue api:generate &lt;span class=&quot;nt&quot;&gt;--validate-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;debug-commands&quot;&gt;Debug commands&lt;/h3&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# List all resources to see which ones are defined in the schema files.&lt;/span&gt;
docker/sdk cli glue  api:debug &lt;span class=&quot;nt&quot;&gt;--list&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Inspect specific resource and print details about properties and operations&lt;/span&gt;
docker/sdk cli glue  api:debug customers &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend

&lt;span class=&quot;c&quot;&gt;# Show merged schema&lt;/span&gt;
docker/sdk cli glue  api:debug customers &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend &lt;span class=&quot;nt&quot;&gt;--show-merged&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Show contributing files for a resource&lt;/span&gt;
docker/sdk cli glue  api:debug customers &lt;span class=&quot;nt&quot;&gt;--api-type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;backend &lt;span class=&quot;nt&quot;&gt;--show-sources&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;features&quot;&gt;Features&lt;/h2&gt;
&lt;h3 id=&quot;automatic-openapi-documentation&quot;&gt;Automatic OpenAPI documentation&lt;/h3&gt;
&lt;p&gt;API Platform generates interactive OpenAPI documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Swagger UI at the root URL &lt;code&gt;/&lt;/code&gt; for example &lt;code&gt;http://glue-backend.eu.spryker.local/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can disable this interface in production environments by configuring the settings in your &lt;code&gt;api_platform.php&lt;/code&gt; configuration file. For details, see &lt;a href=&quot;/docs/dg/dev/architecture/api-platform/configuration.html#disable-swaggerui-in-production&quot;&gt;Disable Swagger UI&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;built-in-validation&quot;&gt;Built-in validation&lt;/h3&gt;
&lt;p&gt;Validation rules from &lt;code&gt;*.validation.yml&lt;/code&gt; files are converted to Symfony Validator constraints:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NotBlank&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Email&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Becomes:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#[Assert\NotBlank(groups: [&apos;customers:create&apos;])]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#[Assert\Email(groups: [&apos;customers:create&apos;])]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;?string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;pagination-support&quot;&gt;Pagination support&lt;/h3&gt;
&lt;p&gt;Standardized pagination with query parameters:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-MARKDOWN&quot;&gt;&gt;GET /customers?page=2&amp;amp;itemsPerPage=20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Provider returns &lt;code&gt;PaginatorInterface&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TraversablePaginator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ArrayObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$currentPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$totalItems&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;operation-specific-behavior&quot;&gt;Operation-specific behavior&lt;/h3&gt;
&lt;p&gt;Define different validation and behavior per operation:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Post&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# Create&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Get&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;# Read one&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GetCollection&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# Read many&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Patch&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# Update&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Delete&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# Delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each operation can have specific validation rules and security settings.&lt;/p&gt;
&lt;h3 id=&quot;relationships&quot;&gt;Relationships&lt;/h3&gt;
&lt;p&gt;Include related resources via the &lt;code&gt;?include=&lt;/code&gt; query parameter:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;relationshipName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;addresses&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;targetResource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;CustomersAddresses&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;uriVariableMappings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;customerReference&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;customerReference&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Request:&lt;/p&gt;
&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /customers/customer--35?include=addresses
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Response includes both the customer and related addresses in JSON:API format. No provider code changes required - relationships work automatically through decoration.&lt;/p&gt;
&lt;p&gt;For detailed information, see &lt;a href=&quot;/docs/dg/dev/architecture/api-platform/relationships.html&quot;&gt;Relationships&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;sparse-fieldsets&quot;&gt;Sparse fieldsets&lt;/h3&gt;
&lt;p&gt;Request only the attributes you need using the &lt;code&gt;fields&lt;/code&gt; query parameter:&lt;/p&gt;
&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /stores?fields[stores]=name,locale
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This returns only &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;locale&lt;/code&gt; in the response attributes, reducing payload size. Sparse fieldsets work with relationships too — filter attributes on both the main resource and included resources.&lt;/p&gt;
&lt;p&gt;For detailed information, see &lt;a href=&quot;/docs/dg/dev/architecture/api-platform/sparse-fieldsets.html&quot;&gt;Sparse Fieldsets&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;performance&quot;&gt;Performance&lt;/h2&gt;
&lt;h3 id=&quot;cache-warming&quot;&gt;Cache warming&lt;/h3&gt;
&lt;p&gt;Pre-generate resources during deployment:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk cli glue  api:generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk cli glue  cache:warmup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;property-level-access-control&quot;&gt;Property-level access control&lt;/h3&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# Can be written&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;readable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Not in responses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;comparison-with-glue-api&quot;&gt;Comparison with Glue API&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;API Platform&lt;/th&gt;
&lt;th&gt;Glue API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Definition&lt;/td&gt;
&lt;td&gt;Schema-based (YAML)&lt;/td&gt;
&lt;td&gt;Code-based (PHP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;Auto-generated OpenAPI&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;td&gt;Declarative&lt;/td&gt;
&lt;td&gt;Programmatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standards&lt;/td&gt;
&lt;td&gt;JSON-LD, Hydra&lt;/td&gt;
&lt;td&gt;JSON API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Very high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use cases&lt;/td&gt;
&lt;td&gt;Standard CRUD&lt;/td&gt;
&lt;td&gt;Complex business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Both can coexist in the same application. For further migration guidance, see &lt;a href=&quot;/docs/dg/dev/upgrade-and-migrate/migrate-to-api-platform.html&quot;&gt;How to migrate to API Platform&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;related-documentation&quot;&gt;Related documentation&lt;/h2&gt;
&lt;p&gt;For detailed implementation guides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/upgrade-and-migrate/integrate-api-platform.html&quot;&gt;How to integrate API Platform&lt;/a&gt; - Setup and configuration&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/upgrade-and-migrate/integrate-api-platform-security.html&quot;&gt;How to integrate API Platform Security&lt;/a&gt; - Authentication and authorization setup&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/upgrade-and-migrate/migrate-to-api-platform.html&quot;&gt;How to migrate to API Platform&lt;/a&gt; - Migrate endpoints from Glue API&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/configuration.html&quot;&gt;API Platform Configuration&lt;/a&gt; - Configure API Platform settings&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/security.html&quot;&gt;Security&lt;/a&gt; - Authentication and authorization&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/enablement.html&quot;&gt;API Platform Enablement&lt;/a&gt; - Creating your first resource&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/resource-schemas.html&quot;&gt;Resource Schemas&lt;/a&gt; - Resource Schemas&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/validation-schemas.html&quot;&gt;Validation Schemas&lt;/a&gt; - Validation Schemas&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/native-api-platform-resources.html&quot;&gt;Native API Platform Resources&lt;/a&gt; - Using native PHP attributes&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/code-buckets.html&quot;&gt;CodeBucket Support&lt;/a&gt; - Region-specific resources&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/sparse-fieldsets.html&quot;&gt;Sparse Fieldsets&lt;/a&gt; - Request only needed attributes&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/troubleshooting.html&quot;&gt;Troubleshooting API Platform&lt;/a&gt; - Common issues&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/upgrade-and-migrate/integrate-api-platform.html&quot;&gt;How to integrate API Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/upgrade-and-migrate/integrate-api-platform-security.html&quot;&gt;How to integrate API Platform Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/native-api-platform-resources.html&quot;&gt;Native API Platform Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/docs/dg/dev/architecture/api-platform/code-buckets.html&quot;&gt;CodeBucket Support in API Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api-platform.com/docs/&quot;&gt;API Platform official documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
            <pubDate>Tue, 19 May 2026 12:29:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/architecture/api-platform.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/architecture/api-platform.html</guid>
            
            
        </item>
        
        <item>
            <title>Keeping dependencies updated for performance</title>
            <description>Keeping your project&apos;s Spryker module dependencies up to date is critical for maintaining optimal performance, security, and reducing long-term upgrade efforts.

## Why keep dependencies updated

Updating dependencies regularly provides several practical benefits:

1. **Risk of security vulnerabilities**: Most recent versions contain necessary fixes to all known vulnerabilities.
2. **Performance and resource consumption optimizations**: Spryker continuously releases performance improvements based on real-life experiences and scenarios.
3. **Decreasing upgrade efforts**: Distributing upgrades across many smaller steps is much easier than doing one massive upgrade later.

## Key resources

The following resources provide information about performance-related module releases:

- [Security release notes 202512.0](https://docs.spryker.com/docs/about/all/releases/security-releases/security-release-notes-202512.0.html)
- [Release notes 202410.0](https://docs.spryker.com/docs/scos/user/intro-to-spryker/releases/release-notes/release-notes-202410.0/release-notes-202410.0.html)
- [Release notes 202507.0](https://docs.spryker.com/docs/scos/user/intro-to-spryker/releases/release-notes/release-notes-202507.0/release-notes-202507.0.html)
- [General performance guidelines](/docs/dg/dev/guidelines/performance-guidelines/general-performance-guidelines.html) - contains the list of recent module versions with known performance optimizations
- [Cart page performance configuration](https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/cart-page-performance-configuration.html)

## Critical module updates

The following sections list important module updates that include performance improvements. It&apos;s recommended that each Spryker project evaluates and applies these updates.

### Product search performance

**spryker/product-page-search** - Recent versions (for example, `3.38.0` - `3.40.0`) include improvements such as:
- Caching logic adjustments for product images
- Potential bulk operation support to make search document writes more efficient
- Avoiding duplicate locale events

### Session management

**spryker/session** - at least `^4.17.0`
- Provides configurable session locking mechanism
- See [Redis session lock](/docs/dg/dev/troubleshooting/troubleshooting-performance-issues/redis-session-lock.html) for configuration details

**spryker/session-redis** - at least `^1.11.2`
- Includes configurable session locker
- See [Redis session lock](/docs/dg/dev/troubleshooting/troubleshooting-performance-issues/redis-session-lock.html) for setup instructions

### Merchant Portal and Back Office performance with ACL rules

- [spryker/acl:^3.26.0](https://github.com/spryker/acl/releases/tag/3.26.0)
- [spryker/acl-entity:^1.16.0](https://github.com/spryker/acl-entity/releases/tag/1.16.0)
- [spryker/gui:^4.11.0](https://github.com/spryker/gui/releases/tag/4.11.0)
- [spryker/merchant-profile:^1.9.0](https://github.com/spryker/merchant-profile/releases/tag/1.9.0)
- [spryker/merchant-user:^1.9.0](https://github.com/spryker/merchant-user/releases/tag/1.9.0)
- [spryker/multi-factor-auth-merchant-portal:^2.1.0](https://github.com/spryker/multi-factor-auth-merchant-portal/releases/tag/2.1.0)
- [spryker/product-attribute:^1.18.0](https://github.com/spryker/product-attribute/releases/tag/1.18.0)
- [spryker/zed-navigation:^1.15.0](https://github.com/spryker/zed-navigation/releases/tag/1.15.0)

### Back Office performance on category list page

- [spryker/product-category:&quot;^4.32.0&quot;](https://github.com/spryker/zed-navigation/releases/tag/4.32.0)

### Order placement performance

- [spryker/calculation:^4.14.0](https://github.com/spryker/calculation/releases/tag/4.14.0)
- [spryker/discount-calculation-connector:^5.4.0](https://github.com/spryker/discount-calculation-connector/releases/tag/5.4.0)
- [spryker/merchant:^3.15.0](https://github.com/spryker/merchant/releases/tag/3.15.0)
- [spryker/sales:^11.60.0](https://github.com/spryker/sales/releases/tag/11.60.0)
- [spryker/product:^6.49.0](https://github.com/spryker/product/releases/tag/6.49.0)
- [spryker/discount:^9.43.0](https://github.com/spryker/discount/releases/tag/9.43.0)
- [spryker/product-cart-connector:^4.13.0](https://github.com/spryker/product-cart-connector/releases/tag/4.13.0)
- [spryker/company-role:^1.9.1](https://github.com/spryker/company-role/releases/tag/1.9.1)
- [spryker/propel:^3.43.0](https://github.com/spryker/propel/releases/tag/3.43.0)
- [spryker/sales:^11.63.0](https://github.com/spryker/sales/releases/tag/11.63.0)
- [spryker/sales-product-connector:^1.11.1](https://github.com/spryker/sales-product-connector/releases/tag/1.11.1)
- [spryker/shipment:^8.24.0](https://github.com/spryker/shipment/releases/tag/8.24.0)

### OMS availability check and order item reservation

- [spryker/availability:^9.27.0](https://github.com/spryker/availability/releases/tag/9.27.0)
- [spryker/stock:^8.10.1](https://github.com/spryker/stock/releases/tag/8.10.1)
- [spryker/oms:^11.45.1](https://github.com/spryker/oms/releases/tag/11.45.1)
- [spryker/propel:^3.43.0](https://github.com/spryker/propel/releases/tag/3.43.0)
- [spryker/sales:^11.63.0](https://github.com/spryker/sales/releases/tag/11.63.0)

### Publish and synchronization (merchant-related)

- [spryker/merchant-product-offer-storage:^2.6.0](https://github.com/spryker/merchant-product-offer-storage/releases/tag/2.6.0)
- [spryker/product-offer-storage:^1.8.0](https://github.com/spryker/product-offer-storage/releases/tag/1.8.0)
- [spryker/propel:^3.45.0](https://github.com/spryker/propel/releases/tag/3.45.0)

### Publish and synchronization (product-related)

- [spryker/price-product:^4.48.0](https://github.com/spryker/price-product/releases/tag/4.48.0)
- [spryker/product-page-search:^3.40.0](https://github.com/spryker/product-page-search/releases/tag/3.40.0)
- [spryker/product-search:^5.24.1](https://github.com/spryker/product-search/releases/tag/5.24.1)
- [spryker/product-storage:^1.47.0](https://github.com/spryker/product-storage/releases/tag/1.47.0)
- [spryker/product-offer-storage:^1.10.0](https://github.com/spryker/product-offer-storage/releases/tag/1.10.0)
- [spryker/price-product-offer:^1.7.1](https://github.com/spryker/price-product-offer/releases/tag/1.7.1)
- [spryker/price-product-offer-storage:^1.5.1](https://github.com/spryker/price-product-offer-storage/releases/tag/1.5.1)
- [spryker/price-product-storage:^4.13.0](https://github.com/spryker/price-product-storage/releases/tag/4.13.0)
- [spryker/product-image:^3.20.1](https://github.com/spryker/product-image/releases/tag/3.20.1)
- [spryker/product-category-storage:^2.11.0](https://github.com/spryker/product-category-storage/releases/tag/2.11.0)
- [spryker/product-category-search:^1.2.1](https://github.com/spryker/product-category-search/releases/tag/1.2.1)
- [spryker/propel:^3.47.0](https://github.com/spryker/propel/releases/tag/3.47.0)
  - Note: If you still use destructive deployments, update the `config/install/destructive.yml` file. You can copy it from any demo shop.
- [spryker/event-behavior:^1.32.0](https://github.com/spryker/event-behavior/releases/tag/1.32.0)
- [spryker/synchronization-behavior:^1.13.0](https://github.com/spryker/synchronization-behavior/releases/tag/1.13.0)

### Publish and synchronization (Category tree build logic)

- [spryker/category:&quot;^5.23.0&quot;](https://github.com/spryker/category/releases/tag/5.23.0)
- [spryker/category-storage:&quot;^2.12.0&quot;](https://github.com/spryker/category-storage/releases/tag/2.12.0)
- [spryker/propel-orm:&quot;^1.22.0&quot;](https://github.com/spryker/propel-orm/releases/tag/1.22.0)
- [spryker/util-sanitize:&quot;^2.3.1&quot;](https://github.com/spryker/util-sanitize/releases/tag/2.3.1)

### Publish and synchronization (queue and event performance)

To update all required modules at once:

```bash
composer update spryker/rabbit-mq:&quot;^2.25.0&quot; spryker/availability-storage:&quot;^2.11.0&quot; spryker/event:&quot;^2.17.1&quot; spryker/glossary-storage:&quot;^1.14.0&quot; spryker/merchant-product:&quot;^1.12.0&quot; spryker/merchant-product-offer:&quot;^1.12.0&quot; spryker/merchant-product-offer-search:&quot;^1.9.0&quot; spryker/merchant-product-search:&quot;^1.6.0&quot; spryker/merchant-product-storage:&quot;^1.7.0&quot; spryker/price-product:&quot;^4.51.0&quot; spryker/price-product-merchant-relationship-storage:&quot;^1.21.0&quot; spryker/price-product-offer-storage:&quot;^1.8.0&quot; spryker/price-product-storage:&quot;^4.16.0&quot; spryker/product:&quot;^6.54.0&quot; spryker/product-category-search:&quot;^1.3.0&quot; spryker/product-category-storage:&quot;^2.13.0&quot; spryker/product-group-storage:&quot;^1.9.0&quot; spryker/product-image:&quot;^3.21.0&quot; spryker/product-image-storage:&quot;^1.21.0&quot; spryker/product-list:&quot;^1.10.0&quot; spryker/product-list-search:&quot;^2.11.0&quot; spryker/product-list-storage:&quot;^1.21.0&quot; spryker/product-offer:&quot;^1.17.0&quot; spryker/product-offer-availability-storage:&quot;^1.6.0&quot; spryker/product-offer-stock:&quot;^1.7.0&quot; spryker/product-page-search:&quot;^3.47.0&quot; spryker/product-page-search-extension:&quot;^1.7.0&quot; spryker/propel:&quot;^3.50.0&quot; spryker/queue:&quot;^1.27.0&quot; spryker/symfony-messenger:&quot;^1.6.0&quot; spryker/tax-product-storage:&quot;^1.8.0&quot; spryker/url-storage:&quot;^1.23.0&quot; spryker/synchronization-behavior:&quot;^1.14.0&quot;
```

- [spryker/rabbit-mq:^2.25.0](https://github.com/spryker/rabbit-mq/releases/tag/2.25.0)
- [spryker/availability-storage:^2.11.0](https://github.com/spryker/availability-storage/releases/tag/2.11.0)
- [spryker/event:^2.17.1](https://github.com/spryker/event/releases/tag/2.17.1)
- [spryker/event-behavior:^1.35.0](https://github.com/spryker/event-behavior/releases/tag/1.35.0)
- [spryker/glossary-storage:^1.14.0](https://github.com/spryker/glossary-storage/releases/tag/1.14.0)
- [spryker/merchant-product:^1.12.0](https://github.com/spryker/merchant-product/releases/tag/1.12.0)
- [spryker/merchant-product-offer:^1.12.0](https://github.com/spryker/merchant-product-offer/releases/tag/1.12.0)
- [spryker/merchant-product-offer-search:^1.9.0](https://github.com/spryker/merchant-product-offer-search/releases/tag/1.9.0)
- [spryker/merchant-product-search:^1.6.0](https://github.com/spryker/merchant-product-search/releases/tag/1.6.0)
- [spryker/merchant-product-storage:^1.7.0](https://github.com/spryker/merchant-product-storage/releases/tag/1.7.0)
- [spryker/price-product:^4.51.0](https://github.com/spryker/price-product/releases/tag/4.51.0)
- [spryker/price-product-merchant-relationship-storage:^1.21.0](https://github.com/spryker/price-product-merchant-relationship-storage/releases/tag/1.21.0)
- [spryker/price-product-offer-storage:^1.8.0](https://github.com/spryker/price-product-offer-storage/releases/tag/1.8.0)
- [spryker/price-product-storage:^4.16.0](https://github.com/spryker/price-product-storage/releases/tag/4.16.0)
- [spryker/product:^6.54.0](https://github.com/spryker/product/releases/tag/6.54.0)
- [spryker/product-category-search:^1.3.0](https://github.com/spryker/product-category-search/releases/tag/1.3.0)
- [spryker/product-category-storage:^2.13.0](https://github.com/spryker/product-category-storage/releases/tag/2.13.0)
- [spryker/product-group-storage:^1.9.0](https://github.com/spryker/product-group-storage/releases/tag/1.9.0)
- [spryker/product-image:^3.21.0](https://github.com/spryker/product-image/releases/tag/3.21.0)
- [spryker/product-image-storage:^1.21.0](https://github.com/spryker/product-image-storage/releases/tag/1.21.0)
- [spryker/product-list:^1.10.0](https://github.com/spryker/product-list/releases/tag/1.10.0)
- [spryker/product-list-search:^2.11.0](https://github.com/spryker/product-list-search/releases/tag/2.11.0)
- [spryker/product-list-storage:^1.21.0](https://github.com/spryker/product-list-storage/releases/tag/1.21.0)
- [spryker/product-offer:^1.17.0](https://github.com/spryker/product-offer/releases/tag/1.17.0)
- [spryker/product-offer-availability-storage:^1.6.0](https://github.com/spryker/product-offer-availability-storage/releases/tag/1.6.0)
- [spryker/product-offer-stock:^1.7.0](https://github.com/spryker/product-offer-stock/releases/tag/1.7.0)
- [spryker/product-page-search:^3.47.0](https://github.com/spryker/product-page-search/releases/tag/3.47.0)
- [spryker/product-page-search-extension:^1.7.0](https://github.com/spryker/product-page-search-extension/releases/tag/1.7.0)
- [spryker/propel:^3.50.0](https://github.com/spryker/propel/releases/tag/3.50.0)
- [spryker/queue:^1.27.0](https://github.com/spryker/queue/releases/tag/1.27.0)
- [spryker/symfony-messenger:^1.6.0](https://github.com/spryker/symfony-messenger/releases/tag/1.6.0)
- [spryker/tax-product-storage:^1.8.0](https://github.com/spryker/tax-product-storage/releases/tag/1.8.0)
- [spryker/url-storage:^1.23.0](https://github.com/spryker/url-storage/releases/tag/1.23.0)
- [spryker/synchronization-behavior:^1.14.0](https://github.com/spryker/synchronization-behavior/releases/tag/1.14.0)

### Data import (memory usage)

- [spryker/acl-entity:&quot;^1.17.0&quot;](https://github.com/spryker/acl-entity/releases/tag/1.17.0)
- [spryker/data-import:&quot;^1.33.0&quot;](https://github.com/spryker/data-import/releases/tag/1.33.0)
- [spryker/merchant-relationship-product-list-data-import:&quot;^0.1.3&quot;](https://github.com/spryker/merchant-relationship-product-list-data-import/releases/tag/0.1.3)
- [spryker/price-product-merchant-relationship-data-import:&quot;^0.2.5&quot;](https://github.com/spryker/price-product-merchant-relationship-data-import/releases/tag/0.2.5)

### Dynamic entity performance improvements

- [spryker/dynamic-entity:^1.21.0](https://github.com/spryker/dynamic-entity/releases/tag/1.21.0)
- [spryker/dynamic-entity-backend-api:^1.15.0](https://github.com/spryker/dynamic-entity-backend-api/releases/tag/1.15.0)
- [spryker/stock:^8.15.0](https://github.com/spryker/stock/releases/tag/8.15.0)

### Cart page and checkout for large carts (100+ items)

For comprehensive guidance on optimizing cart performance, see [Cart page performance configuration](https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/cart-page-performance-configuration.html).

- [spryker-shop/checkout-page:^3.41.0](https://github.com/spryker-shop/checkout-page/releases/tag/3.41.0)
- [spryker-shop/session-customer-validation-page:^1.4.0](https://github.com/spryker-shop/session-customer-validation-page/releases/tag/1.4.0)

## Update strategy

To effectively manage dependency updates:

1. **Monitor release notes**: Regularly check Spryker release notes for performance-related updates.
2. **Test in staging**: Always test module updates in a staging environment before production deployment.
3. **Prioritize performance modules**: Focus on modules that directly impact your application&apos;s performance bottlenecks.
4. **Use semantic versioning**: Understand the impact of major, minor, and patch updates.
5. **Batch related updates**: Group related module updates together for testing efficiency.

## Compatibility considerations

When updating modules:

- Check module compatibility with your current Spryker version
- Review breaking changes in major version updates
- Test all affected functionality after updates
- Monitor application performance metrics before and after updates
- Consider using Spryker&apos;s [Composer Dependency Manager](https://docs.spryker.com/docs/scos/dev/setup/managing-scos-dependencies-with-composer.html)
</description>
            <pubDate>Tue, 19 May 2026 08:42:59 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/guidelines/performance-guidelines/keeping-dependencies-updated.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/guidelines/performance-guidelines/keeping-dependencies-updated.html</guid>
            
            
        </item>
        
    </channel>
</rss>
