GraphQL
Guidelines for GraphQL client integrations — error capture, performance instrumentation, breadcrumbs, and server/client differences.
GraphQL client integrations should match the guidelines for HTTP Client Integrations with differences described below.
The failedRequestStatusCodes parameter does not exist because GraphQL errors are not HTTP errors, so that the request can be errored even though the HTTP status code of the response is successful.
Instead, the error has to be captured if the GraphQL response contains an errors array. This can be done by regexing the response body, e.g.:
val regex = "(?i)\"errors\"\\s*:\\s*\\[".toRegex()
// [body] is the stringified GraphQL response body
if (regex.containsMatchIn(body)) {
// captures the error
}
val regex = "(?i)\"errors\"\\s*:\\s*\\[".toRegex()
// [body] is the stringified GraphQL response body
if (regex.containsMatchIn(body)) {
// captures the error
}
Additional fields for breadcrumbs:
- data (all fields are optional but recommended):
operation_name- The GraphQL operation nameoperation_type- The GraphQL operation type, i.e:query,mutation,subscriptionoperation_id- The GraphQL operation ID
Required fields for the Request interface:
{
"request": {
"api_target": "graphql",
"data": {
"foo": "bar"
}
}
}
{
"request": {
"api_target": "graphql",
"data": {
"foo": "bar"
}
}
}
The data field is a JSON object that contains the GraphQL request payload.
Required fields for the Response interface:
{
"contexts": {
"response": {
"data": {
"foo": "bar"
}
}
}
}
{
"contexts": {
"response": {
"data": {
"foo": "bar"
}
}
}
}
The data field is a JSON object that contains the GraphQL response payload. Attaching request and response bodies should be guarded by sendDefaultPii and/or another flag to opt-in (e.g. captureFailedRequests).
Required fields for the Event interface:
The fingerprints field should be set to ["$operationName", "$operationType", "$statusCode"].
{
"fingerprints": ["$operationName", "$operationType", "$statusCode"]
}
{
"fingerprints": ["$operationName", "$operationType", "$statusCode"]
}
The GraphQL Performance integration should match the guidelines for GraphQL Client Error Capture with a few differences:
The transaction's name should be set with the GraphQL operation name, if possible, otherwise fallback to something unique that makes sense, e.g. the canonical name of the actual/generated class.
The transaction's description should be set with the GraphQL operation name, operation type (query, mutation or subscription) and status code, if possible.
The request.api_target should be set with graphql.
The request.data should be set with the raw GraphQL request payload, if possible. This should be guarded by an opt-in flag, e.g. sendDefaultPii.
The contexts.response.data should be set with the raw GraphQL response payload, only if there were errors. This should be guarded by an opt-in flag, e.g. sendDefaultPii and maxResponseBodySize.
Some frameworks may use a Stream object for the response, in this case, the object can't be consumed twice, so the SDK should try check and clone the object, if possible.
Spans should be created for resolvers, if possible. These are sometimes also called data fetchers.
Spans should be created for data loaders, if possible.
The operation type should follow the Span Operation Conventions.
Extra (data) attributes for transactions and/or spans, there are Span Data and OTel GraphQL conventions.
Instrumenting APM for GraphQL will depend on the instrumented GraphQL library, if there are available hooks for it, the SDK should use them, otherwise, the SDK could try to monkeypatch the library or instrument the transport layer using heuristics, for example, if the URL ends with graphql, if there are HTTP Headers, etc.
If there are hooks available and the transport layer is also instrumented (e.g. Apollo Interceptors for GraphQL and Spring), the SDK should give preference to the layer that has more information and avoid creating duplicate transactions/spans, or merge the information, if possible.
Spring GraphQL has its own observation package.
GraphQL Java has its own instrumentation package.
Apollo GraphQL has its own tracing extensions, in this case it'd even be possible to create synthetic transactions and spans out of the tracing extension.
Changes in the product may be necessary, e.g. if request.api_target is set to graphql, the request.data and contexts.response.data should do syntax highlighting.
Performance issues can be created for GraphQL transactions and spans, for example, N+1, query complexity, etc.
GraphQL Performance for Clients is very similar to the implementation for Servers, the difference is that you'll create a span instead of a transaction.
Spans don't contain the request and response interfaces, but set the span description similarly to the transaction description.
| Version | Date | Summary |
|---|---|---|
1.0.0 | 2025-02-24 | Initial spec, extracted from expected features |
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").