# Operations and performance

**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](/commerce-integration-layer/schema-extensions.md) and [Managed API Extension](/commerce-integration-layer/managed-api-extensions.md) 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](/commerce-integration-layer/development/best-practices.md). For the exact values of request, extension, and outbound call limits, see [Limits](/commerce-integration-layer/limits.md).

## GraphQL queries

The [router](/commerce-integration-layer/overview.md#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](/commerce-integration-layer/development/best-practices.md#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.
- **`@requires` adds work.** A field that pulls Commerce Integration Layer data through `@requires` triggers 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`](/commerce-integration-layer/development/get-started.md#explore-your-projects-api) or [`schema fetch`](/commerce-integration-layer/development/get-started.md#fetch-the-schema) to inspect the schema while you tune queries.

## Extension resolvers

Schema extension resolvers run in a [restricted sandbox](/commerce-integration-layer/development/writing-schema-extensions.md#runtime-constraints) for the lifetime of one GraphQL request, not in a long-lived Node process.

- **Finish within the request.** The sandbox provides no background scheduling, `setInterval` is 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](/commerce-integration-layer/limits.md#outbound-requests), and each resolver call can make a [limited number of outbound calls](/commerce-integration-layer/limits.md#resolver-execution). Use timeouts (`AbortController`) and prefer nullable return types so a slow vendor does not fail the whole query. Add hosts to the [allowlist](/commerce-integration-layer/development/configure-and-deploy.md#outbound-http-domain-allowlist) before you deploy.
- **Do not re-fetch what the graph already owns.** Pull Commerce Integration Layer fields with `@requires` instead 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](/commerce-integration-layer/managed-api-extensions.md) 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.

```bash
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:

```bash
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:

```graphql
{ _extensionBundle { version sourceRevision } }
```

## Related pages

- [Area overview page with navigation](/commerce-integration-layer.md)
- [Previous page: Configure and deploy](/commerce-integration-layer/development/configure-and-deploy.md)
- [Next page: Monitoring and alerting](/commerce-integration-layer/development/monitoring-and-alerting.md)
- [Search documentation and API specs](/search.md)