Performance tips
Optimize performance with Composable Commerce.
To optimize performance with Composable Commerce, it's important to understand how to effectively make use of its scalability and response time capabilities. This guide provides practical advice for solution implementers, focusing on code optimization strategies to keep response times low.
The performance tips outlined here vary in impact based on different use cases, so they are not ranked by priority. Keep in mind that the tips below are optimizations, and while it's beneficial to follow them, they should not compromise the desired user experience.
API request planning
You should plan your API requests according to the following guidelines (ordered by importance):
- Avoid sequential API requests wherever you can.
- Consider your use of Reference Expansion. We recommended using Reference Expansion instead of parallel or sequential requests (nowadays, network roundtrip time is more crucial than the number of bytes transferred). However, you should avoid using Reference Expansion if you do not necessarily need it, as it takes time to perform the expansion internally. It's better to not include
expand
parameters in your request defaults but to add them on a case-by-case basis. - Do parallel requests. All the Composable Commerce SDKs support these, as do most good generic HTTP clients. You will need to plan ahead concerning which identifiers to query because you will need them in the client request or session data to perform parallel requests.
In any case, if you have use cases where the querying and reference expansion options force you into sequential requests, please post a suggestion to the Composable Commerce support team so that we can polish the API to avoid such cases.
JSON document size
While the maximum size of JSON documents persisted through any API endpoint is 16 megabytes
, we recommend limiting your average documents to no more than 100 KB.
Your largest documents (for example, a Product with many Product Variants, or an Order with many Line Items) should ideally not exceed 2 MB.
Querying and showing data
- For a user-facing application, always go through the Product Projections endpoint. Only use the Products endpoint to administrate, manage, and import/export products. The API responses will be only half as large because the projections only contain the
current
or thestaged
version of the data (depending on how you set thestaged
parameter), whereas the Products endpoint always returns both the fullcurrent
and thestaged
versions. - Use the Product Projection Search endpoint wherever you can (even for fetching single products by their
id
). Internally, the Product Projection Search endpoint is based on a different technology stack that delivers faster response times due to its read-only nature. - When querying objects:
- Try to set a sensible
limit
to the number of objects returned where you can. This depends on the cost of the query and on the size of the objects retrieved. As a rule of thumb, start with a small value and increase it in small steps until performance starts to drop off. - Deactivate the field
total
when you are not interested in the total number of results matching the query. Refer to PagedQueryResult for more details. - Avoid expensive query predicate patterns and understand the indexing behavior. This and specific tips are documented in the Query Predicate Documentation
- Try to set a sensible
- When doing bulk downloads, we recommend using the "process" implementation of our JavaScript SDK "base" service, which implements best practice defaults and patterns. Feel free to implement a similar pattern in your preferred stack.
Response size
Fetching a large amount of data from Product Projections and Product Projection Search results in longer response times. We recommend targeting the following response sizes to ensure the best performance:
- 5 MB for Products
- 1 MB for search results
We are aware that reaching these targets can sometimes be difficult. Here are some tips that may help to optimize your queries:
- To reduce the number of products/results returned, and therefore, the response size, try using lower
limit
values in the query. - For multi-language projects, use the
localeProjection
parameter to limit the number of locales present in the Product endpoints and search responses. - Consider setting up Stores and use them in Carts and Orders to reduce their size. The
storeProjection
parameter can also be included when querying Product Projections or Product Projection Search to reduce the number of locales and prices in the response.
Product Projection Search
Indexing
When you create, update, or delete a product, it must be indexed before the change is available in the Product Projection Search endpoint, which happens after a delay.
This delay is not the same for all updates. Products with many variants, locales, prices, or searchable attributes need more time to get indexed than others, and thus, have a longer delay.
Additionally, when changing the configuration of your Project that affects products (for example, adding locales, currencies, countries, customer groups, or adding attributes to a product type), it will recreate the index for all products. Any following changes are put on hold until the process is completed and then applied later.
Project configuration
The following thresholds for project configuration parameters are recommendations for you to keep under control; they are not limits imposed by Composable Commerce. Keeping the project configuration parameters below these thresholds will result in good indexing performance.
If your Project exceeds any of these values, we cannot guarantee that the search index will be updated with the latest changes to your Products and Prices.
In some cases, the Product Projection Search endpoint will not return any results.
If you are planning to exceed at least one of these thresholds, contact our Composable Commerce support team so that we can review your use case and give you recommendations.
- 1 000 000 products per project
- 3 000 000 variants per project
- 15 locales per project
- 100 channels per project
- 20 suggestion keywords per product
- 25 searchable attributes across all product types (depending on the types of attributes and attribute values, for example, localizable texts have a bigger impact on indexing than boolean values)
In addition, your application design should carefully consider the frequency and timing of configuration changes that affect products. We recommend that you apply all the configuration changes together before updating your products. In this way, you can optimize the effort required to rebuild the project's index.
Filtering, faceting, and sorting
- Consider reducing the number of faceting attributes on the search request if you want to decrease the response time.
- When possible, use
filter.query
filters to reduce the number of items to be faceted and/or sorted, as larger collections cause faceting and sorting to take proportionally longer. - Use the filter by category subtree functionality whenever you want to retrieve products assigned to a specified category plus its subcategories.
Matching Product Variants
For each search request, you can enable the marked-matching-variants functionality that leads to marking those variants within the returned products which are matching the search criteria.
As matching variants can be complex to calculate, this feature is deactivated by default.
Since this feature may have an impact on the search performance, we let you control for which requests you want to enable it and when you don't need it.
Modifying data
Multiple update actions
- It is possible to add a large number of update actions into one update request as long as they change the same Object (for example Update Product).
- If you want to publish the updated product right away, you can include the respective publish update action into the list of update actions. It must be the last action on the list though.
All update actions within a single update request operate as a cohesive unit. This means that either all update actions succeed collectively, or if any one action fails, the entire set of update actions is rejected. Given this behavior, you should always consider whether to combine specific update actions in a single request or spread them across multiple requests.
Caching parts of the model
Composable Commerce has been built for stateless frontend implementations, it is meant to pass every user request without caching in the frontend implementation. We recommend doing this until you encounter serious and specific performance problems. You may waste a large amount of time debugging cache staleness and the invalidation efforts will likely not pay off.
Nevertheless, many UI scenarios aren't doable without caching some data. This refers for example to the page header, especially category-driven menu trees and metadata like product types and channels.
- Although you could query the whole category tree every time, it makes sense to cache the menu as a final menu object across users. We suggest auto-refreshing it frequently (for example, every five seconds).
- Concerning User and Cart data, it depends on how much information is directly visible in the UI's initial state. In most cases, the information can be stored in an encrypted User Cookie (or a classic stateful server-side session) and the full User and/or Cart Object is pulled from the API on demand.
A notable exception to the general recommendation to avoid caching is that in some development frameworks, especially in the content management system (CMS) space, caching is built into the basic assumptions on a low level. In these cases, it's not worth fighting against the framework's assumptions.
Import / Export / Sync integrations
Most real-world projects have a number of backend system integrations. In addition to the general advice above, one efficient way to implement sync jobs is by setting up a Subscription to notify about changes to a resource.
This approach allows you to be notified in near real-time about new events through your preferred message queue without the need for periodical polling. This reduces the load and lowers the number of requests to our APIs. Subscription destinations are also cloud-native, highly available, and auto-scalable. This provides generally better performance than other polling solutions.
To ensure data integrity, we recommend executing a full sync periodically.
Whether you can also do this pattern for information that is synced with Composable Commerce depends on the capabilities of the other involved systems. As previously mentioned, the JavaScript SDK provides mature helpers for bulk operations.
The Composable Commerce Import API allows you to bulk import data for creating and updating resources. You can learn how to use the Import API effectively, including limits and how to optimize performance, on the Import API best practices page.
GraphQL
Fetch only the fields that you need. This will result in smaller payloads and is better for the network and the serialization/deserialization process.
Use <fieldName>Ref
fields where the expanded resource is not needed.
Optimize your queries towards low Query complexity.