Using Types and Custom Fields
Sometimes the default fields present on a resource do not meet your business' needs. This tutorial shows how to extend resources with Types and Custom Fields.
In this tutorial, you will extend the Customer resource representation. You can find a list of all customizable resources in the API Reference documentation.
Let's say you operate an online business selling shoes. You want to provide personalization for your logged in customers by storing their preferred shoe size. By storing their shoe size as a part of their customer data, you can auto-select their shoe size when navigating to a product detail page, or provide search results with only shoes in their size.
To do this, we need to extend the standard Customer object with a Type, which defines a CustomField.
Learn more about using Types in our self-paced Extensibility overview module.
Customization in commercetools Composable Commerce
Adding a custom field is a two step process. First, we need to create a Type by submitting a TypeDraft with, among other things, a resourceTypeId
and a FieldDefinition. The FieldDefinition describes the new Custom Field, and the resourceTypeId
indicates which resources are customizable using the Type.
Creating a Type
Before we can add the preferredShoeSize
field to the Customer object, we need to create a new Type which contains the preferredShoeSize
CustomField.
To do this, we send a POST
request to the /<project-id>/types
endpoint with the following payload:
{"key": "customer-preferredShoeSize","name": { "en": "Additional field to store preferred shoe size" },"resourceTypeIds": ["customer"],"fieldDefinitions": [{"type":{"name":"LocalizedString"},"name":"preferredShoeSize","label":{"en":"Preferred Shoe Size"},"required":false,"inputHint":"SingleLine"}]}
The resourceTypeIds
in this request indicate which resources we can use the Custom Field with. We also specify that we want to use a LocalizedString
type.
After successful creation the response contains the new Type with the preferredShoeSize
field:
{"id": "b9c5e724-8d86-485b-ae1e-a1d3bcc8deae","version": 1,"key": "customer-preferredShoeSize","name": {"en": "Additional field to store preferred shoe size"},"resourceTypeIds": ["customer"],"fieldDefinitions": [{"name": "preferredShoeSize","label": {"en": "Preferred Shoe Size"},"required": false,"type": {"name": "LocalizedString"},"inputHint": "SingleLine"}],"createdAt": "2015-10-02T09:44:24.628Z","lastModifiedAt": "2015-10-02T09:44:24.628Z"}
Creating a Customer with the preferredShoeSize
CustomField
All other fields of the Customer object are inherited from the standard resource, but we can now create a new Customer with the additional information about shoe size.
To do so, submit a POST
request to the /<project-id>/customers
API endpoint with following payload:
{"customerNumber":"Registered-0042","email":"john.doe@example.com","firstName": "John","lastName": "Doe","password": "secret123","custom": {"type": {"key": "customer-preferredShoeSize","typeId": "type"},"fields": {"preferredShoeSize": {"en":"38"}}}}
You should receive a response to that request with the code 201 containing the customized customer resource:
{"customer": {"id": "85c8d7a1-da11-477f-8ad7-a19c6c274f71","version": 1,"createdAt": "2018-12-14T14:06:05.555Z","lastModifiedAt": "2018-12-14T14:06:05.555Z","customerNumber": "Registered-0042","email": "john.doe@example.com","firstName": "John","lastName": "Doe","password": "aVlInP9GSgTfLob/D619djMauHcxyXvhhYKZUtTjm3s=$3S3KeoGMjHXiHpVr90QoSydU3O1u2qcXcrvsu9dWHao=","addresses": [],"shippingAddressIds": [],"billingAddressIds": [],"isEmailVerified": false,"custom": {"type": {"typeId": "type","id": "b9c5e724-8d86-485b-ae1e-a1d3bcc8deae"},"fields": {"preferredShoeSize": {"en": "38"}}}}}
Update an existing Customer with a CustomField
What happens if we want to use the CustomField on an existing customer? Can we update existing Customer resources with the custom field?
Yes, we can. The API provides update actions for that.
When we created the "John Doe" customer, we used the customer-preferredShoeSize
customer type instead of the default. To use the custom field on an existing customer, we need to have them use the custom-preferredShoeSize
type so we can use the preferredShoeSize
field.
The SetCustomType update action provides this functionality. Let's make the existing customer "Jane Roe" a customer-preferredShoeSize
customer type by sending the payload below with the update request to the Customer API endpoint:
POST /{project-id}/customers/{id-of-customer-jane-roe}
with following payload:
{"version":1,"actions": [{"action": "setCustomType","type": {"id": "b9c5e724-8d86-485b-ae1e-a1d3bcc8deae","typeId": "type"}}]}
We can find the ID of the customer-preferredShoeSize
number by Querying the CustomTypes API endpoint if needed.
The response to the update action shows that the customer now contains the Type:
{"id": "85c8d7a1-da11-477f-8ad7-a19c6c274f71","version": 2,"createdAt": "2018-12-14T14:06:05.555Z","lastModifiedAt": "2018-12-14T14:25:57.885Z","customerNumber": "Registered-0024","email": "jane.roe@example.com","firstName": "Jane","lastName": "Roe","password": "aVlInP9GSgTfLob/D619djMauHcxyXvhhYKZUtTjm3s=$3S3KeoGMjHXiHpVr90QoSydU3O1u2qcXcrvsu9dWHao=","addresses": [],"shippingAddressIds": [],"billingAddressIds": [],"isEmailVerified": false,"custom": {"type": {"typeId": "type","id": "b9c5e724-8d86-485b-ae1e-a1d3bcc8deae"},"fields": {}}}
As you can see the Type has been set, but there are no values for the fields:
fields:{}
Due to this we'll have to give the preferred shoe size information for "Jane Roe" in another update action called Set CustomField.
POST /{project-id}/customers/{id-of-customer-jane-roe}
with following payload:
{"version":2,"actions":[{"action":"setCustomField","name": "preferredShoeSize","value":{"en":"38"}}]}
In the response we'll now find the preferredShoeSize
field with the appropriate value:
fields:{"preferredShoeSize": {"en":"38"}}
That's it. We've updated an existing Customer with a CustomField.
Query Customer with CustomField
Let's use the custom field preferredShoeSize
for queries on customers.
Imagine we have extra stock for small shoe sizes in the store. We can query all the customers who have a preferredShoeSize
of 38
and target them with a discount code.
We'll filter by this value for the English language part of the Localized String in preferredShoeSize
by the predicate: preferredShoeSize(en="38")
.
Since preferredShoeSize
is a CustomField we'll need to mark it like that, like so: custom(fields(preferredShoeSize(en="38")))
The complete URL-encoded query request on the Customers endpoint is as follows:
GET {projectKey}/customers?where=custom(fields(preferredShoeSize(en%3D%2238%22)))
CustomerPagedQueryResponse customersWithShoeSize = apiRoot.customers().get().withWhere("custom(fields(preferredShoeSize(en=\"38\")))").executeBlocking().getBody();
The response to the query should only contain customers with a preferred shoe size of 38 in your project.
The use case tackled in this tutorial is just one example for using CustomFields on the Customer resource.
Plenty of other use cases exist, for example:
- Storing loyalty points for each purchase by a customer.
- Adding contextual in-cart promotions by adding fields on cart LineItems.
And more!
Types and Custom Fields give you the flexibility to customize resources to the specific requirement of your business.