Install the Products Backend API
Edit on GitHubThis document describes how to install the Products Backend API, which exposes concrete and abstract product data at /products on the Glue Backend API.
Prerequisites
Install the required features:
| NAME | VERSION | INSTALLATION GUIDE |
|---|---|---|
| Spryker Core | 202608.0 | Install the Spryker Core feature |
| Self-Service Portal | 202608.0 | Install Self-Service Portal |
The Self-Service Portal feature is optional. Install it only to use the productClass and shipmentType attributes.
Step 1 installs the remaining modules the API depends on, so you do not have to install them beforehand.
The API resource is generated by API Platform. If your project does not use API Platform yet, follow Integrate API Platform before you continue.
Install feature core
1) Install the required modules
Install the feature and API Platform using Composer:
composer require spryker-feature/product-experience-management:"202608.0" spryker/api-platform:"^1.0.0" --update-with-dependencies
The Product Experience Management feature declares spryker/api-platform as a suggested dependency, not a required one. Install it explicitly, as shown above. Without it, the /products resource is never registered and the endpoint returns 404.
The API registers plugins that ship in modules the feature does not constrain to the versions providing them. Update those modules explicitly:
composer require \
spryker/category:"^5.25.0" \
spryker/price-product:"^4.54.0" \
spryker/product:"^6.57.0" \
spryker/product-attribute:"^1.22.0" \
spryker/product-bundle:"^7.29.0" \
spryker/product-category:"^4.34.0" \
spryker/product-extension:"^1.6.0" \
spryker/product-image:"^3.22.0" \
spryker/shipment-type:"^1.3.0" \
spryker/stock:"^8.17.0" \
spryker/tax:"^5.20.0" \
spryker/tax-product-connector:"^4.13.0" \
spryker/uuid-behavior:"^1.3.0" \
--update-with-dependencies
What each version provides
| MODULE | MINIMUM VERSION | PROVIDES |
|---|---|---|
| spryker/product | ^6.57.0 | Collection writers and the plugin stacks registered in step 4 |
| spryker/product-extension | ^1.6.0 | Interfaces of the collection validator plugins |
| spryker/category | ^5.25.0 | CategoryExistsProductAbstractCollection*ValidatorPlugin, spy_category.uuid |
| spryker/tax | ^5.20.0 | spy_tax_set.uuid |
| spryker/tax-product-connector | ^4.13.0 | TaxSetExistsProductAbstractCollection*ValidatorPlugin |
| spryker/price-product | ^4.54.0 | Price collection validator plugins, spy_price_product_store.uuid |
| spryker/product-image | ^3.22.0 | Image set expander and validator plugins, spy_product_image_set.uuid |
| spryker/product-category | ^4.34.0 | ProductCategoryAbstractCollectionExpanderPlugin |
| spryker/product-attribute | ^1.22.0 | SuperAttributeProductConcreteExpanderPlugin |
| spryker/product-bundle | ^7.29.0 | ProductBundleProductConcreteCollection*ValidatorPlugin |
| spryker/shipment-type | ^1.3.0 | ShipmentTypeExistsProductConcreteCollection*ValidatorPlugin |
| spryker/stock | ^8.17.0 | StockProductConcreteCollectionAfterUpdatePlugin, stock validator plugins |
| spryker/uuid-behavior | ^1.3.0 | UUID behavior used by the new schema definitions |
To use the productClass and shipmentType attributes, install the Self-Service Portal feature as well:
composer require spryker-feature/self-service-portal:"202608.0" --update-with-dependencies
Make sure the following modules have been installed:
| MODULE | EXPECTED DIRECTORY |
|---|---|
| ProductExperienceManagement | vendor/spryker-feature/product-experience-management |
| ApiPlatform | vendor/spryker/api-platform |
Make sure the installed module versions are at least the ones listed above. If a validator plugin class cannot be found in step 4, the corresponding module is below its minimum version.
2) Set up configuration
The API references categories, tax sets, prices, and image sets by UUID. Enable the UUID columns for those entities.
src/Pyz/Zed/Category/CategoryConfig.php
<?php
namespace Pyz\Zed\Category;
use Spryker\Zed\Category\CategoryConfig as SprykerCategoryConfig;
class CategoryConfig extends SprykerCategoryConfig
{
/**
* @return bool
*/
public function isCategoryUuidEnabled(): bool
{
return true;
}
}
src/Pyz/Zed/PriceProduct/PriceProductConfig.php
<?php
namespace Pyz\Zed\PriceProduct;
use Spryker\Zed\PriceProduct\PriceProductConfig as SprykerPriceProductConfig;
class PriceProductConfig extends SprykerPriceProductConfig
{
/**
* @return bool
*/
public function isPriceProductStoreUuidEnabled(): bool
{
return true;
}
}
src/Pyz/Zed/ProductImage/ProductImageConfig.php
<?php
namespace Pyz\Zed\ProductImage;
use Spryker\Zed\ProductImage\ProductImageConfig as SprykerProductImageConfig;
class ProductImageConfig extends SprykerProductImageConfig
{
/**
* @return bool
*/
public function isProductImageSetUuidEnabled(): bool
{
return true;
}
}
src/Pyz/Zed/Tax/TaxConfig.php
<?php
namespace Pyz\Zed\Tax;
use Spryker\Zed\Tax\TaxConfig as SprykerTaxConfig;
class TaxConfig extends SprykerTaxConfig
{
/**
* @return bool
*/
public function isTaxSetUuidEnabled(): bool
{
return true;
}
}
3) Set up the database schema and transfer objects
Apply database changes and generate entity and transfer changes:
console propel:install
console transfer:generate
Make sure that the following changes have occurred in the database:
| DATABASE ENTITY | TYPE | EVENT |
|---|---|---|
| spy_category.uuid | column | created |
| spy_tax_set.uuid | column | created |
| spy_price_product_store.uuid | column | created |
| spy_product_image_set.uuid | column | created |
| spy_stock.uuid | column | created |
Make sure that the following changes have been applied in transfer objects:
| TRANSFER | TYPE | EVENT | PATH |
|---|---|---|---|
| Category.uuid | property | created | src/Generated/Shared/Transfer/CategoryTransfer |
| MoneyValue.uuid | property | created | src/Generated/Shared/Transfer/MoneyValueTransfer |
| ProductImageSet.uuid | property | created | src/Generated/Shared/Transfer/ProductImageSetTransfer |
| StockProduct.stockUuid | property | created | src/Generated/Shared/Transfer/StockProductTransfer |
| ProductConcrete.validFrom | property | created | src/Generated/Shared/Transfer/ProductConcreteTransfer |
| ProductConcrete.validTo | property | created | src/Generated/Shared/Transfer/ProductConcreteTransfer |
| ProductConcrete.productBundle | property | created | src/Generated/Shared/Transfer/ProductConcreteTransfer |
| ProductConcrete.productClasses | property | created | src/Generated/Shared/Transfer/ProductConcreteTransfer |
| ProductConcrete.shipmentTypes | property | created | src/Generated/Shared/Transfer/ProductConcreteTransfer |
| ProductClass | class | created | src/Generated/Shared/Transfer/ProductClassTransfer |
| ProductForBundle | class | created | src/Generated/Shared/Transfer/ProductForBundleTransfer |
| ProductAbstractTaxSetCollection | class | created | src/Generated/Shared/Transfer/ProductAbstractTaxSetCollectionTransfer |
| ProductAbstractRelations.withTaxSet | property | created | src/Generated/Shared/Transfer/ProductAbstractRelationsTransfer |
propel:install adds the UUID columns, but it leaves them empty for rows that already exist. Generate the missing values:
console uuid:generate Category spy_category
console uuid:generate Tax spy_tax_set
console uuid:generate PriceProduct spy_price_product_store
console uuid:generate ProductImage spy_product_image_set
console uuid:generate Stock spy_stock
Each command reports how many records it updated. Commands for tables that already have UUIDs report zero records and make no changes.
Until you run these commands, existing categories, tax sets, prices, image sets, and warehouses have no UUID. Requests that reference them by UUID fail with a 422 validation error, and read responses return those UUIDs as null.
4) Set up behavior
Register the plugins that validate references and apply cross-module changes for collection operations.
| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
|---|---|---|---|
| SuperAttributeProductConcreteExpanderPlugin | Expands concrete products with their super attribute values. | None | Spryker\Zed\ProductAttribute\Communication\Plugin\Product |
| ProductImageAbstractCollectionExpanderPlugin | Expands abstract product collections with image sets. | None | Spryker\Zed\ProductImage\Communication\Plugin\Product |
| ProductCategoryAbstractCollectionExpanderPlugin | Expands abstract product collections with category assignments. | None | Spryker\Zed\ProductCategory\Communication\Plugin\Product |
| StockProductConcreteCollectionAfterUpdatePlugin | Persists stock changes after a concrete product collection is updated. | None | Spryker\Zed\Stock\Communication\Plugin\Product |
| PriceProductConcreteCollectionCreateValidatorPlugin | Validates prices when concrete products are created. | None | Spryker\Zed\PriceProduct\Communication\Plugin\Product |
| ProductImageSetExistsProductConcreteCollectionCreateValidatorPlugin | Validates that referenced image sets exist. | None | Spryker\Zed\ProductImage\Communication\Plugin\Product |
| StockExistsProductConcreteCollectionCreateValidatorPlugin | Validates that referenced warehouses exist. | None | Spryker\Zed\Stock\Communication\Plugin\Product |
| ShipmentTypeExistsProductConcreteCollectionCreateValidatorPlugin | Validates that referenced shipment types exist. | None | Spryker\Zed\ShipmentType\Communication\Plugin\Product |
| ProductClassExistsProductConcreteCollectionCreateValidatorPlugin | Validates that referenced product classes exist. | Self-Service Portal feature | SprykerFeature\Zed\SelfServicePortal\Communication\Plugin\Product |
| ProductBundleProductConcreteCollectionCreateValidatorPlugin | Validates bundled product assignments. | None | Spryker\Zed\ProductBundle\Communication\Plugin\Product |
| CategoryExistsProductAbstractCollectionCreateValidatorPlugin | Validates that referenced categories exist. | None | Spryker\Zed\Category\Communication\Plugin\Product |
| TaxSetExistsProductAbstractCollectionCreateValidatorPlugin | Validates that referenced tax sets exist. | None | Spryker\Zed\TaxProductConnector\Communication\Plugin\Product |
| PriceProductAbstractCollectionCreateValidatorPlugin | Validates prices when abstract products are created. | None | Spryker\Zed\PriceProduct\Communication\Plugin\Product |
The update validator plugins mirror the create validator plugins and live in the same namespaces.
Add the plugins to ProductDependencyProvider. getProductConcreteExpanderPlugins() and getProductAbstractCollectionExpanderPlugins() already exist in most projects — add the listed plugins to the stacks you have instead of replacing them.
src/Pyz/Zed/Product/ProductDependencyProvider.php
<?php
namespace Pyz\Zed\Product;
use Spryker\Zed\Category\Communication\Plugin\Product\CategoryExistsProductAbstractCollectionCreateValidatorPlugin;
use Spryker\Zed\Category\Communication\Plugin\Product\CategoryExistsProductAbstractCollectionUpdateValidatorPlugin;
use Spryker\Zed\PriceProduct\Communication\Plugin\Product\PriceProductAbstractCollectionCreateValidatorPlugin;
use Spryker\Zed\PriceProduct\Communication\Plugin\Product\PriceProductConcreteCollectionCreateValidatorPlugin;
use Spryker\Zed\PriceProduct\Communication\Plugin\Product\PriceProductConcreteCollectionUpdateValidatorPlugin;
use Spryker\Zed\Product\ProductDependencyProvider as SprykerProductDependencyProvider;
use Spryker\Zed\ProductAttribute\Communication\Plugin\Product\SuperAttributeProductConcreteExpanderPlugin;
use Spryker\Zed\ProductBundle\Communication\Plugin\Product\ProductBundleDeactivatorProductConcreteAfterUpdatePlugin;
use Spryker\Zed\ProductBundle\Communication\Plugin\Product\ProductBundleProductConcreteAfterUpdatePlugin;
use Spryker\Zed\ProductBundle\Communication\Plugin\Product\ProductBundleProductConcreteCollectionCreateValidatorPlugin;
use Spryker\Zed\ProductBundle\Communication\Plugin\Product\ProductBundleProductConcreteCollectionUpdateValidatorPlugin;
use Spryker\Zed\ProductCategory\Communication\Plugin\Product\ProductCategoryAbstractCollectionExpanderPlugin;
use Spryker\Zed\ProductImage\Communication\Plugin\Product\ProductImageAbstractCollectionExpanderPlugin;
use Spryker\Zed\ProductImage\Communication\Plugin\Product\ProductImageSetExistsProductConcreteCollectionCreateValidatorPlugin;
use Spryker\Zed\ProductImage\Communication\Plugin\Product\ProductImageSetExistsProductConcreteCollectionUpdateValidatorPlugin;
use Spryker\Zed\PriceProduct\Communication\Plugin\ProductConcrete\PriceProductConcreteAfterUpdatePlugin;
use Spryker\Zed\ProductImage\Communication\Plugin\ProductConcreteAfterUpdatePlugin as ImageSetProductConcreteAfterUpdatePlugin;
use Spryker\Zed\ProductSearch\Communication\Plugin\ProductConcrete\ProductSearchProductConcreteAfterUpdatePlugin;
use Spryker\Zed\ProductValidity\Communication\Plugin\ProductValidityUpdatePlugin;
use Spryker\Zed\ShipmentType\Communication\Plugin\Product\ShipmentTypeExistsProductConcreteCollectionCreateValidatorPlugin;
use Spryker\Zed\ShipmentType\Communication\Plugin\Product\ShipmentTypeExistsProductConcreteCollectionUpdateValidatorPlugin;
use Spryker\Zed\Stock\Communication\Plugin\Product\StockExistsProductConcreteCollectionCreateValidatorPlugin;
use Spryker\Zed\Stock\Communication\Plugin\Product\StockExistsProductConcreteCollectionUpdateValidatorPlugin;
use Spryker\Zed\Stock\Communication\Plugin\Product\StockProductConcreteCollectionAfterUpdatePlugin;
use Spryker\Zed\TaxProductConnector\Communication\Plugin\Product\TaxSetExistsProductAbstractCollectionCreateValidatorPlugin;
use Spryker\Zed\TaxProductConnector\Communication\Plugin\Product\TaxSetExistsProductAbstractCollectionUpdateValidatorPlugin;
use Spryker\Zed\TaxProductConnector\Communication\Plugin\Product\TaxSetProductAbstractCollectionExpanderPlugin;
use SprykerFeature\Zed\SelfServicePortal\Communication\Plugin\Product\ProductClassExistsProductConcreteCollectionCreateValidatorPlugin;
use SprykerFeature\Zed\SelfServicePortal\Communication\Plugin\Product\ProductClassExistsProductConcreteCollectionUpdateValidatorPlugin;
use SprykerFeature\Zed\SelfServicePortal\Communication\Plugin\Product\ProductClassProductConcreteAfterUpdatePlugin;
use SprykerFeature\Zed\SelfServicePortal\Communication\Plugin\Product\ShipmentTypeProductConcretePostUpdatePlugin;
class ProductDependencyProvider extends SprykerProductDependencyProvider
{
/**
* @return array<\Spryker\Zed\ProductExtension\Dependency\Plugin\ProductConcreteExpanderPluginInterface>
*/
protected function getProductConcreteExpanderPlugins(): array
{
return [
// Keep the plugins your project already registers.
new SuperAttributeProductConcreteExpanderPlugin(),
];
}
/**
* @return array<\Spryker\Zed\ProductExtension\Dependency\Plugin\ProductAbstractCollectionExpanderPluginInterface>
*/
protected function getProductAbstractCollectionExpanderPlugins(): array
{
return [
new TaxSetProductAbstractCollectionExpanderPlugin(),
new ProductImageAbstractCollectionExpanderPlugin(),
new ProductCategoryAbstractCollectionExpanderPlugin(),
];
}
/**
* @return array<\Spryker\Zed\Product\Dependency\Plugin\ProductConcretePluginUpdateInterface>
*/
protected function getProductConcreteAfterUpdateCollectionPlugins(): array
{
return [
new ImageSetProductConcreteAfterUpdatePlugin(),
new PriceProductConcreteAfterUpdatePlugin(),
new StockProductConcreteCollectionAfterUpdatePlugin(),
new ProductSearchProductConcreteAfterUpdatePlugin(),
new ProductValidityUpdatePlugin(),
new ProductClassProductConcreteAfterUpdatePlugin(),
new ShipmentTypeProductConcretePostUpdatePlugin(),
new ProductBundleProductConcreteAfterUpdatePlugin(),
new ProductBundleDeactivatorProductConcreteAfterUpdatePlugin(),
];
}
/**
* @return array<\Spryker\Zed\ProductExtension\Dependency\Plugin\ProductConcreteCollectionCreateValidatorPluginInterface>
*/
protected function getProductConcreteCollectionCreateValidatorPlugins(): array
{
return [
new PriceProductConcreteCollectionCreateValidatorPlugin(),
new ProductImageSetExistsProductConcreteCollectionCreateValidatorPlugin(),
new StockExistsProductConcreteCollectionCreateValidatorPlugin(),
new ShipmentTypeExistsProductConcreteCollectionCreateValidatorPlugin(),
new ProductClassExistsProductConcreteCollectionCreateValidatorPlugin(),
new ProductBundleProductConcreteCollectionCreateValidatorPlugin(),
];
}
/**
* @return array<\Spryker\Zed\ProductExtension\Dependency\Plugin\ProductConcreteCollectionUpdateValidatorPluginInterface>
*/
protected function getProductConcreteCollectionUpdateValidatorPlugins(): array
{
return [
new PriceProductConcreteCollectionUpdateValidatorPlugin(),
new ProductImageSetExistsProductConcreteCollectionUpdateValidatorPlugin(),
new StockExistsProductConcreteCollectionUpdateValidatorPlugin(),
new ShipmentTypeExistsProductConcreteCollectionUpdateValidatorPlugin(),
new ProductClassExistsProductConcreteCollectionUpdateValidatorPlugin(),
new ProductBundleProductConcreteCollectionUpdateValidatorPlugin(),
];
}
/**
* @return array<\Spryker\Zed\ProductExtension\Dependency\Plugin\ProductAbstractCollectionCreateValidatorPluginInterface>
*/
protected function getProductAbstractCollectionCreateValidatorPlugins(): array
{
return [
new CategoryExistsProductAbstractCollectionCreateValidatorPlugin(),
new TaxSetExistsProductAbstractCollectionCreateValidatorPlugin(),
new PriceProductAbstractCollectionCreateValidatorPlugin(),
];
}
/**
* @return array<\Spryker\Zed\ProductExtension\Dependency\Plugin\ProductAbstractCollectionUpdateValidatorPluginInterface>
*/
protected function getProductAbstractCollectionUpdateValidatorPlugins(): array
{
return [
new CategoryExistsProductAbstractCollectionUpdateValidatorPlugin(),
new TaxSetExistsProductAbstractCollectionUpdateValidatorPlugin(),
];
}
}
If a validator plugin is missing, the API accepts references to entities that do not exist and the write fails later with a database error instead of a 422 validation error. If an expander plugin is missing, the corresponding data is absent from read responses.
5) Generate the API resources
Clear the Glue Backend cache and regenerate the API resources:
rm -rf data/cache/GlueBackend/<environment>
docker/sdk cli "GLUE_APPLICATION=GLUE_BACKEND vendor/bin/glue api:generate"
docker/sdk cli "GLUE_APPLICATION=GLUE_BACKEND vendor/bin/glue cache:clear"
Replace <environment> with your environment name, for example, development.
Verification
Request a Back Office access token as described in Authenticate as a Back Office user, then retrieve a product collection:
curl "https://glue-backend.mysprykershop.com/products?perPage=1" \
-H "Authorization: Bearer {access_token}"
The integration is successful when the request returns 200 with a data array. A 404 means the resource was not generated — repeat step 5 and confirm that spryker/api-platform is installed. A 401 means the access token is missing or expired.
Thank you!
For submitting the form