GraphQL API

Access commercetools Composable Commerce via a GraphQL API.

The GraphQL API provides queries and mutations to the resources stored on commercetools Composable Commerce with the same API Clients that are used for the REST API. This page documents how a valid API request needs to be formulated, how the Scopes are applied, how the query complexity can be determined and how Reference Expansion can be achieved. Furthermore, this page explains how custom fields and attributes can be queried and modified, but it does not document which standard types and fields the API provides. Those information can be retrieved from the GraphQL schema that can be obtained via introspection or via the Documentation Explorer in the GraphQL console. Documented here explicitly are types and fields that are currently in public beta.

Introspection lets you also discover features that are currently in closed beta intended for customers taking part in the early evaluation phase for upcoming features. Be aware that those features are not covered by our SLAs and can change or be removed without public notice. The GraphQL schema publicly available on Github contains all officially released API features that are covered by our SLAs.

Application of scopes

The access to resources is granted by the same scopes that are used on the REST API endpoints. We do not document them here in detail, but we give some guideline on how to find the scope best suitable for a use case in general:

Use caseGraphQL serviceScope
query for a particular resourceQuery.{resourceType}view_{resourceType}s
query for resources of a certain typeQuery.{resourceType}sview_{resourceType}s
create a resource of a certain typeMutation.create{resourceType}manage_{resourceType}s
update a particular resourceMutation.update{resourceType}manage_{resourceType}s
delete a particular resourceMutation.delete{resourceType}manage_{resourceType}s

For example, the manage_products scope is required for updating a Product with the Mutation.updateProduct service, the view_products scope would not be sufficient for this.

The view_published_products scope can be used by the Query.products service to retrieve data in masterData.current, but not in masterData.staged. The same applies for the Query.productProjectionSearch service that only returns data with this scope when the staged parameter is set to false in such queries.

GraphQL queries and mutations

You can access the GraphQL endpoint with following URL:

https://api.{region}.commercetools.com/{projectKey}/graphql

The endpoint accepts HTTP POST requests with following fields in a JSON body:

  • query - String
    The string representation of the Source Text of the Document as specified in the Language section of the GraphQL specification.
  • variables - Object - Optional
    A JSON object containing key-value pairs in which the keys are the variable names and the values are the variable values.
  • operationName - String - Optional
    The name of the operation, in case you defined several of them in the query.

Example query

The following GraphQL query retrieves the id and the version of a Product with a particular sku provided as variable $sku:

query ($sku: String!) {
product(sku: $sku) {
id
version
}
}

Along with the above query, we have to pass the variables with the API call in which we assign a value to the $sku variable . In this example we look for the Product containing a ProductVariant with sku = "SKU-123" and so we assign this value to our variable in the variables object like shown below:

{
"sku": "SKU-123"
}

The following cURL command sends a HTTP POST request including our example query and variables to the GraphQL endpoint on our Project my-shop.

$ curl -X POST https://api.{region}.commercetools.com/my-shop/graphql \
-H "Content-Type:application/json" \
-H "Authorization:Bearer ..." \
-d '{"query": "query ($sku: String!) {product(sku: $sku) {id version}}", "variables": {"sku": "SKU-123"}}'

Product Attributes, Custom Objects, and Custom Fields

Product Attributes, Custom Objects, and Custom Fields contain Project-specific dynamic data that can be accessed using raw GraphQL fields.

Retrieve Product Attributes

The contents of the value field reflect the data type used by the Product Attribute. For more information see AttributeType. You must determine the data type yourself.

Example query and response for raw Product Attributes:

query {
product(id: "some-uuid") {
masterData {
current {
variants {
...variantFields
}
}
}
}
}
fragment variantFields on ProductVariant {
sku
attributesRaw {
name
value
}
}
{
"data": {
"product": {
"masterData": {
"current": {
"variants": [
{
"sku": "M0E20000000E218",
"attributesRaw": [
{
"name": "productSupported",
"value": true
},
{
"name": "supportContact",
"value": {
"en": "english@example.com",
"de": "german@example.com"
}
}
]
}
]
}
}
}
}
}

Retrieve Custom Fields

