# Apollo 3 Integration | Sentry for Servlet

To be able to capture transactions, you'll need to first [set up tracing](https://docs.sentry.io/platforms/java/guides/servlet/tracing.md).

Sentry's Apollo 3 integration provides both the `SentryApollo3Interceptor` and the `SentryApollo3HttpInterceptor`, which create a span for each outgoing HTTP request executed with an [Apollo Kotlin](https://github.com/apollographql/apollo-kotlin) GraphQL client. The integration also provides extension functions on the `ApolloClient.Builder`.

This integration supports Apollo 3.x. For Apollo 4.x use our [Apollo 4 integration](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo4.md).

## [Install](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo3.md#install)

Install the Apollo 3 integration:

```xml
<dependency>
    <groupId>io.sentry</groupId>
    <artifactId>sentry-apollo-3</artifactId>
    <version>8.24.0</version>
</dependency>
```

For other dependency managers, see the [central Maven repository](https://search.maven.org/artifact/io.sentry/sentry-apollo-3).

## [Configure With Extension](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo3.md#configure-with-extension)

Add `Interceptors` to `ApolloClient.Builder` using `SentryApolloBuilderExtensions`:

```java
import com.apollographql.apollo3.ApolloClient;
import io.sentry.apollo3.SentryApolloBuilderExtensionsKt;

ApolloClient apollo = SentryApolloBuilderExtensionsKt.sentryTracing(new ApolloClient.Builder())
    .serverUrl("https://your-api-host/")
    .build();
```

## [Manual Configuration](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo3.md#manual-configuration)

Because `HttpInterceptors` need to be added to the `NetworkTransport`, the `SentryInterceptors` have to be added manually if you're using a custom `NetworkTransport`:

```java
import com.apollographql.apollo3.ApolloClient;
import com.apollographql.apollo3.network.http.HttpNetworkTransport;
import io.sentry.apollo3.SentryApollo3HttpInterceptor;
import io.sentry.apollo3.SentryApollo3Interceptor;

ApolloClient apollo = new ApolloClient.Builder()
        .networkTransport(
            new HttpNetworkTransport.Builder()
                .serverUrl("https://your-api-host/")
                .addInterceptor(new SentryApollo3HttpInterceptor())
                .build())
        .addInterceptor(new SentryApollo3Interceptor())
        .build();
```

##### Important

Apollo Kotlin is built with Kotlin coroutines. This means that `SentryApolloInterceptor` can be used with Java using only Global Hub Mode (single Hub used by all threads), with Kotlin using single Hub mode, or with [Sentry's coroutines support](https://docs.sentry.io/platforms/java/guides/servlet/enriching-events/scopes.md#kotlin-coroutines).

## [Using With Java](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo3.md#using-with-java)

Configure Global Hub Mode:

```java
import io.sentry.Sentry;

Sentry.init(options -> {
  ..
}, true)
```

In Global Hub Mode, all threads use the same Hub.

## [Using With Kotlin Coroutines](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo3.md#using-with-kotlin-coroutines)

Make sure that your coroutine has access to the correct Sentry context when launching, by providing an instance of `SentryContext`:

```kotlin
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.exception.ApolloException
import io.sentry.kotlin.SentryContext
import kotlinx.coroutines.launch

launch(SentryContext()) {
  val response = try {
    apollo.query(..).toDeferred().await()
  } catch (e: ApolloException) {
    // handle protocol errors
    return@launch
  }
}
```

## [Modify or Drop Spans](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo3.md#modify-or-drop-spans)

Spans created around requests can be modified or dropped using `SentryApollo3HttpInterceptor.BeforeSpanCallback` passed to `SentryApollo3HttpInterceptor` or the `sentryTracing` extension function:

```java
import com.apollographql.apollo3.ApolloClient;
import io.sentry.apollo3.SentryApolloBuilderExtensionsKt;

ApolloClient apollo = SentryApolloBuilderExtensionsKt.sentryTracing(
            new ApolloClient.Builder(),
            (span, request, response) -> {
              if ("LaunchDetails".equals(span.getOperation())) {
                span.setTag("tag-name", "tag-value");
              }
              return span;
            })
        .serverUrl("https://your-api-host/")
        .build();
```

## [GraphQL Client Errors](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo3.md#graphql-client-errors)

This feature automatically captures GraphQL client errors (like bad operations and response codes) as error events and reports them to Sentry. The error event will contain the `request` and `response` data, including the `url`, `status_code`, and `data` (stringified `query`).

Sentry will group GraphQL client errors by the `operationName`, `operationType`, and `statusCode`, so that you can easily see the number of errors that happened for each.

This feature is enabled by default and can be disabled by setting the `captureFailedRequests` option to `false`:

```kotlin
import com.apollographql.apollo3.ApolloClient
import io.sentry.apollo3.sentryTracing

val apollo = ApolloClient.builder()
    .serverUrl("https://your-api-host/")
    .sentryTracing(captureFailedRequests = false)
    .build()
```

By default, only GraphQL client errors with a response that includes the [errors](https://spec.graphql.org/October2021/#sec-Errors) array will be captured as error events.

GraphQL client errors from every target (`.*` regular expression) are automatically captured, but you can change this behavior by setting the `failedRequestTargets` option with either a regular expression or a plain `String`. A plain string must contain at least one of the items from the list. Plain strings don't have to be full matches, meaning the URL of a request is matched when it contains a string provided through the option.

```kotlin
import com.apollographql.apollo3.ApolloClient
import io.sentry.apollo3.sentryTracing

val apollo = ApolloClient.builder()
    .serverUrl("https://your-api-host/")
    .sentryTracing(captureFailedRequests = true, failedRequestTargets = listOf("myapi.com"))
    .build()
```

By default, error events won't contain `Headers` or `Cookies`, but you can change this behavior by setting the `sendDefaultPii` option to `true`:

```kotlin
Sentry.init { options ->
  options.isSendDefaultPii = true
}
```

Error events will contain the raw bodies of GraphQL requests and responses, which may include sensitive data. To avoid this, parameterize your queries using the [variables](https://spec.graphql.org/October2021/#sec-Language.Variables) field. [Relay](https://docs.sentry.io/product/relay.md) will then run [PII Data Scrubbing](https://docs.sentry.io/product/relay.md#pii-data-scrubbing), automatically transforming values into `[Filtered]`.

Alternatively, you can customize the event and scrub the data yourself.

### [Customize or Drop the Error Event](https://docs.sentry.io/platforms/java/guides/servlet/tracing/instrumentation/apollo3.md#customize-or-drop-the-error-event)

The captured error event can be customized or dropped with a `BeforeSendCallback`:

```kotlin
import io.sentry.Sentry
import io.sentry.SentryOptions.BeforeSendCallback
import com.apollographql.apollo3.api.http.HttpRequest
import com.apollographql.apollo3.api.http.HttpResponse
import io.sentry.TypeCheckHint.APOLLO_REQUEST
import io.sentry.TypeCheckHint.APOLLO_RESPONSE

Sentry.init { options ->
  // Add a callback that will be used before the event is sent to Sentry.
  // With this callback, you can modify the event or, when returning null, also discard the event.
  options.beforeSend = BeforeSendCallback { event, hint ->
    val request = hint.getAs(APOLLO_REQUEST, HttpRequest::class.java)
    val response = hint.getAs(APOLLO_RESPONSE, HttpResponse::class.java)

    // customize or drop the event
    event
  }
}
```
