OpenTelemetry integration for Apollo Server - replaces the deprecated `@alphasense/jaeger-apollo-plugin`.
npm install otel-apollo-pluginOpenTelemetry integration for Apollo Server - replaces the deprecated @alphasense/jaeger-apollo-plugin.
This package provides a modern, standards-based approach to distributed tracing in Apollo Server using OpenTelemetry (OTEL), replacing the deprecated Jaeger/OpenTracing implementation.
- Deprecated Dependencies: jaeger-client-node is officially deprecated and unmaintained
- Industry Standard: OpenTelemetry is a CNCF graduated project, the future of observability
- Better Performance: OTLP protocol is more efficient than Jaeger's HTTP/Thrift
- Vendor Flexibility: Easy to switch backends (Datadog, Honeycomb, AWS X-Ray, etc.)
- Active Maintenance: Regular releases and security updates
``bash`
bun add @alphasense/otel-apollo-pluginor
npm install @alphasense/otel-apollo-pluginor
pnpm add @alphasense/otel-apollo-plugin
CRITICAL: Initialize OpenTelemetry at the very top of your entry file, before importing Apollo Server or Express!
`typescript
// index.ts - MUST be the first import!
import { initializeOpenTelemetry } from '@alphasense/otel-apollo-plugin';
// Initialize OTEL first
const sdk = initializeOpenTelemetry({
serviceName: 'graphql-search',
serviceVersion: '1.0.0',
});
// Now import Apollo Server and other modules
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
`
The plugin enriches traces with additional context like operation names, user IDs, and error information.
`typescript
import { ApolloServer } from '@apollo/server';
import { OpenTelemetryPlugin, enrichContextWithOtel } from '@alphasense/otel-apollo-plugin';
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
OpenTelemetryPlugin(),
// ... other plugins
],
});
`
`typescript
import { enrichContextWithOtel } from '@alphasense/otel-apollo-plugin';
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
const baseContext = {
user: await getUserFromToken(req.headers.authorization),
// ... other context fields
};
// Adds requestId from trace ID
return enrichContextWithOtel(baseContext);
},
});
`
`typescript
import { getActiveSpan, getBaggageItem, BAGGAGE_KEYS, SpanStatusCode } from '@alphasense/otel-apollo-plugin';
const resolvers = {
Query: {
user: async (parent, { id }, context) => {
// Get active span to add custom attributes
const span = getActiveSpan();
span?.setAttribute('user.id', id);
// Get baggage values (e.g., clientId from gateway)
const clientId = getBaggageItem(BAGGAGE_KEYS.CLIENT_ID);
try {
const user = await fetchUser(id);
span?.setStatus({ code: SpanStatusCode.OK });
return user;
} catch (error) {
span?.setStatus({
code: SpanStatusCode.ERROR,
message: error.message,
});
span?.recordException(error);
throw error;
}
},
},
};
`
`typescript
import { getTracer, SpanStatusCode } from '@alphasense/otel-apollo-plugin';
async function fetchUserData(userId: number) {
const tracer = getTracer('graphql-search');
const span = tracer.startSpan('fetch-user-data');
try {
const data = await database.query('SELECT * FROM users WHERE id = ?', [userId]);
span.setAttribute('user.id', userId);
span.setAttribute('db.rows_returned', data.length);
span.setStatus({ code: SpanStatusCode.OK });
return data;
} catch (error) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message,
});
span.recordException(error);
throw error;
} finally {
span.end();
}
}
`
`bashEnable/disable OpenTelemetry (default: true)
OTEL_ENABLED=true
$3
`typescript
initializeOpenTelemetry({
serviceName: 'graphql-search',
serviceVersion: '1.0.0',
otlpEndpoint: 'http://grafana-agent-traces.tracing:4317',
enabled: true,
graphqlConfig: {
ignoreTrivialResolveSpans: true, // Skip trivial resolvers (default: true)
depth: 10, // Maximum span depth (default: 10)
mergeItems: true, // Merge list items (default: true)
},
});
`Migration from jaeger-apollo-plugin
$3
#### 1. Context Structure
Before (OpenTracing):
`typescript
interface BaseContext extends JaegerPluginContext {
tracer: Tracer;
rootSpan: Span;
// ...
}
`After (OpenTelemetry):
`typescript
import { getActiveSpan, getTracer } from '@alphasense/otel-apollo-plugin';interface BaseContext {
requestId: string; // From enrichContextWithOtel()
// No tracer or rootSpan needed
// ...
}
// In resolvers:
const span = getActiveSpan();
const tracer = getTracer('my-service');
`#### 2. Baggage Access
Before:
`typescript
const clientId = context.rootSpan?.getBaggageItem('CLIENT_ID');
`After:
`typescript
import { getBaggageItem, BAGGAGE_KEYS } from '@alphasense/otel-apollo-plugin';const clientId = getBaggageItem(BAGGAGE_KEYS.CLIENT_ID);
`#### 3. Span Operations
Before:
`typescript
span.setTag('http.status_code', 200);
span.log({ event: 'error', message: 'Failed' });
span.finish();
`After:
`typescript
span.setAttribute('http.status_code', 200);
span.addEvent('error', { message: 'Failed' });
span.end();
`#### 4. Initialization Order
Critical: OpenTelemetry MUST be initialized before importing Apollo Server:
`typescript
// ✅ Correct
import { initializeOpenTelemetry } from '@alphasense/otel-apollo-plugin';
const sdk = initializeOpenTelemetry({ serviceName: 'my-service' });
import { ApolloServer } from '@apollo/server';// ❌ Wrong - traces will be incomplete!
import { ApolloServer } from '@apollo/server';
import { initializeOpenTelemetry } from '@alphasense/otel-apollo-plugin';
`API Reference
$3
####
initializeOpenTelemetry(config: OtelConfig)Initialize the OpenTelemetry SDK with OTLP exporter.
$3
####
OpenTelemetryPluginApollo Server plugin that enriches traces with GraphQL operation data.
####
enrichContextWithOtelAdds OpenTelemetry data (requestId) to your Apollo Server context.
$3
####
getActiveSpan(): Span | undefinedGet the currently active span from the OpenTelemetry context.
####
getTracer(name: string, version?: string): TracerGet a tracer instance for creating custom spans.
####
getBaggageItem(key: BaggageKey): string | undefinedGet a baggage value from the current context.
####
setBaggageItem(key: BaggageKey, value: string): ContextSet a baggage value in the current context.
####
getAllBaggage(): RecordGet all baggage items as an object.
####
getTraceId(): string | undefinedGet the current trace ID.
####
getSpanId(): string | undefinedGet the current span ID.
$3
####
BAGGAGE_KEYSStandard baggage keys:
-
BAGGAGE_KEYS.CLIENT_ID - Client ID making the request
- BAGGAGE_KEYS.OPERATION_NAME - Root operation nameDevelopment
$3
`bash
bun run build
`$3
`bash
bun test
`$3
`bash
bun run lint
``MIT
Issues and pull requests are welcome!