The contents of the value field reflect the data type used by the Custom Field. For more information see FieldType. You must determine the data type yourself.

Example query and response for raw Custom Fields:

{
product(id: "some-uuid") {
masterData {
current {
variants {
prices {
...customFields
}
}
}
}
}
}
fragment customFields on ProductPrice {
custom {
customFieldsRaw {
name
value
}
}
}
{
"data": {
"product": {
"masterData": {
"current": {
"variants": [
{
"prices": [
{
"custom": {
"customFieldsRaw": [
{
"name": "kiloPrice",
"value": {
"type": "centPrecision",
"currencyCode": "EUR",
"centAmount": 95,
"fractionDigits": 2
}
}
]
}
}
]
}
]
}
}
}
}
}

Create Product Attributes, Custom Fields, and Custom Objects

To set values for Product Attributes, Custom Objects, and Custom Fields, you must use their corresponding input object types. For each of these, the value field requires a String in the form of escaped JSON.

Examples for the value field on ProductAttributeInput:

"{\"type\": \"centPrecision\", \"currencyCode\": \"USD\", \"centAmount\": 1000, \"fractionDigits\": 2}"
"\"yellow\""

Examples for the value field on CustomFieldInput:

"[\"This is a string\", \"This is another string\"]"
"{\"id\": \"b911b62d-353a-4388-93ee-8d488d9af962\", \"typeId\": \"product\"}"

Example for the value field on CustomObjectDraft:

"{ \"stringField\": \"myVal\", \"numberField\": 123, \"boolField\": false, \"nestedObject\": { \"nestedObjectKey\": \"anotherValue\" }, \"dateField\": \"2018-10-12T14:00:00.000Z\" }"

Existence of query results

For use cases in which you only want to check whether at least one result exists that matches your query, we recommend using the Boolean exists field in your query. This will optimize the query and result in a shorter response time.

query {
# As the quotation marks are within quotation marks, they must be escaped
products(where: "productType(id=\"some-uuid\")") {
exists
}
}

The API returns true in case there is at least one result matching the query condition, but the field value is false in case no matching result could be found.

{
"data": {
"products": {
"exists": true
}
}
}

Reference Expansion

The GraphQL API supports Reference Expansion, just like the HTTP API.

By convention, the GraphQL API offers two fields for each expandable reference:

  • <fieldName>Ref - Fetches the Reference or KeyReference to the resource only (lower query complexity).
  • <fieldName> - Fetches the expanded resource that is referenced (higher query complexity).
    Returns null if the referenced resource is not found.

Expanding a reference in the GraphQL API impacts the performance of the request, and adds to the complexity score. If you don't need the expanded reference for your use case, you should use the <fieldName>Ref to get a better performance on your query.

For example, if you want to obtain the number of child categories for a specific category, as well as identifiers for its ancestors, the following query would work:

{
category(id: "some-uuid") {
children {
id
}
ancestors {
id
}
}
}

The total number of child categories will be the size of children array, and the ancestors identifiers are also available from the ancestors. However, the complexity of the example above can be reduced. If you only need the number of child categories, the childCount field is the way to go. Also, in case you only need the identifiers for the ancestors, it's better to use the ancestorsRef field. This is how the less complex query looks like:

{
category(id: "some-uuid") {
childCount
ancestorsRef {
id
}
}
}

Custom Fields BETA

Reference Expansion for Custom Fields of CustomFieldReferenceType and for the CustomFieldSetType of those is supported in public beta.

If, for example, a Customer has a Custom Field that holds a Reference to another Customer, the following query expands the referenced Customer so that we retrieve the email of the referenced Customer in the same query.

query {
customer(id: "some-uuid") {
email
custom {
customFieldsRaw(includeNames: ["customfield-name"]) {
referencedResource {
... on Customer {
email
}
}
}
}
}
}

Product Attributes BETA

Reference Expansion for Product Attributes are supported in public beta.

You can include referencedResource (for single references) and referencedResourceSet (for sets of references) under attributesRaw for allVariants or masterVariant:

