or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

caching-performance.mdcontext-types.mderror-handling.mdindex.mdlogging-system.mdplugin-system.mdrequest-processing.mdresult-processing.mdschema-management.mdserver-configuration.mdsubscription-system.md
tile.json

plugin-system.mddocs/

Plugin System

Comprehensive plugin system for extending server functionality with custom middleware, authentication, caching, validation, and more. Based on Envelop architecture with lifecycle hooks.

Capabilities

Core Plugin Interface

Base plugin interface defining lifecycle hooks for customizing server behavior.

/**
 * Core plugin interface with lifecycle hooks
 */
interface Plugin<
  TPluginContext extends Record<string, any> = {},
  TServerContext extends Record<string, any> = {},
  TUserContext extends Record<string, any> = {},
> {
  /** Hook called during server initialization */
  onYogaInit?: OnYogaInitHook<TServerContext>;
  /** Hook called during request parsing */
  onRequestParse?: OnRequestParseHook<TServerContext>;
  /** Hook called when GraphQL parameters are available */
  onParams?: OnParamsHook<TServerContext>;
  /** Hook called after GraphQL execution */
  onExecutionResult?: OnExecutionResultHook<TServerContext>;
  /** Hook called during result processing */
  onResultProcess?: OnResultProcess<TServerContext>;
  /** Hook called during plugin initialization */
  onPluginInit?: OnPluginInitHook<TPluginContext>;
}

Hook Type Definitions

Type definitions for plugin lifecycle hooks and their event payloads.

/**
 * Server initialization hook
 */
type OnYogaInitHook<TServerContext extends Record<string, any>> = (
  payload: OnYogaInitEventPayload<TServerContext>
) => void;

interface OnYogaInitEventPayload<TServerContext extends Record<string, any>> {
  yoga: YogaServer<TServerContext, any>;
}

/**
 * Request parsing hook
 */
type OnRequestParseHook<TServerContext> = (
  payload: OnRequestParseEventPayload<TServerContext>
) => OnRequestParseHookResult | Promise<OnRequestParseHookResult>;

interface OnRequestParseEventPayload<TServerContext> {
  request: Request;
  url: URL;
  requestParser?: RequestParser;
  serverContext?: TServerContext;
}

type OnRequestParseHookResult = {
  onRequestParseDone?: OnRequestParseDoneHook;
};

/**
 * Parameters hook
 */
type OnParamsHook<TServerContext> = (
  payload: OnParamsEventPayload<TServerContext>
) => void | Promise<void>;

interface OnParamsEventPayload<TServerContext = Record<string, unknown>> {
  params: GraphQLParams;
  request: Request;
  serverContext?: TServerContext;
  setParams: (params: GraphQLParams) => void;
  setRequest: (request: Request) => void;
  fetchAPI: FetchAPI;
}

/**
 * Execution result hook
 */
type OnExecutionResultHook<TServerContext> = (
  payload: OnExecutionResultEventPayload<TServerContext>
) => void | Promise<void>;

interface OnExecutionResultEventPayload<TServerContext> {
  result: ExecutionResult;
  setResult: (result: ExecutionResult | AsyncIterable<ExecutionResult>) => void;
  serverContext?: TServerContext;
}

/**
 * Result processing hook
 */
type OnResultProcess<TServerContext> = (
  payload: OnResultProcessEventPayload<TServerContext>
) => void | Promise<void>;

interface OnResultProcessEventPayload<TServerContext> {
  result: ResultProcessorInput;
  setResult: (result: ResultProcessorInput) => void;
  serverContext?: TServerContext;
  request: Request;
  fetchAPI: FetchAPI;
}

GraphiQL Plugin

Plugin for integrating GraphiQL IDE with customizable options.

/**
 * GraphiQL plugin for development IDE integration
 * @param config - GraphiQL configuration options
 * @returns Plugin instance
 */
function useGraphiQL<TServerContext extends Record<string, any>>(
  config?: GraphiQLPluginConfig<TServerContext>
): Plugin<TServerContext & Record<string, any> & YogaInitialContext, TServerContext, Record<string, any>>;

interface GraphiQLPluginConfig<TServerContext> {
  /** GraphiQL options or factory function */
  options?: GraphiQLOptionsOrFactory<TServerContext>;
  /** Custom render function */
  render?: (options: GraphiQLOptions) => string;
}

type GraphiQLOptionsOrFactory<TServerContext> =
  | GraphiQLOptions
  | GraphiQLOptionsFactory<TServerContext>;

type GraphiQLOptionsFactory<TServerContext> = (
  request: Request,
  serverContext?: TServerContext
) => GraphiQLOptions | Promise<GraphiQLOptions>;

