Introduction
In this blog post, we will walk through how to build a custom Customer API in Spryker, utilizing Glue and RESTful principles to retrieve customer data in a structured way. We’ll break down each component of the code and explain its functionality, showing you how to implement a Customer API that integrates seamlessly into Spryker’s architecture.
Setting the Scene: What Are We Building?
The aim of this API is to expose customer details to external systems in a standardized JSON API format. This guide will walk through the steps, showing how to integrate customer data, implement the response structure, and handle API requests effectively.
Step 1: Define the Factory (CustomCustomerApiFactory.php)
A key component of any Spryker module is the factory, which manages the creation of dependencies. In our case, the CustomCustomerApiFactory class will handle the dependencies related to the customer API and provide a mechanism to fetch data.
Here’s a breakdown of the methods:
- getRestResourceBuilder(): This method provides the REST resource builder required for generating the response format.
- getCustomerClient(): This method returns an instance of the CustomerClient, allowing us to fetch customer data using the findCustomerById method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php namespace Pyz\Glue\CustomCustomerApi; use Spryker\Glue\GlueApplication\Rest\JsonApi\RestResourceBuilderInterface; use Spryker\Glue\Kernel\AbstractFactory; use Spryker\Client\Customer\CustomerClientInterface; class CustomCustomerApiFactory extends AbstractFactory { public function getRestResourceBuilder(): RestResourceBuilderInterface { return $this->getProvidedDependency(CustomCustomerApiDependencyProvider::RESOURCE_BUILDER); } /** * Get CustomerClient instance. * * @return \Spryker\Client\Customer\CustomerClientInterface */ public function getCustomerClient(): CustomerClientInterface { return $this->getProvidedDependency(CustomCustomerApiDependencyProvider::CLIENT_CUSTOMER); } } ?> |
Step 2: Managing Dependencies (CustomCustomerApiDependencyProvider.php)
Dependencies in Spryker are managed using the DependencyProvider class. This class is used to bind the dependencies that the factory will use
- RESOURCE_BUILDER: We define this constant to point to the GlueApplicationDependencyProvider::RESOURCE_BUILDER.
- CLIENT_CUSTOMER: This dependency provides the customer client, allowing us to interact with customer data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php namespace Pyz\Glue\CustomCustomerApi; use Spryker\Glue\GlueApplication\GlueApplicationDependencyProvider; use Spryker\Glue\GlueApplication\Rest\JsonApi\RestResourceBuilderInterface; use Spryker\Glue\Kernel\AbstractBundleDependencyProvider; use Spryker\Glue\Kernel\Container; use Spryker\Client\Customer\CustomerClientInterface; class CustomCustomerApiDependencyProvider extends AbstractBundleDependencyProvider { public const RESOURCE_BUILDER = 'RESOURCE_BUILDER'; public const CLIENT_CUSTOMER = 'CLIENT_CUSTOMER'; /** * Provide dependencies for CustomCustomerApi module * * @param \Spryker\Glue\Kernel\Container $container * @return \Spryker\Glue\Kernel\Container */ public function provideDependencies(Container $container): Container { $container->set(static::RESOURCE_BUILDER, function (Container $container): RestResourceBuilderInterface { return $container->get(GlueApplicationDependencyProvider::RESOURCE_BUILDER); }); // Use CustomerClient instead of Facade $container->set(static::CLIENT_CUSTOMER, function (Container $container): CustomerClientInterface { return $container->getLocator()->customer()->client(); }); return $container; } } ?> |
Step 3: Configuring Resource Routes (CustomCustomerApiResourcePlugin.php)
In Spryker, plugins are used to extend functionality.The CustomCustomerApiResourcePlugin defines the route and resources exposed by the API. Here, we’re setting up a GET endpoint that fetches customer data.
- configure(): Configures the route for the resource. We’re setting up a GET method.
- getResourceType(): Defines the resource type, which is “customcustomerapi”.
- getController(): The controller that will handle the API request.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<?php namespace Pyz\Glue\CustomCustomerApi\Plugin\GlueApplication; use Spryker\Glue\Kernel\AbstractPlugin; use Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\ResourceRoutePluginInterface; use Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\ResourceRouteCollectionInterface; /** * @method \Pyz\Glue\CustomCustomerApi\CustomCustomerApiFactory getFactory() */ class CustomCustomerApiResourcePlugin extends AbstractPlugin implements ResourceRoutePluginInterface { /** * @inheritDoc */ public function configure(ResourceRouteCollectionInterface $resourceRouteCollection): ResourceRouteCollectionInterface { return $resourceRouteCollection->addGet('get', true); } /** * @inheritDoc */ public function getResourceType(): string { return 'customcustomerapi'; } /** * @inheritDoc */ public function getController(): string { return 'custom-customer-api-resource'; } /** * @inheritDoc */ public function getModuleName(): string { return 'CustomCustomerApi'; } /** * @inheritDoc */ public function getResourceAttributesClassName(): string { return \Generated\Shared\Transfer\CustomerTransfer::class; } } ?> |
Step 4: Fetching Customer Data (CustomCustomerApiResourceController.php)
The controller is the heart of the logic behind our API. In the getAction() method, we retrieve the customer’s ID from the request, fetch the data from the customer client, and prepare the response.
- If the customer ID is missing or invalid, we return an error response.
- Once the customer is found, we return their data in a structured RestCustomCustomerApiAttributesTransfer object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php namespace Pyz\Glue\CustomCustomerApi\Controller; use Spryker\Glue\GlueApplication\Rest\JsonApi\RestResponseInterface; use Spryker\Glue\GlueApplication\Rest\Request\Data\RestRequestInterface; use Spryker\Glue\Kernel\Controller\AbstractController; use Generated\Shared\Transfer\RestCustomCustomerApiAttributesTransfer; use Generated\Shared\Transfer\CustomerTransfer; use Pyz\Glue\CustomCustomerApi\Processor\Response\CustomCustomerApiResponseBuilder; class CustomCustomerApiResourceController extends AbstractController { /** * Fetch and return customer details * * @param RestRequestInterface $restRequest * @return RestResponseInterface */ public function getAction(RestRequestInterface $restRequest): RestResponseInterface { $customerId = (int) $restRequest->getHttpRequest()->query->get('customerId'); if (!$customerId) { return (new CustomCustomerApiResponseBuilder())->createErrorResponse('Invalid customer ID.'); } // Prepare CustomerTransfer $customerTransfer = new CustomerTransfer(); $customerTransfer->setIdCustomer($customerId); // Fetch customer data using the CustomerFacade $customerTransfer = $this->getFactory() ->getCustomerClient() ->findCustomerById($customerTransfer); if (!$customerTransfer || !$customerTransfer->getIdCustomer()) { return (new CustomCustomerApiResponseBuilder())->createErrorResponse('Customer not found.'); } // Populate customer attributes $restCustomerAttributes = new RestCustomCustomerApiAttributesTransfer(); $restCustomerAttributes->setIdCustomer($customerTransfer->getIdCustomer()); $restCustomerAttributes->setFirstName($customerTransfer->getFirstName()); $restCustomerAttributes->setLastName($customerTransfer->getLastName()); $restCustomerAttributes->setEmail($customerTransfer->getEmail()); $restCustomerAttributes->setGender($customerTransfer->getGender()); $restCustomerAttributes->setDateOfBirth($customerTransfer->getDateOfBirth()); $restCustomerAttributes->setSalutation($customerTransfer->getSalutation()); $restCustomerAttributes->setCreatedAt($customerTransfer->getCreatedAt()); $restCustomerAttributes->setUpdatedAt($customerTransfer->getUpdatedAt()); $restCustomerAttributes->setMessage('Customer data retrieved successfully!'); return (new CustomCustomerApiResponseBuilder())->createSuccessResponse($restCustomerAttributes); } } ?> |
Step 5: Building the Response (CustomCustomerApiResponseBuilder.php)
The CustomCustomerApiResponseBuilder is responsible for creating responses in the expected format. It builds both success and error responses using the appropriate RestResource and RestErrorMessageTransfer.
- Success Response: Creates a RestResponse with customer attributes.
- Error Response: Handles error scenarios by returning the appropriate status code and message.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php namespace Pyz\Glue\CustomCustomerApi\Processor\Response; use Generated\Shared\Transfer\RestCustomCustomerApiAttributesTransfer; use Generated\Shared\Transfer\RestErrorMessageTransfer; use Spryker\Glue\GlueApplication\Rest\JsonApi\RestResource; use Spryker\Glue\GlueApplication\Rest\JsonApi\RestResponse; use Spryker\Glue\GlueApplication\Rest\JsonApi\RestResponseInterface; class CustomCustomerApiResponseBuilder { /** * @param RestCustomCustomerApiAttributesTransfer $restCustomerAttributes * @return RestResponseInterface */ public function createSuccessResponse(RestCustomCustomerApiAttributesTransfer $restCustomerAttributes): RestResponseInterface { $restResponse = new RestResponse(); $restResource = new RestResource('customers', (string)$restCustomerAttributes->getIdCustomer(), $restCustomerAttributes); return $restResponse->addResource($restResource); } /** * @param string $errorMessage * @return RestResponseInterface */ public function createErrorResponse(string $errorMessage): RestResponseInterface { $restResponse = new RestResponse(); // Creating error message transfer $errorTransfer = (new RestErrorMessageTransfer()) ->setCode(400) // HTTP error code (Bad Request) ->setStatus(400) // HTTP status ->setDetail($errorMessage); // The actual error message return $restResponse->addError($errorTransfer); } } ?> |
Step 6: Defining the Transfer Object (custom_customer_api.transfer.xml)
Finally, the transfer object is defined in XML. The RestCustomCustomerApiAttributesTransfer contains the attributes we want to expose for each customer, such as idCustomer, firstName, lastName, and so on.
src/Pyz/Shared/CustomCustomerApi/Transfer/custom_customer_api.transfer.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<transfers> <transfer name="RestCustomCustomerApiAttributesTransfer"> <property name="idCustomer" type="int"/> <property name="firstName" type="string"/> <property name="lastName" type="string"/> <property name="email" type="string"/> <property name="gender" type="string"/> <property name="dateOfBirth" type="string"/> <property name="salutation" type="string"/> <property name="createdAt" type="string"/> <property name="updatedAt" type="string"/> <property name="message" type="string"/> </transfer> </transfers> |
Step 7: Registering the Plugin (GlueApplicationDependencyProvider.php)
Lastly, we register our resource plugin within the GlueApplicationDependencyProvider to ensure it’s included in the application’s resource routes.
To register the plugin, modify GlueApplicationDependencyProvider.php:
src/Pyz/Glue/GlueApplication/GlueApplicationDependencyProvider.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php namespace Pyz\Glue\GlueApplication; use Pyz\Glue\CustomCustomerApi\Plugin\GlueApplication\CustomCustomerApiResourcePlugin; use Spryker\Glue\GlueApplication\GlueApplicationDependencyProvider as SprykerGlueApplicationDependencyProvider; class GlueApplicationDependencyProvider extends SprykerGlueApplicationDependencyProvider { protected function getResourceRoutePlugins(): array { return [ new CustomCustomerApiResourcePlugin(), ]; } } ?> |
Step 8: Generate Transfer Objects and Clear Cache
Run the following command to clear caches:
docker/sdk cli
console transfer:generate
console cache:empty-all
Step 9: call access tokens
Api Endpoint:
http://glue.eu.spryker.local/access-tokens
Method:
POST
Request body
1 2 3 4 5 6 7 8 9 |
{ "data": { "type": "access-tokens", "attributes": { "username": "mukesh.singh@bluethinkinc.com", "password": "Mukesh@#123" } } } |
Responce:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "data": { "type": "access-tokens", "id": null, "attributes": { "tokenType": "Bearer", "expiresIn": 28799, "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJmcm9udGVuZCIsImp0aSI6IjZhODExMGRhNGY3YjFjZTY4YTU0YmYzZjA5MmE1Y2QxZWFmMzFkYWQyZTY0OWViOTExMjJlMDkwMTIxMThkNjgwN2UyYjAyNTQ3OThmMTNhIiwiaWF0IjoxNzM5Nzg4NDg5LjAwODczMDksIm5iZiI6MTczOTc4ODQ4OS4wMDg3MzMsImV4cCI6MTczOTgxNzI4OC45NzQ3MTY5LCJzdWIiOiJ7XCJpZF9jb21wYW55X3VzZXJcIjpudWxsLFwiaWRfYWdlbnRcIjpudWxsLFwiY3VzdG9tZXJfcmVmZXJlbmNlXCI6XCJjdXN0b21lci0tMzVcIixcImlkX2N1c3RvbWVyXCI6MjcsXCJwZXJtaXNzaW9uc1wiOm51bGx9Iiwic2NvcGVzIjpbImN1c3RvbWVyIl19.dtP0mYPStfXFzNHZqcC6i1DWsSXhR45PxK0sTNoAYcVhYfaIb7XDhPNgmqXJJkXlcY_qKzUN1HZuhg-dfnwayz0Z-eBO3xtGiOOGeGGSwry8BTib1bkvxR1eWbrzjpQPXq7bOn7G1iOwzX33Y38VVEJdofd0caV9i5amuS_Z4tV2Xhq-PANfvb_WDCBOzDdfErxI-m9Xu-XjJ--NWlJv3CHbJrejC4rIUwSc9CzWBA-x1I-aCNvEamPkPqJULkNuo6gdZkANBQtztWSCM2Dw5GjIT7xOOD70DXQPQu2Dko6lSo4Fdf16i_F9uRYVuMS9SKmniTOX8XfkLvYvxa5-rQ", "refreshToken": "def50200e409b4baa133c78b0dea60f4eeaf8452fd3acd300c47985f174c5c2d2ea5baf2cacbf42e2b3a63c2127b6f322896c642703c7c13754e1143724cd35a927a1a28dbea4e4ce6a5ec170069dc85ad370f8ac01df6a3ea130aa6701d5c4d7735b246947a32c0df660d3d998e2738d057766661cabb3bebfe70c0b748153643cf27f5f921d1c72a353f99f3b424dbc330f59dff40d63b851fd0ca33a3904ad072d4f312849c9756e2ca9d922b5b1f27b67bd7fc3a8626acb53406e671a1bf3f936b5129ed319e690a3c5431d42e55db1fe7381e8b40e0d1e0a3db36a10227c7a49b7c7c72af6f419a8d15972d854822f1395376526973d113419fa985f999eb9fcc091f773fd82f3dc455fae91bdece39a4f5e7e7933e3a6d6bec9fb023863c331cfb9e3429d2831a549e661500e40cfaf823e4210473b99ba5cd90bfe8e32d329349df4030cd36ee83172b11dcc451a37947846ea6d685cbdbcd0a3ad0ec4133c5c1356d746a229a87232c8afa7b609401956958f979bbb7259642a72b6ba9b3f258f61849138fc2912c9c6faccf1b9c8be841620ab031ef85387aee0d8755a6a1cadc78cf0282cfd83b2623a664da3c26bc643a2d01cf71f7c0eebeffad9139d5faf8551965fc76de5ddda3f48a9b95159a8de362b990eb54afb9f3db8ea5f0ecac004e8fb56c09b9de00bcac", "idCompanyUser": null }, "links": { "self": "http://glue.eu.spryker.local/access-tokens" } } } |
Get Customer data Customer id based through api
Api Endpoint:
http://glue.eu.spryker.local/customcustomerapi/?customerId=27
Authorization: Add headers for any necessary API key or token-based authentication.
Customer ID: The customer ID is passed as part of the URL to fetch data specific to that customer
Method:
GET
Response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
{ "data": [ { "type": "customers", "id": "27", "attributes": { "idCustomer": 27, "firstName": "mukesh", "lastName": "singh", "email": "mukesh.singh@bluethinkinc.com", "gender": "Male", "dateOfBirth": "2025-01-23", "salutation": "Mr", "createdAt": "2025-02-17 10:34:10.000000", "updatedAt": "2025-02-17 10:34:40.000000", "message": "Customer data retrieved successfully!" }, "links": { "self": "http://glue.eu.spryker.local/customers/27" } } ], "links": { "self": "http://glue.eu.spryker.local/customcustomerapi?customerId=27" } } |
Conclusion
With this step-by-step guide, you have now built a custom Spryker API to retrieve customer data based on a customer ID.You can customize it further by adding more fields, improving error handling, and securing the API as needed.
bluethinkinc_blog
2025-03-28