{
product(id: "{productID}") {
masterData {
current {
allVariants {
attributesRaw {
referencedResource {
## If a referenced Product exists, return its name, SKUs, and key
... on Product {
masterData {
current {
slug(locale: "en")
}
}
skus
key
}
## If a referenced ProductType exists, return the Attributes names and ProductType name
... on ProductTypeDefinition {
attributeDefinitions {
results {
name
}
}
name
}
}
referencedResourceSet {
## If a set of referenced Products exists, return their names, SKUs, and keys
... on Product {
masterData {
current {
slug(locale: "en")
}
}
skus
key
}
## If a set of referenced ProductTypes exists, return their Attribute names and ProductType names
... on ProductTypeDefinition {
attributeDefinitions {
results {
name
}
}
name
}
}
}
}
}
}
}
}

Product Search Attributes BETA

Reference Expansion for Product Search Attributes are supported in public beta.

You can include referencedResource (for single references) and referencedResourceSet (for sets of references) under attributesRaw for masterVariant:

{
productProjectionSearch(staged: true) {
results {
masterVariant {
attributesRaw(
includeNames: "{includedAttributes}"
excludeNames: "{excludedAttributes}"
) {
name
value
referencedResource {
id
... on Product {
skus
}
}
referencedResourceSet {
id
... on Product {
skus
}
}
}
}
}
}
}

Query complexity

You can fetch a lot of useful information in a single HTTP request using GraphQL. This is really helpful to avoid parsing unused fields, and remove the unnecessary network overhead by reducing the number of requests. At the same time, it is important to remember that a single query can potentially generate a lot of database operations. Hence, you cannot assume that the response time for a query increases linear with the number of fields in the query.
The GraphQL schema defines a "cost" per field, which you can take advantage of by analyzing your queries, using following options:

  • Inspecting the x-graphql-query-complexity response header and its value.
  • Using GraphiQL's Profiling functionality to measure the impact. GraphiQL profiling

QueryComplexityLimitExceeded

To prevent complex queries from having a negative impact on the overall performance, we block queries equal to or higher than the complexity limit of 20 000. In such cases, queries return a QueryComplexityLimitExceeded error code with the HTTP status code 400.

code
String
"QueryComplexityLimitExceeded"
message
String
//
Any string property matching this regular expression

Error-specific additional fields.

Error response format

The general structure of error responses from the GraphQL API is as follows:

{
"errors": [
{
"message": "Some error message",
"path": ["somePathSegment"],
"extensions": {
"code": "SomeErrorCode"
}
}
]
}

Each element of the errors array has the following fields:

  • message - String - detailed description of the error explaining the root cause of the problem and suggesting you how to correct the error.
  • path - Array - Optional - list of query fields ordered from the root of the query response up to the field in which the error occurred. (Only present when an error can be associated to a particular field in the query result).
  • extensions - Object - dictionary with additional information where applicable.
    • code - String - one of the error codes listed on the Errors page.

In the following example we experience a problem on the query path category -> children:

Example querygraphql
query {
category(id: "650fdb2d-93ea-46f9-97f3-a92816b8305e") {
children {
key
}
}
}
Example responsejson
"errors": [
{
"message": "Too many categories would have been loaded. Please use the 'categories' field instead.",
"path": ["category", "children"],
"extensions": {
"code": "InvalidInput"
}
}
]

Interactive GraphQL console

To explore the GraphQL API, you can use an interactive GraphiQL environment which is available as a part of our ImpEx & API Playground.

Below animation demonstrates how you can use this tool for autocompletion and to explore the documentation that is part of the GraphQL schema:

GraphiQL demonstration

Public beta functionalities BETA

Find below a list of functionality that is currently in beta.

  • createdBy on Versioned
  • lastModifiedBy on Versioned
  • MultiBuyLineItemsTarget and MultiBuyCustomLineItemsTarget on CartDiscount
  • stores on CartDiscount
  • everything related to MyCart, MyOrder, MyPayment, MyProfile, MyShoppingList, MyBusinessUnit, MyQuote, MyQuoteRequest
  • NestedTypes on ProductType
  • referencedResource and referencedResourceSet
  • everything related to ProductTailoring