interface GraphiQLOptions {
  /** Default headers to be set when opening a new tab */
  defaultHeaders?: string;
  /** Default GraphQL query string to show */
  defaultQuery?: string;
  /** Default set of tabs with queries, variables, and headers */
  defaultTabs?: Array<{
    headers?: string | null;
    query: string | null;
    variables?: string | null;
  }>;
  /** Initial headers for the header editor (JSON encoded string) */
  headers?: string;
  /** Whether to persist headers contents in storage */
  shouldPersistHeaders?: boolean;
  /** Request credentials mode */
  credentials?: RequestCredentials;
  /** Favicon URL for the page (defaults to Yoga logo) */
  favicon?: string;
  /** Page title (defaults to "Yoga GraphiQL") */
  title?: string;
  /** Logo to display in top right corner */
  logo?: string;
  /** Protocol for subscriptions */
  subscriptionsProtocol?: 'SSE' | 'GRAPHQL_SSE' | 'WS' | 'LEGACY_WS';
  /** Extra headers to always include with user headers */
  additionalHeaders?: Record<string, string>;
  /** HTTP method for querying original schema */
  method?: 'GET' | 'POST';
  /** Whether to use GET for queries when querying original schema */
  useGETForQueries?: boolean;
  /** External fragments to include in query document */
  externalFragments?: string;
  /** Maximum number of executed operations to store (default: 20) */
  maxHistoryLength?: number;
  /** Whether target server supports input value deprecation (default: false) */
  inputValueDeprecation?: boolean;
  /** Custom operation name for introspection query */
  introspectionQueryName?: string;
  /** Whether to include schema description in introspection (default: false) */
  schemaDescription?: boolean;
  /** Editor theme (default: "graphiql") */
  editorTheme?: string;
  /** Key map for editor (default: "sublime") */
  keyMap?: 'sublime' | 'emacs' | 'vim';
  /** Default visibility of editor tools */
  defaultEditorToolsVisibility?: boolean | 'variables' | 'headers';
  /** Whether headers editor is enabled */
  isHeadersEditorEnabled?: boolean;
  /** Whether to disable tabs */
  disableTabs?: boolean;
  /** Whether to include isRepeatable flag on directives (default: false) */
  directiveIsRepeatable?: boolean;
  /** Enable experimental fragment variables */
  experimentalFragmentVariables?: boolean;
  /** Convert GraphQL comments to descriptions (default: false) */
  commentDescriptions?: boolean;
  /** Request timeout in milliseconds */
  timeout?: number;
  /** Number of retry attempts */
  retry?: number;
  /** GraphQL endpoint URL (defaults to "/graphql") */
  endpoint?: string;
}

/**
 * Render GraphiQL HTML interface
 * @param opts - GraphiQL options
 * @returns HTML string for GraphiQL interface
 */
function renderGraphiQL(opts: GraphiQLOptions): string;

/**
 * Check if GraphiQL should be rendered based on request
 * @param request - HTTP request object
 * @returns True if GraphiQL should be rendered (GET request with text/html accept)
 */
function shouldRenderGraphiQL(request: Request): boolean;

Schema Plugin

Plugin for handling schema definition and dynamic schema resolution.

/**
 * Schema plugin for handling GraphQL schema definition
 * @param schemaOrSchemaFactory - Schema or factory function
 * @returns Plugin instance
 */
function useSchema<TServerContext, TUserContext>(
  schemaOrSchemaFactory: YogaSchemaDefinition<TServerContext, TUserContext>
): Plugin<TServerContext & TUserContext & YogaInitialContext, TServerContext, TUserContext>;

Health Check Plugins

Plugins for health and readiness checks.

/**
 * Health check plugin
 * @param options - Health check configuration
 * @returns Plugin instance
 */
function useHealthCheck(options: HealthCheckPluginOptions): Plugin;

interface HealthCheckPluginOptions {
  /** Health check endpoint path */
  endpoint?: string;
  /** Custom health check function */
  healthCheck?: () => Promise<boolean> | boolean;
}

/**
 * Readiness check plugin
 * @param options - Readiness check configuration
 * @returns Plugin instance
 */
function useReadinessCheck(options: ReadinessCheckPluginOptions): Plugin;

interface ReadinessCheckPluginOptions {
  /** Readiness check endpoint path */
  endpoint?: string;
  /** Custom readiness check function */
  readinessCheck?: () => Promise<boolean> | boolean;
  /** Check function that runs periodically */
  check?: () => Promise<boolean> | boolean;
}

Request Processing Plugins

Plugins for request parsing and processing functionality.

