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 design
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
expandparameters 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.
JSON document size
16 megabytes, we recommend limiting your average documents to no more than 100 KB.Your largest documents, such as a Product with many Product Variants or an Order with many Line Items, should ideally not exceed 2 MB.
Query and show 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
currentor thestagedversion of the data (depending on how you set thestagedparameter), whereas the Products endpoint always returns both the fullcurrentand thestagedversions. - 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
limitto 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
totalwhen 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 paging through large datasets, you may encounter a deep pagination problem where subsequent requests become slower the deeper you try to fetch results. This is because the underlying storage engine has to re-examine all previously fetched records. To avoid this problem, we recommend replacing
offset-based pagination with the approach we describe in iterate over all elements.
Response size
- 5 MB for Products
- 1 MB for search results
Reaching these targets can sometimes be difficult. The following tips help optimize your queries:
- To reduce the number of results returned and the response size, try using lower
limitvalues in the query. - For multi-language projects, use the
localeProjectionparameter 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
storeProjectionparameter can also be included when querying Product Projections or Product Projection Search to reduce the number of locales and prices in the response.
Search query optimization
Prioritize exact matches over wildcard and prefix searches
- Use queries for exact matches:
Whenever possible, use the
exactquery expression for optimal performance. This is especially important for fields with high cardinality (many unique values). - Minimize wildcard usage:
Wildcard searches are resource-intensive.
Only use the
wildcardexpression when absolutely necessary and keep wildcard characters (*, ?) at the end of the search term (for example,product*is better than*product*). - Prefer exact matches to prefix searches:
While prefix searches (
prefixquery expression) are generally faster than wildcards, querying for exact matches is still the most efficient approach. If you can query for a specific value instead of a prefix, do so.
Optimize compound expressions
- AND vs. OR:
andclauses are generally more efficient thanorclauses, as they narrow down the search space more effectively. Structure your queries to leverageandwhere possible. - Order of clauses:
Place the most selective (the one that eliminates the most documents) filters first.
For
andclauses it means, make the most selectiveandclause the root of your complex expression. This reduces the number of documents that need to be evaluated by subsequent filters. - Avoid negations when possible:
Negations (
notclauses) can be computationally expensive. Try to rephrase your queries to avoid them if possible. For example, instead of searching fornot red, search forblue or greenif those are the only other options. - Use filter when possible:
For better query performance, use a
filterexpression when you don't need to sort results by relevance score. Thefilterexpression instructs the platform to skip calculating relevance scores.
Field-specific optimizations
- keyword fields:
For exact matches on keyword type fields (IDs, keys, etc.), the
exactquery expression is the most efficient option. - text and localizedText fields:
For full-text searches, use the
fullTextquery expression. Be mindful of themustMatchparameter (anyvs.all);allis more restrictive and often more performant. - Numeric and date fields:
Use
rangequeries for filtering on numeric and date ranges. Specify both upper and lower bounds whenever possible to create closed ranges for better performance.
Product Search
- Limit the maximum number of buckets to return in the distinct facet expression.
- Use includes for specific bucket values.
- Apply appropriate scoping and filtering for facet calculation.
Product Projection Search
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.
In some cases, the Product Projection Search endpoint will not return any results.
- 1 000 000 Products per Project
- 3 000 000 Product Variants per Project
- 15 Locales per Project
- 100 Channels per Project
- 20 SearchKeywords per Product and per Locale. When the number of SearchKeywords exceeds the recommended threshold, the API may enforce a limit of 10–20 keywords per Product and per Locale.
- 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)
Filter, facet, and sort
- Consider reducing the number of faceting attributes on the search request if you want to decrease the response time.
- When possible, use
filter.queryfilters 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.
Match Product Variants
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.
Modify data
Multiple update actions
Cache 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
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.
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.
<fieldName>Ref fields where the expanded resource is not needed.