Sending requests with Data Exchange API
Edit on GitHubThis document describes how to interact with databases using the Data Exchange API. The Data Exchange API lets you configure endpoints to interact with any database tables. In this document, /dynamic-data/countries
is used to interact with the spy_country
and spy_tax_rate
tables as an example.
Prerequisites
The Data Exchange API is a non-resource-based API, and routes all specified endpoints directly to a controller. By default, all routes within the Data Exchange API are protected to ensure data security. To access the API, you need to obtain an access token by sending the POST /token/
request with the appropriate credentials:
POST /token/ HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Content-Length: 67
grant_type=password&username={username}&password={password}
Retrieve a collection of fields
To retrieve a collection of countries, you need to send the GET https://glue.mysprykershop.com/dynamic-entity/countries
request. The request needs to include the necessary headers, such as Content-Type, Accept, and Authorization, with the access token provided.
Pagination allows for efficient data retrieval by specifying the desired range of results. To use pagination, include the desired page limit and offset in the request:
GET /dynamic-entity/countries?page[offset]=1&page[limit]=2 HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Response sample:
{
"data": [
{
"id_country": 2,
"iso2_code": "AD",
"iso3_code": "AND",
"name": "Andorra",
"postal_code_mandatory": true,
"postal_code_regex": "AD\\d{3}"
},
{
"id_country": 3,
"iso2_code": "AE",
"iso3_code": "ARE",
"name": "United Arab Emirates",
"postal_code_mandatory": false,
"postal_code_regex": null
}
]
}
The response contains all the columns from the spy_country
table that are configured in spy_dynamic_entity_definition.definition
. Each column is represented using the fieldVisibleName
as the key, providing a comprehensive view of the table’s data in the API response. By default, the API GET
request returns up to 20 records.
Filtering enables targeted data retrieval, refining the response to match the specified criteria. To apply filtering to the results based on specific fields, include the appropriate filter parameter in the request:
GET /dynamic-entity/countries?filter[country.iso2_code]=AA HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Response sample:
{
"data": [
{
"id_country": 1,
"iso2_code": "AA",
"iso3_code": "UUD",
"name": "Create",
"postal_code_mandatory": false,
"postal_code_regex": null
}
]
}
It is also possible to supply multiple values for a field. They are filtered as IN condition:
GET /dynamic-entity/countries?filter[country.iso2_code]={"in": ["AC","AD", "AE"]} HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Response sample:
{
"data": [
{
"id_country": 1,
"iso2_code": "AC",
"iso3_code": "ASC",
"name": "Ascension Island",
"postal_code_mandatory": false,
"postal_code_regex": null
},
{
"id_country": 2,
"iso2_code": "AD",
"iso3_code": "AND",
"name": "Andorra",
"postal_code_mandatory": true,
"postal_code_regex": "AD\\d{3}"
},
{
"id_country": 3,
"iso2_code": "AE",
"iso3_code": "ARE",
"name": "United Arab Emirates",
"postal_code_mandatory": false,
"postal_code_regex": null
}
]
}
When you combine multiple filters in a single request, the system applies an AND
condition to the retrieved results.
To retrieve a country by a specific ID, send a GET
request with the following parameters:
GET /dynamic-entity/countries/3 HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Response sample:
{
"data":[
{
"id_country": 3,
"iso2_code": "AE",
"iso3_code": "ARE",
"name": "United Arab Emirates",
"postal_code_mandatory": false,
"postal_code_regex": null
}
]
}
If you send a request to an endpoint that’s not configured in spy_dynamic_entity_configuration
, the following is returned:
[
{
"message": "Not found",
"status": 404,
"code": "007"
}
]
Retrieve a collection of entities together with relations
To retrieve a collection of countries with relations, you need to send the GET https://glue.mysprykershop.com/dynamic-entity/countries?include=countryTaxRates
request.
The include
parameter allows you to define a list of relations to be retrieved alongside the main entity of the request.
It follows this format {mainEntity}?include={childRelationOfTheMainEntity.childRelationOfTheChildRelation},{secondRelationChain},{thirdRelationChain}
, where:
- The
include
parameter can consist of one or several relation chains, separated by commas (,). - Each relation chain represents a series of relation names (see
spy_dynamic_entity_configuration_relation.name
) separated periods (.). - Each relation in the relation chain represents a connection between two
spy_dynamic_entity_configuration
entities. See thespy_dynamic_entity_configuration_relation
table structure. - To be processed correctly, a relation chain must contain only existing relation names arranged in the correct sequence that aligns with the relation hierarchy. Otherwise, an error will be returned. See error codes.
GET /dynamic-entity/countries?include=countryTaxRates HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Response sample:
{
"data": [
"...",
{
"id_country": 14,
"iso2_code": "AT",
"iso3_code": "AUT",
"name": "Austria",
"postal_code_mandatory": true,
"postal_code_regex": "\\d{4}",
"countryTaxRates": [
{
"id_tax_rate": 1,
"fk_country": 14,
"name": "Austria Standard",
"rate": "20.00"
},
{
"id_tax_rate": 21,
"fk_country": 14,
"name": "Austria Reduced1",
"rate": "13.00"
},
{
"id_tax_rate": 22,
"fk_country": 14,
"name": "Austria Reduced2",
"rate": "10.00"
}
]
},
"..."
]
}
The response contains all columns from the spy_country
table and the included spy_tax_rate
table, as configured in spy_dynamic_entity_definition.definition
. Each column is identified using the fieldVisibleName
as the key, providing a comprehensive view of the table’s data in the API response.
Currently (as of version 202404.0 and earlier), filters don’t work with relations
and are only used to filter the root entity results.
Sending POST
requests
To create a collection of countries, submit the following HTTP request:
POST /dynamic-entity/countries HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Content-Length: 154
{
"data": [
{
"iso2_code": "WA",
"iso3_code": "WWA",
"name": "FOO"
}
]
}
The response payload includes all the specified fields from the request body, along with the ID of the created entity. Response sample:
{
"data":[
{
"iso2_code": "WA",
"iso3_code": "WWA",
"name": "FOO",
"id_country": 257
}
]
}
All fields included in the request payload are marked as isCreatable: true
in the configuration. So, the API creates a new record with all the provided fields.
However, if any of the provided fields are configured as isCreatable: false
, the request results in error.
For example, configure isCreatable: false
for iso3_code
and send the same request. You should receive the following error response:
[
{
"message": "Modification of immutable field `countries[0].iso3_code` is prohibited.",
"status": 400,
"code": "1304"
}
]
Certain database-specific configurations may result in issues independent of entity configurations. For example, with MariaDB, it is impossible to set the ID value for an auto-incremented field. Additionally, the iso2_code
field in the spy_country
table must have a unique value. Therefore, before creating a new record, you need to make sure you are not passing a duplicate value for this field. If a duplicate value is passed, the following is returned:
[
{
"message": "Failed to persist the data for `countries[0].iso2_code`. Please verify the provided data and try again. Entry is duplicated.",
"status": 400,
"code": "1309"
}
]
This response is caused by a caught Propel exception, specifically an integrity constraint violation: (Integrity constraint violation: 1062 Duplicate entry 'WA' for key 'spy_country-iso2_code')
.
Sending PATCH
requests
To update a collection of countries, submit the following HTTP request:
PATCH /dynamic-entity/countries HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Content-Length: 174
{
"data": [
{
"id_country": 1,
"iso2_code": "WB",
"iso3_code": "WWB",
"name": "FOO"
}
]
}
The response payload includes all the specified fields from the request body. Response sample:
{
"data":[
{
"iso2_code": "WB",
"iso3_code": "WWB",
"name": "FOO",
"id_country": 1
}
]
}
To update a specific country, send the following HTTP request:
PATCH /dynamic-entity/countries/1 HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Content-Length: 143
{
"data": {
"iso2_code": "WB",
"iso3_code": "WWB",
"name": "FOO"
}
}
All fields included in the request payload are marked as isCreatable: true
in the configuration. So, the API creates a new record with all the provided fields.
However, if any of the provided fields are configured as isCreatable: false
, the request results in error.
For example, configure isCreatable: false
for iso3_code
and send the same request. You should receive the following error response:
[
{
"message": "Modification of immutable field `countries[0].iso3_code` is prohibited.",
"status": 400,
"code": "1304"
}
]
If id_country
is not provided, the following is returned:
[
{
"message": "Incomplete Request - missing identifier for `countries[0]`.",
"status": 400,
"code": "1310"
}
]
If id_country
is not found, the following is returned:
[
{
"message": "The entity `countries[0]` could not be found in the database.",
"status": 404,
"code": "1303"
}
]
Similarly to the POST
request, it is important to consider database-specific configurations when sending a PATCH
request.
Sending PUT
request
When you send a PUT request, you provide the updated representation of the resource in the request payload. The server replaces the entire existing resource with the new representation provided. If the resource does not exist at the specified URL, the request creates a new resource.
To replace an existing entity, send the following PUT
request:
PUT /dynamic-entity/countries HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Content-Length: 263
{
"data": [
{
"id_country": 1,
"iso2_code": "WB",
"iso3_code": "WWB",
"name": "FOO"
}
]
}
The response payload includes all touched fields for the provided resource. Response sample:
{
"data": [
{
"iso2_code": "WB",
"iso3_code": "WWB",
"name": "FOO",
"postal_code_mandatory": null,
"postal_code_regex": null,
"id_country": 1
}
]
}
It is also possible to send a PUT
request for a specific ID instead of a collection using the following request:
PUT /dynamic-entity/countries/1 HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Content-Length: 143
{
"data": {
"iso2_code": "WB",
"iso3_code": "WWB",
"name": "FOO"
}
}
When using PUT
requests, it’s important to consider that the same rules and configurations apply
as with PATCH
and POST
requests. This means that the isEditable
attribute determines whether existing
items can be modified during a PUT
request. Additionally, the isCreatable
attribute is used for non-existing
items that are created automatically according to the PUT
request RFC.
In technical terms, the PUT
request follows the same guidelines as PATCH
and POST
requests in relation to
the definition and database-specific configurations. The isEditable
attribute governs the update capability
of existing items during a PUT
request, ensuring that only editable fields can be modified. Similarly, the isCreatable
attribute pertains to non-existing items and governs their automatic creation as part of the PUT
request process,
adhering to the standards outlined in the PUT
request RFC. It’s crucial to adhere to these rules and configurations
to ensure accurate and consistent data manipulation during PUT
operations.
Sending DELETE
requests
By default, entities can’t be deleted. To be able to delete an entity, in the configuration, set the isDeletable
attribute for the entity to true
. The configuration to delete entities isn’t cascaded to child entities. Before you can delete an entity, you need to delete all of its child entities.
To delete a collection of countries, submit the following HTTP request:
DELETE /dynamic-entity/countries?filter[countries.iso2_code]={"in": ["UA","US","AL"]} HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
The response should be 204 No Content
. If deletion is not allowed, 405 Method not allowed
is returned.
To delete a specific country, submit the following HTTP request:
PUT /dynamic-entity/countries/1 HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
The response should be 204 No Content
. If deletion is not allowed, 405 Method not allowed
is returned.
Sending POST
, PATCH
and PUT
requests with relationships
To create or update an entity along with its related entities, you need to include the relationships directly in the request payload. The payload should be structured to reflect the hierarchy and connections between the main entity and its child entities.
Currently, our system doesn’t support many-to-many
relationships for POST, PATCH, and PUT requests.
Only one-to-one
and one-to-many
relationships are allowed. This means that each child entity can be associated
with only one parent entity.
The payload for these requests follows a nested structure where the main entity and its related entities are included within a data array. Each object in the data array represents an instance of the main entity, and each related entity is nested within it.
For correct processing, make sure that related entities are defined with existing relation names. Also, organize them in alignment with their hierarchical relationships in the database, corresponding to the relationships defined in tables like spy_dynamic_entity_configuration_relation
and spy_dynamic_entity_configuration_relation_field_mapping
:
spy_dynamic_entity_configuration_relation
specifies the relationships between parent and child entities. Each record links a parent entity to a child entity.spy_dynamic_entity_configuration_relation_field_mapping
contains the field mappings between related entities.
The hierarchical relationships are primarily defined by the foreign key references in these tables.
For example, spy_dynamic_entity_configuration_relation
uses foreign keys to establish connections between
parent and child configurations in spy_dynamic_entity_configuration
.
Incorrect or non-existent relation names or a misalignment in the hierarchy leads to processing errors. For a detailed list of potential errors, see Error codes.
For POST, PATCH, and PUT requests the payload must accurately reflect the entity relationships. Make sure that each entity in the request includes its corresponding related entities, structured as nested objects within the payload.
POST /dynamic-entity/countries HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
Content-Length: 245
{
"data": [
{
"iso2_code": "DE",
"iso3_code": "DEU",
"name": "Germany",
"countryTaxRates": [
{
"name": "Germany Standard",
"rate": "1.00"
}
]
}
]
}
Response sample:
{
"data": [
{
"iso2_code": "DE",
"iso3_code": "DEU",
"name": "Germany",
"postal_code_mandatory": null,
"postal_code_regex": null,
"id_country": 1,
"countryTaxRates": [
{
"name": "Germany Standard",
"rate": "1.00",
"id_country_tax_rate": 1,
"fk_country": 1
}
]
}
]
}
The response contains all columns from the spy_country
table and the included spy_tax_rate
table,
as configured in spy_dynamic_entity_definition.definition
. Each column is identified using the fieldVisibleName
as the key, providing a comprehensive view of the table’s data in the API response.
For POST and PUT requests, which are used to create new entities, child entities receive their foreign key reference to the parent entity only after the parent entity is created. The system automatically assigns the foreign key to the child entities based on the newly created parent entity’s ID.
Non-transactional saving
By default, the Data Exchange API uses a transactional approach to save data. If an error occurs during the saving process, the entire transaction is rolled back, and no data is saved. However, in some cases, you may want to save data non-transactionally. In the non-transactional mode, the API wraps each entity and its related records (if present in the request) in a separate transaction.
To enable the non-transactional behavior, you need to set the X-Is-Transactional
with the value false
in the request.
In the following example, the first entity will be saved successfully, while the second entity won’t be saved due to the missing rate
field.
POST /dynamic-entity/countries HTTP/1.1
Host: glue-backend.mysprykershop.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
X-Is-Transactional: false
Content-Length: 445
{
"data": [
{
"iso2_code": "DE",
"iso3_code": "DEU",
"name": "Germany",
"countryTaxRates": [
{
"name": "Germany Standard",
"rate": "1.00"
}
]
},
{
"iso2_code": "DE",
"iso3_code": "DEU",
"name": "Germany",
"countryTaxRates": [
{
"name": "Entity without a rate"
}
]
}
]
}
Due to the non-transactional mode, the user will receive a response with the saved entity in the data
field and an error message in the error
field.
{
"data": [
{
"iso2_code": "DE",
"iso3_code": "DEU",
"name": "Germany",
"id_country": 260,
"postal_code_mandatory": false,
"postal_code_regex": null,
"countryTaxRates": [
{
"name": "Germany Standard",
"rate": "1.00",
"fk_country": 260,
"id_tax_rate": 43
}
]
}
],
"errors": [
{
"code": "1307",
"status": 400,
"message": "The required field must not be empty. Field: `countries[1].tax-rates[0].rate`"
}
]
}
The header name can be changed in the DynamicEntityBackendApiConfig::HEADER_IS_TRANSACTIONAL
constant.
<?php
namespace Spryker\Glue\DynamicEntityBackendApi;
use Spryker\Glue\Kernel\AbstractBundleConfig;
class DynamicEntityBackendApiConfig extends AbstractBundleConfig
{
/**
* @var string
*/
protected const HEADER_IS_TRANSACTIONAL = 'X-Is-Transactional'; // The header name which is used to enable non-transactional mode.
}
Error codes
Error codes for GET
, POST
, PATCH
and PUT
requests:
Error code | Message | Description |
---|---|---|
1301 | Invalid or missing data format. Please ensure that the data is provided in the correct format. Example request body: {'data':[{...},{...},..]} |
The request body isn’t valid. Make sure the data is provided in the correct format. An example request body: {'data':[{...data entity...},{...data entity...},...]} . |
1302 | Failed to persist the data for entity[index].field . Please verify the provided data and try again. |
The data could not be persisted in the database. |
1303 | The entity entity[index] could not be found in the database. |
The requested entity could not be found in the database for retrieval or update. |
1304 | Modification of immutable field entity[index].field is prohibited. |
The field is prohibited from being modified. Check the configuration for this field. |
1305 | Invalid data type entity[index] for field: field |
The specified field has an incorrect type. Check the configuration for this field. |
1306 | Invalid data value entity[index] for field: field . Field rules: validation rules . |
The error indicates a data row and a field that doesn’t comply with the validation rules in the configuration. Here is an example of the error: Invalid data value for field: id, row number: 2. Field rules: min: 0, max: 127 . |
1307 | The required field must not be empty. Field: entity[index].field |
The specified field is required according to the configuration. Add the data for this field and try again. |
1308 | Entity some field identifier not found by identifier, and new identifier can not be persisted. Please update the request. |
The entity couldn’t be found using the provided identifier, and a new identifier can’t be persisted. Update your request or check configuration for the identifier field. |
1309 | Failed to persist the data entity[index].field . Please verify the provided data and try again. Entry is duplicated. |
This error may occur if a record with the same information already exists in the database. |
1310 | Incomplete Request - missing identifier for entity[index] . |
The request is incomplete. The identifier is missing. Check the request and try again. |
1311 | The provided entity[index].field is incorrect or invalid. |
The request contains a field that isn’t present in the configuration. Check the field names. |
1312 | Dynamic entity configuration for table alias alias not found. |
Make sure that you send a valid alias of the entity in the request. |
1313 | Relation relation not found. Please check the requested relation name and try again. |
Make sure that the relation you’re sending in the relation chain is valid and present in the spy_dynamic_entity_configuration_relation table. |
1314 | The relationship relation is not editable by configuration. |
Make sure that the relation you’re sending in the relation chain is configurable. |
1315 | Filter field field for table alias alias not found. |
Make sure that the field you’re sending for the filter exist in configuration. |
1316 | The URL is invalid. entity[index] field field must have a URL data format. |
Make sure that the URL is passed in relative format and starts with a / . |
1317 | Failed to delete the data for entity[index] . The entity has a child entity and can not be deleted. Child entity: entity[index] . |
Make sure that the entity you want to delete doesn’t have child entities. Delete child entities before deleting this entity. |
1318 | Method not allowed for the entity alias . |
Make sure that the entity that you want to delete is set as isDeletable: true in the configuration. |
Thank you!
For submitting the form