Comprehensive plugin architecture for extending Apollo Server functionality with built-in plugins for caching, tracing, analytics, federation, and custom integrations.
Core plugin interface defining lifecycle hooks for server and request-level customization.
/**
* Main plugin interface for extending Apollo Server
* @template TContext - The GraphQL context type
*/
interface ApolloServerPlugin<TContext extends BaseContext> {
/**
* Called when the server starts up
* @param service - Server context with logger, cache, schema, etc.
* @returns Optional server listener for additional hooks
*/
serverWillStart?(service: GraphQLServerContext): Promise<GraphQLServerListener | void>;
/**
* Called for each GraphQL request
* @param requestContext - Request context with query, variables, etc.
* @returns Optional request listener for request lifecycle hooks
*/
requestDidStart?(requestContext: GraphQLRequestContext<TContext>): Promise<GraphQLRequestListener<TContext> | void>;
/**
* Called when an unexpected error occurs during request processing
* @param requestContext - Request context when error occurred
* @param error - The unexpected error
*/
unexpectedErrorProcessingRequest?(requestContext: GraphQLRequestContext<TContext>, error: Error): Promise<void>;
}
/**
* Server-level context available to plugins
*/
interface GraphQLServerContext {
/** Logger instance */
logger: Logger;
/** Cache instance */
cache: KeyValueCache<string>;
/** GraphQL schema */
schema: GraphQLSchema;
/** Apollo configuration */
apollo: ApolloConfig;
/** Whether server started in background */
startedInBackground: boolean;
}
/**
* Server lifecycle listener
*/
interface GraphQLServerListener {
/** Called when schema loads or updates */
schemaDidLoadOrUpdate?(schemaContext: GraphQLSchemaContext): Promise<void>;
/** Called to drain server connections */
drainServer?(): Promise<void>;
/** Called when server will stop */
serverWillStop?(): Promise<void>;
/** Called to render landing page */
renderLandingPage?(): Promise<LandingPage>;
}GraphQL cache control implementation with HTTP header generation.
/**
* Enables GraphQL cache control via @cacheControl directive
* @param options - Cache control configuration
* @returns Apollo Server plugin
*/
function ApolloServerPluginCacheControl(
options?: ApolloServerPluginCacheControlOptions
): ApolloServerPlugin;
/**
* Cache control plugin options
*/
interface ApolloServerPluginCacheControlOptions {
/** Default max age for fields in seconds (default: 0) */
defaultMaxAge?: number;
/** Whether to calculate and set HTTP Cache-Control headers */
calculateHttpHeaders?: boolean | 'if-cacheable';
/** Testing-only cache hints override */
__testing__cacheHints?: Map<string, CacheHint>;
}
/**
* Cache annotation interface
*/
interface CacheAnnotation extends CacheHint {
/** Whether to inherit max age from parent field */
inheritMaxAge?: true;
}Comprehensive analytics plugin for reporting operation metrics and traces to Apollo Studio.
/**
* Reports operation metrics and traces to Apollo Studio
* @param options - Usage reporting configuration
* @returns Apollo Server plugin
*/
function ApolloServerPluginUsageReporting<TContext>(
options?: ApolloServerPluginUsageReportingOptions<TContext>
): ApolloServerPlugin<TContext>;
/**
* Usage reporting plugin options
*/
interface ApolloServerPluginUsageReportingOptions<TContext> {
/** Include individual execution traces (default: true) */
sendTraces?: boolean;
/** Variable value reporting policy */
sendVariableValues?: VariableValueOptions;
/** Header reporting policy */
sendHeaders?: SendValuesBaseOptions;
/** Error reporting policy */
sendErrors?: SendErrorsOptions;
/** Include parse/validation failures */
sendUnexecutableOperationDocuments?: boolean;
/** Field-level execution tracking configuration */
fieldLevelInstrumentation?: number | ((request: GraphQLRequestContext<TContext>) => number);
/** Request filtering predicate */
includeRequest?: (requestContext: GraphQLRequestContext<TContext>) => Promise<boolean>;
/** Client information extraction function */
generateClientInfo?: GenerateClientInfo<TContext>;
/** Send reports immediately after each request */
sendReportsImmediately?: boolean;
/** Batch reporting interval in milliseconds */
reportIntervalMs?: number;
/** Size threshold for sending reports (default: 4MB) */
maxUncompressedReportSize?: number;
/** Custom Apollo Studio endpoint URL */
endpointUrl?: string;
/** Custom fetch implementation */
fetcher?: Fetcher;
/** Logger for plugin messages */
logger?: Logger;
}
/**
* Client information interface
*/
interface ClientInfo {
/** Client name */
clientName?: string;
/** Client version */
clientVersion?: string;
}
/**
* Generate client info function type
*/
type GenerateClientInfo<TContext> = (
requestContext: GraphQLRequestContext<TContext>
) => ClientInfo;Customizable GraphQL landing pages with Apollo Studio integration.
/**
* Local development landing page with embedded Sandbox
* @param options - Local landing page configuration
* @returns Apollo Server plugin
*/
function ApolloServerPluginLandingPageLocalDefault(
options?: ApolloServerPluginLandingPageLocalDefaultOptions
): ApolloServerPlugin;
/**
* Production landing page with embedded Explorer
* @param options - Production landing page configuration
* @returns Apollo Server plugin
*/
function ApolloServerPluginLandingPageProductionDefault(
options?: ApolloServerPluginLandingPageProductionDefaultOptions
): ApolloServerPlugin;
/**
* Local landing page options
*/
interface ApolloServerPluginLandingPageLocalDefaultOptions {
/** Embed Apollo Studio Sandbox */
embed?: true | EmbeddableSandboxOptions;
/** Landing page version */
version?: string;
/** Show/hide Apollo footer */
footer?: boolean;
/** Include cookies in Sandbox requests */
includeCookies?: boolean;
/** Initial document to display */
document?: string;
/** Initial variables to display */
variables?: Record<string, any>;
/** Initial headers to display */
headers?: Record<string, string>;
}
/**
* Production landing page options
*/
interface ApolloServerPluginLandingPageProductionDefaultOptions
extends ApolloServerPluginLandingPageLocalDefaultOptions {
/** Apollo Studio graph reference (required for embedded mode) */
graphRef?: string;
}
/** Default embedded Explorer version */
const DEFAULT_EMBEDDED_EXPLORER_VERSION = 'v3';
/** Default embedded Sandbox version */
const DEFAULT_EMBEDDED_SANDBOX_VERSION = 'v2';
/** Default Apollo Server landing page version */
const DEFAULT_APOLLO_SERVER_LANDING_PAGE_VERSION = '_latest';Plugins for Apollo Federation and distributed tracing.
/**
* Generates federation traces for subgraphs
* @param options - Inline trace configuration
* @returns Apollo Server plugin
*/
function ApolloServerPluginInlineTrace(
options?: ApolloServerPluginInlineTraceOptions
): ApolloServerPlugin;
/**
* Inline trace plugin options
*/
interface ApolloServerPluginInlineTraceOptions {
/** How to handle errors in traces */
includeErrors?: SendErrorsOptions;
/** Internal option for conditional activation */
__onlyIfSchemaIsSubgraph?: boolean;
}
/**
* Reports schema changes to Apollo Studio
* @param options - Schema reporting configuration
* @returns Apollo Server plugin
*/
function ApolloServerPluginSchemaReporting(
options?: ApolloServerPluginSchemaReportingOptions
): ApolloServerPlugin;
/**
* Schema reporting plugin options
*/
interface ApolloServerPluginSchemaReportingOptions {
/** Maximum initial delay for reporting in milliseconds */
initialDelayMaxMs?: number;
/** Override schema SDL to report */
overrideReportedSchema?: string;
/** Custom reporting endpoint URL */
endpointUrl?: string;
/** Custom fetch implementation */
fetcher?: Fetcher;
}Plugins for server infrastructure and lifecycle management.
/**
* Gracefully drains HTTP server connections on shutdown
* @param options - Drain HTTP server configuration
* @returns Apollo Server plugin
*/
function ApolloServerPluginDrainHttpServer(
options: ApolloServerPluginDrainHttpServerOptions
): ApolloServerPlugin;
/**
* Drain HTTP server plugin options
*/
interface ApolloServerPluginDrainHttpServerOptions {
/** HTTP server instance to drain */
httpServer: http.Server;
/** Grace period before force close in milliseconds */
stopGracePeriodMillis?: number;
}
/**
* Implements callback-based subscriptions for Apollo Router
* @param options - Subscription callback configuration
* @returns Apollo Server plugin
*/
function ApolloServerPluginSubscriptionCallback(
options?: ApolloServerPluginSubscriptionCallbackOptions
): ApolloServerPlugin;
/**
* Subscription callback plugin options
*/
interface ApolloServerPluginSubscriptionCallbackOptions {
/** Maximum consecutive heartbeat failures before termination */
maxConsecutiveHeartbeatFailures?: number;
/** Logger for plugin messages */
logger?: Logger;
/** Retry configuration for requests */
retry?: retry.Options;
/** Custom fetch implementation */
fetcher?: Fetcher;
}Plugins for disabling features and modifying behavior.
/**
* Removes GraphQL validation error suggestions
* @returns Apollo Server plugin
*/
function ApolloServerPluginDisableSuggestions(): ApolloServerPlugin;
/**
* Disables built-in cache control plugin
* @returns Apollo Server plugin
*/
function ApolloServerPluginCacheControlDisabled(): ApolloServerPlugin<BaseContext>;
/**
* Disables built-in inline trace plugin
* @returns Apollo Server plugin
*/
function ApolloServerPluginInlineTraceDisabled(): ApolloServerPlugin<BaseContext>;
/**
* Disables built-in landing page plugin
* @returns Apollo Server plugin
*/
function ApolloServerPluginLandingPageDisabled(): ApolloServerPlugin<BaseContext>;
/**
* Disables built-in schema reporting plugin
* @returns Apollo Server plugin
*/
function ApolloServerPluginSchemaReportingDisabled(): ApolloServerPlugin<BaseContext>;
/**
* Disables built-in usage reporting plugin
* @returns Apollo Server plugin
*/
function ApolloServerPluginUsageReportingDisabled(): ApolloServerPlugin<BaseContext>;import { ApolloServer } from "@apollo/server";
import {
ApolloServerPluginCacheControl,
ApolloServerPluginLandingPageLocalDefault
} from "@apollo/server/plugin/cacheControl";
import { ApolloServerPluginLandingPageLocalDefault } from "@apollo/server/plugin/landingPage/default";
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
// Enable cache control with custom settings
ApolloServerPluginCacheControl({
defaultMaxAge: 300, // 5 minutes
calculateHttpHeaders: 'if-cacheable',
}),
// Custom landing page
ApolloServerPluginLandingPageLocalDefault({
embed: {
initialState: {
document: `query GetBooks { books { title author } }`,
variables: {},
headers: { authorization: 'Bearer token' },
},
},
}),
],
});import type { ApolloServerPlugin, GraphQLRequestListener } from "@apollo/server";
// Simple logging plugin
const loggingPlugin: ApolloServerPlugin = {
requestDidStart() {
return Promise.resolve({
didResolveOperation(requestContext) {
console.log(`Operation: ${requestContext.operationName || 'Anonymous'}`);
return Promise.resolve();
},
didEncounterErrors(requestContext) {
console.error('GraphQL errors:', requestContext.errors);
return Promise.resolve();
},
willSendResponse(requestContext) {
console.log('Response sent');
return Promise.resolve();
},
} as GraphQLRequestListener);
},
};
// Authentication plugin
interface AuthContext {
user?: User;
}
const authPlugin: ApolloServerPlugin<AuthContext> = {
requestDidStart() {
return Promise.resolve({
didResolveOperation(requestContext) {
const { user } = requestContext.contextValue;
if (!user && requiresAuth(requestContext.operationName)) {
throw new Error('Authentication required');
}
return Promise.resolve();
},
});
},
};
const server = new ApolloServer<AuthContext>({
typeDefs,
resolvers,
plugins: [loggingPlugin, authPlugin],
});import { ApolloServerPluginUsageReporting } from "@apollo/server/plugin/usageReporting";
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
ApolloServerPluginUsageReporting({
// Send detailed traces for better insights
sendTraces: true,
// Include variable values (excluding sensitive ones)
sendVariableValues: {
transform: ({ variables, operationString }) => {
// Remove sensitive variables
const sanitized = { ...variables };
delete sanitized.password;
delete sanitized.token;
return sanitized;
},
},
// Include relevant headers
sendHeaders: {
onlyNames: ['user-agent', 'referer'],
},
// Extract client information
generateClientInfo: ({ request }) => ({
clientName: request.http?.headers.get('x-client-name'),
clientVersion: request.http?.headers.get('x-client-version'),
}),
// Filter out health checks
includeRequest: async (requestContext) => {
return requestContext.operationName !== 'HealthCheck';
},
}),
],
});import http from 'http';
import { ApolloServer } from "@apollo/server";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
// Create HTTP server
const httpServer = http.createServer();
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
// Enable graceful shutdown
ApolloServerPluginDrainHttpServer({
httpServer,
stopGracePeriodMillis: 5000, // 5 second grace period
}),
],
});
await server.start();
// Set up GraphQL endpoint
httpServer.on('request', async (req, res) => {
// Handle GraphQL requests...
});
// Graceful shutdown
process.on('SIGTERM', async () => {
console.log('SIGTERM received, shutting down gracefully');
httpServer.close(() => {
console.log('HTTP server closed');
});
await server.stop();
});
httpServer.listen(4000);