> ## Documentation Index
> Fetch the complete documentation index at: https://braintrust.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Apollo GraphQL

> Trace Apollo GraphQL queries, mutations, and resolvers in Braintrust to debug and monitor your GraphQL API

[Apollo GraphQL](https://www.apollographql.com/) is a platform for building GraphQL APIs. Braintrust traces Apollo GraphQL operations across Apollo Server applications and the Apollo Router federation gateway.

<View title="TypeScript" icon="/images/sdk-icons/typescript.svg">
  <h2 id="tracing-typescript">
    Tracing
  </h2>

  Braintrust traces Apollo Server operations through an OpenTelemetry pipeline. Install the SDK and dependencies, then attach Braintrust's span processor and instrument your resolvers.

  <h3 id="setup-typescript">
    Setup
  </h3>

  Install the Braintrust TypeScript SDK with the OpenTelemetry dependencies, then configure your environment. This integration uses Braintrust's [TypeScript SDK OpenTelemetry configuration](/docs/integrations/sdk-integrations/opentelemetry#typescript-sdk-configuration).

  <Steps>
    <Step title="Install packages">
      Install the [Braintrust TypeScript SDK](/docs/sdks/typescript/quickstart) alongside the OpenTelemetry, Apollo Server, and utility dependencies.

      <CodeGroup>
        ```bash pnpm theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
        pnpm add braintrust @braintrust/otel @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-trace-base @apollo/server @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources @opentelemetry/semantic-conventions dotenv
        ```

        ```bash npm theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
        npm install braintrust @braintrust/otel @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-trace-base @apollo/server @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources @opentelemetry/semantic-conventions dotenv
        ```
      </CodeGroup>
    </Step>

    <Step title="Set environment variables">
      ```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      # Required
      BRAINTRUST_API_KEY=your-api-key

      # Parent identifier for organizing traces
      # Format: project_name:experiment_name
      BRAINTRUST_PARENT=project_name:apollo-graphql

      # Optional: Custom OpenTelemetry endpoint (for self-hosted Braintrust)
      # BRAINTRUST_OTEL_ENDPOINT=https://api.braintrust.dev/otel/v1/traces
      ```
    </Step>
  </Steps>

  <h3 id="opentelemetry-typescript">
    OpenTelemetry
  </h3>

  Configure a tracer provider with Braintrust's span processor, then create spans in your resolvers. Add the following to `apollo-graphql-braintrust.ts`:

  <CodeGroup>
    ```typescript title="apollo-graphql-braintrust.ts" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    import { trace } from "@opentelemetry/api";
    import { SpanStatusCode } from "@opentelemetry/api";
    import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
    import { resourceFromAttributes } from "@opentelemetry/resources";
    import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
    import { BraintrustSpanProcessor } from "@braintrust/otel";

    const provider = new BasicTracerProvider({
      resource: resourceFromAttributes({
        [ATTR_SERVICE_NAME]: "graphql-api",
      }),
      spanProcessors: [new BraintrustSpanProcessor()]
    });

    provider.register();

    // Get a tracer for your GraphQL operations
    const tracer = trace.getTracer("apollo-graphql", "1.0.0");

    // Import Apollo Server
    import { ApolloServer } from "@apollo/server";
    import { startStandaloneServer } from "@apollo/server/standalone";

    // Define your schema
    const typeDefs = `#graphql
      type Query {
        hello(name: String): String!
        books: [Book!]!
        book(id: ID!): Book
      }

      type Book {
        id: ID!
        title: String!
        author: String!
        year: Int
      }

      type Mutation {
        addBook(title: String!, author: String!, year: Int): Book!
      }
    `;

    // Mock data functions (replace with your actual implementation)
    async function fetchBooks() {
      return [
        {
          id: "1",
          title: "The Great Gatsby",
          author: "F. Scott Fitzgerald",
          year: 1925,
        },
        { id: "2", title: "1984", author: "George Orwell", year: 1949 },
      ];
    }

    async function fetchBookById(id: string) {
      const books = await fetchBooks();
      return books.find((book) => book.id === id);
    }

    async function createBook({
      title,
      author,
      year,
    }: {
      title: string;
      author: string;
      year?: number;
    }) {
      return {
        id: String(Date.now()),
        title,
        author,
        year: year || new Date().getFullYear(),
      };
    }

    // Implement resolvers with tracing
    const resolvers = {
      Query: {
        hello: (_: any, { name }: { name?: string }) => {
          // Create a span for this resolver
          const span = tracer.startSpan("query.hello");
          span.setAttributes({
            "graphql.operation": "query",
            "graphql.field": "hello",
            "input.name": name || "undefined",
          });

          try {
            const result = `Hello, ${name || "World"}!`;
            span.setStatus({ code: SpanStatusCode.OK });
            return result;
          } catch (error) {
            span.recordException(error as Error);
            span.setStatus({ code: SpanStatusCode.ERROR });
            throw error;
          } finally {
            span.end();
          }
        },

        books: async () => {
          const span = tracer.startSpan("query.books");
          span.setAttributes({
            "graphql.operation": "query",
            "graphql.field": "books",
          });

          try {
            const books = await fetchBooks();
            span.setAttribute("books.count", books.length);
            span.setStatus({ code: SpanStatusCode.OK });
            return books;
          } catch (error) {
            span.recordException(error as Error);
            span.setStatus({ code: SpanStatusCode.ERROR });
            throw error;
          } finally {
            span.end();
          }
        },

        book: async (_: any, { id }: { id: string }) => {
          const span = tracer.startSpan("query.book");
          span.setAttributes({
            "graphql.operation": "query",
            "graphql.field": "book",
            "book.id": id,
          });

          try {
            const book = await fetchBookById(id);
            span.setAttribute("book.found", book ? "true" : "false");
            span.setStatus({ code: SpanStatusCode.OK });
            return book;
          } catch (error) {
            span.recordException(error as Error);
            span.setStatus({ code: SpanStatusCode.ERROR });
            throw error;
          } finally {
            span.end();
          }
        },
      },

      Mutation: {
        addBook: async (
          _: any,
          { title, author, year }: { title: string; author: string; year?: number },
        ) => {
          const span = tracer.startSpan("mutation.addBook");
          span.setAttributes({
            "graphql.operation": "mutation",
            "graphql.field": "addBook",
            "book.title": title,
            "book.author": author,
          });

          if (year) span.setAttribute("book.year", year);

          try {
            const newBook = await createBook({ title, author, year });
            span.setAttribute("book.id", newBook.id);
            span.setStatus({ code: SpanStatusCode.OK });
            return newBook;
          } catch (error) {
            span.recordException(error as Error);
            span.setStatus({ code: SpanStatusCode.ERROR });
            throw error;
          } finally {
            span.end();
          }
        },
      },
    };

    // Create and start Apollo Server
    const server = new ApolloServer({ typeDefs, resolvers });

    async function main() {
      const { url } = await startStandaloneServer(server, {
        listen: { port: 4000 },
        context: async ({ req }) => {
          // Create a root span for each GraphQL request
          const rootSpan = tracer.startSpan("graphql.request");
          rootSpan.setAttribute("http.method", req.method || "POST");
          rootSpan.setAttribute("http.url", req.url || "/graphql");

          // The span will be automatically exported when it ends
          // BraintrustSpanProcessor handles the batching and sending
          setTimeout(() => rootSpan.end(), 100);

          return {};
        },
      });

      console.log(`🚀 Server ready at: ${url}`);
    }

    main().catch(console.error);
    ```
  </CodeGroup>

  <h3 id="tracing-resources-typescript">
    Tracing resources
  </h3>

  * [Apollo GraphQL documentation](https://www.apollographql.com/docs/)
  * [Braintrust OpenTelemetry guide](/docs/integrations/sdk-integrations/opentelemetry)
</View>

<h2 id="apollo-router">
  Apollo Router
</h2>

Apollo Router is a language-neutral federation gateway configured through a YAML file. To trace it, enable its OpenTelemetry exporter and point OTLP at Braintrust. Add the following to `apollo-router-braintrust.yaml`:

```yaml title="apollo-router-braintrust.yaml" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
connectors:
  preview_connect_v0_3: true
  sources:
    # OpenAI API configuration
    openai:
      $config:
        apiKey: ${env.OPENAI_API_KEY}

# OpenTelemetry configuration for Braintrust
telemetry:
  exporters:
    tracing:
      # OTLP exporter for Braintrust
      otlp:
        enabled: true
        # Braintrust OTEL endpoint
        endpoint: ${env.BRAINTRUST_OTEL_ENDPOINT:-https://api.braintrust.dev/otel}
        # Use HTTP protocol for Braintrust
        protocol: http
        # HTTP configuration with headers for Braintrust
        http:
          headers:
            # Braintrust API authentication
            Authorization: Bearer ${env.BRAINTRUST_API_KEY}
            # Parent project/experiment for traces
            x-bt-parent: ${env.BRAINTRUST_PARENT:-project_name:apollo-graphql-project}
        # Batch processor configuration for optimal performance
        batch_processor:
          scheduled_delay: 5s
          max_concurrent_exports: 2
          max_export_batch_size: 512
          max_export_timeout: 30s
          max_queue_size: 2048

    metrics:
      # Prometheus endpoint for local debugging
      prometheus:
        enabled: true
        listen: 0.0.0.0:9090
        path: /metrics
      # Optional: OTLP metrics to Braintrust
      otlp:
        enabled: false
        endpoint: ${env.BRAINTRUST_OTEL_ENDPOINT:-https://api.braintrust.dev/otel}
        protocol: http
        http:
          headers:
            Authorization: Bearer ${env.BRAINTRUST_API_KEY}
            x-bt-parent: ${env.BRAINTRUST_PARENT:-project_name:apollo-graphql-project}

# Include subgraph errors in responses for debugging
include_subgraph_errors:
  all: true

# GraphQL query limits
limits:
  max_depth: 20
  max_height: 200
  max_aliases: 30
  max_root_fields: 30
```

<h3 id="apollo-router-resources">
  Apollo Router resources
</h3>

* [Apollo Router documentation](https://www.apollographql.com/docs/router/)
* [Braintrust OpenTelemetry guide](/docs/integrations/sdk-integrations/opentelemetry)
