Keep storefront queries fast, extension code within its limits, and published bundles healthy in production.
Performance and operability on the Commerce Integration Layer show up in three places:
- How your storefront queries the Experience API.
- How your schema extension and Managed API Extension code behaves at runtime.
- How a published bundle rolls out and is verified.
This page focuses on operational behavior and verification. For design guidance, see Best practices. For the exact values of request, extension, and outbound call limits, see Limits.
GraphQL queries
The router plans every query against your Project's published supergraph. A single operation can touch the core subgraph and your extension subgraph, then the planner fetches from each and stitches the result.
- Select only what you render. The merged schema is broad. Requesting fields you do not use adds resolver work and payload size with no benefit. See Querying the API.
- Extension fields add latency. Each extension field your query selects is resolved in the extension sandbox during the same request. Prefer nullable fields and degrade gracefully when an external dependency is slow or unavailable.
@requiresadds work. A field that pulls Commerce Integration Layer data through@requirestriggers an extra fetch before your resolver runs. Keep required fields minimal to only what your computation needs.
Introspection is unavailable on the public GraphQL endpoint. Use
commercetools integration-layer explore or schema fetch to inspect the schema while you tune queries.Extension resolvers
Schema extension resolvers run in a restricted sandbox for the lifetime of one GraphQL request, not in a long-lived Node process.
- Finish within the request. The sandbox provides no background scheduling,
setIntervalis unavailable. Do not start work that outlives the resolver as the host does not wait for it. - Keep outbound calls fast. Network access is only through allowlisted
fetch. Each outbound call has time and size limits, and each resolver call can make a limited number of outbound calls. Use timeouts (AbortController) and prefer nullable return types so a slow vendor does not fail the whole query. Add hosts to the allowlist before you deploy. - Do not re-fetch what the graph already owns. Pull Commerce Integration Layer fields with
@requiresinstead of calling commercetools or your own services for data already on the entity. - Return entity stubs. When a field returns an entity such as
Product, return{ id }and let the router resolve the rest to avoid duplicating core-subgraph work in your resolver.
validate catches many sandbox violations before publish and the runtime enforces the rest.Managed API Extensions
A Managed API Extension handler runs inside a commercetools write, synchronously, before the resource is persisted. commercetools applies the callback deadline (approximately two seconds by default) and fails the write if the handler exceeds it.
Keep handlers short by validating or computing from the incoming resource, returning an approval, updating actions, or errors, and avoiding outbound calls unless they are fast and necessary. A handler that throws blocks the write, so return an explicit decision instead of relying on timeouts.
Publishing and rollout
commercetools integration-layer extension push stores a new bundle and, by default, waits until the extension runtime reports whether that revision loaded successfully. Use this in CI so a broken bundle fails the pipeline before you assume it is live.commercetools integration-layer extension push # wait for load (default)
commercetools integration-layer extension push --no-wait # return once bytes are stored
commercetools integration-layer extension push --wait-timeout 300
Each stored revision moves through a lifecycle:
| State | Meaning |
|---|---|
pending | Stored, not yet loaded by the extension runtime. |
running | Loaded and serving. The bundle works. |
failed | The runtime could not load it; a reason explains why. |
If the newest revision fails, the Project keeps serving the newest non-failed revision beneath it. A bad push rolls back rather than taking the Project offline.
A revision marked
running stays serving even if the runtime crashes later. Only a pending revision can be marked failed, so a bundle that already proved itself is never silently retired by a later crash.Checking what's live
Use
extension status to see which bundle a Project has stored:commercetools integration-layer extension status
The command reports the newest stored revision's
version, size, upload time, and, when the push that stored it recorded one, sourceRevision. It does not report lifecycle state, a failure reason, or the revision actually served — for those, use the Merchant Center extension panel, which shows the newest revision alongside the one being served and, if the newest failed, its reason.Every push records your version-control revision with the bundle (
sourceRevision). It appears in extension status, the Merchant Center extension panel (for both the newest and served revisions), extension log lines, and over GraphQL:{ _extensionBundle { version sourceRevision } }