/**
 * Request parser plugin
 * @param options - Parser configuration
 * @returns Plugin instance
 */
function useRequestParser(options: RequestParserPluginOptions): Plugin;

interface RequestParserPluginOptions {
  /** Custom request parsers */
  parsers?: RequestParser[];
}

type RequestParser = (request: Request) => Promise<GraphQLParams> | GraphQLParams | null;

/**
 * Execution cancellation plugin
 * @returns Plugin instance
 */
function useExecutionCancellation(): Plugin;

/**
 * Result processors plugin
 * @returns Plugin instance
 */
function useResultProcessors(): Plugin;

Caching Plugins

Plugin for parser and validation caching to improve performance.

/**
 * Parser and validation cache plugin
 * @param options - Cache configuration
 * @returns Plugin instance
 */
function useParserAndValidationCache(options: ParserAndValidationCacheOptions): Plugin;

interface ParserAndValidationCacheOptions {
  /** Maximum cache entries */
  max?: number;
  /** Time to live in milliseconds */
  ttl?: number;
  /** Custom cache implementation */
  cache?: LRUCache<any>;
}

Instrumentation

Plugin instrumentation utilities for monitoring and observability.

/**
 * Instrumentation type for monitoring plugin execution
 */
type Instrumentation<TContext extends Record<string, any>> =
  | ((context: TContext) => void)
  | {
      onPluginInit?: (context: TContext) => void;
      onExecute?: (context: TContext) => void;
      onSubscribe?: (context: TContext) => void;
    };

/**
 * Get instrumentation and plugin utilities
 */
function getInstrumentationAndPlugin(instrumentation: Instrumentation<any>): {
  instrumentation: Instrumentation<any>;
  plugin: Plugin;
};

/**
 * Chain multiple instrumentations
 */
function chain(...instrumentations: Array<Instrumentation<any>>): Instrumentation<any>;

/**
 * Compose instrumentations
 */
function composeInstrumentation(...instrumentations: Array<Instrumentation<any>>): Instrumentation<any>;

Usage Examples:

import { 
  createYoga, 
  useGraphiQL, 
  useHealthCheck, 
  useReadinessCheck,
  useParserAndValidationCache 
} from 'graphql-yoga';

// Basic plugin usage
const yoga = createYoga({
  schema: mySchema,
  plugins: [
    useGraphiQL({
      options: {
        title: 'My GraphQL API',
        defaultQuery: `
          query {
            hello
          }
        `
      }
    }),
    useHealthCheck({
      endpoint: '/health'
    }),
    useParserAndValidationCache({
      max: 1000,
      ttl: 3600000 // 1 hour
    })
  ]
});

// Custom plugin creation
function useCustomAuth(): Plugin {
  return {
    onParams({ params, request, setParams }) {
      const token = request.headers.get('authorization');
      if (!token) {
        throw new Error('Unauthorized');
      }
      
      // Add user to params
      const user = validateToken(token);
      setParams({
        ...params,
        extensions: {
          ...params.extensions,
          user
        }
      });
    }
  };
}

// Plugin with server context
function useLogging<TServerContext>(): Plugin<any, TServerContext, any> {
  return {
    onYogaInit({ yoga }) {
      yoga.logger.info('Server initialized');
    },
    onParams({ params, serverContext }) {
      console.log('Processing operation:', params.operationName);
      console.log('Server context:', serverContext);
    },
    onExecutionResult({ result }) {
      if (result.errors) {
        console.error('Execution errors:', result.errors);
      }
    }
  };
}

// Advanced plugin with dynamic configuration
function useRateLimit(options: { max: number; window: number }): Plugin {
  const requests = new Map<string, number[]>();
  
  return {
    onParams({ request }) {
      const ip = request.headers.get('x-forwarded-for') || 'unknown';
      const now = Date.now();
      const windowStart = now - options.window;
      
      if (!requests.has(ip)) {
        requests.set(ip, []);
      }
      
      const ipRequests = requests.get(ip)!;
      const validRequests = ipRequests.filter(time => time > windowStart);
      
      if (validRequests.length >= options.max) {
        throw new Error('Rate limit exceeded');
      }
      
      validRequests.push(now);
      requests.set(ip, validRequests);
    }
  };
}

// Using plugins with server
const advancedYoga = createYoga({
  schema: mySchema,
  plugins: [
    useCustomAuth(),
    useLogging(),
    useRateLimit({ max: 100, window: 60000 }), // 100 requests per minute
    useGraphiQL(),
    useHealthCheck(),
    useReadinessCheck({
      check: async () => {
        // Check database connection, external services, etc.
        return await checkDatabaseConnection();
      }
    })
  ]
});