Fully-featured GraphQL Server with focus on easy setup, performance & great developer experience
Comprehensive plugin system extending Envelop with Yoga-specific hooks for request processing, result handling, and server lifecycle management. Enables extensible GraphQL server functionality through composable plugins.
Main plugin interface that extends Envelop plugins with Yoga-specific functionality.
/**
* Yoga plugin interface extending Envelop and server adapter plugins
*/
interface Plugin<
PluginContext extends Record<string, any> = {},
TServerContext extends Record<string, any> = {},
TUserContext = {}
> extends EnvelopPlugin<YogaInitialContext & PluginContext>, ServerAdapterPlugin<TServerContext> {
/** Called when Yoga server initializes */
onYogaInit?: OnYogaInitHook<TServerContext>;
/** Called during HTTP request parsing */
onRequestParse?: OnRequestParseHook<TServerContext>;
/** Called before GraphQL execution with parameters */
onParams?: OnParamsHook;
/** Called during result processing */
onResultProcess?: OnResultProcess;
/** Called before GraphQL execution */
onExecute?: OnExecuteHook<YogaInitialContext & PluginContext & TUserContext>;
/** Called before GraphQL subscription */
onSubscribe?: OnSubscribeHook<YogaInitialContext & PluginContext & TUserContext>;
}Usage Examples:
import { Plugin } from "graphql-yoga";
// Basic plugin
const myPlugin: Plugin = {
onYogaInit({ yoga }) {
console.log(`Yoga server initialized with endpoint: ${yoga.graphqlEndpoint}`);
},
onRequestParse({ request, url }) {
console.log(`Processing request to: ${url.pathname}`);
}
};
// Plugin with context typing
const contextPlugin: Plugin<{ startTime: number }> = {
onParams({ setParams, params }) {
setParams({
...params,
extensions: {
...params.extensions,
startTime: Date.now()
}
});
}
};
// Use plugins
const yoga = createYoga({
plugins: [myPlugin, contextPlugin]
});Hooks that are unique to Yoga for server lifecycle and request processing.
/**
* Hook called when Yoga server initializes
*/
type OnYogaInitHook<TServerContext extends Record<string, any>> = (
payload: OnYogaInitEventPayload<TServerContext>
) => void;
interface OnYogaInitEventPayload<TServerContext extends Record<string, any>> {
yoga: YogaServer<TServerContext, any>;
}
/**
* Hook called during HTTP request parsing
*/
type OnRequestParseHook<TServerContext> = (
payload: OnRequestParseEventPayload<TServerContext>
) => PromiseOrValue<void | OnRequestParseHookResult>;
interface OnRequestParseEventPayload<TServerContext> {
request: Request;
url: URL;
requestParser: RequestParser | undefined;
serverContext: TServerContext;
setRequestParser: (parser: RequestParser) => void;
}
type OnRequestParseHookResult = {
onRequestParseDone?: OnRequestParseDoneHook;
};Usage Examples:
// Server initialization hook
const initPlugin: Plugin = {
onYogaInit({ yoga }) {
// Access server instance
console.log(`Server ID: ${yoga.id}`);
console.log(`GraphQL endpoint: ${yoga.graphqlEndpoint}`);
// Set up server-wide resources
setupMetrics(yoga);
}
};
// Request parsing hook
const parsePlugin: Plugin = {
onRequestParse({ request, url, setRequestParser }) {
// Custom request parser for specific content types
if (request.headers.get('content-type')?.includes('application/custom')) {
setRequestParser(async (req) => {
const body = await req.text();
return parseCustomFormat(body);
});
}
}
};Hooks for intercepting and modifying GraphQL parameters and execution results.
/**
* Hook called before GraphQL execution with parameters
*/
type OnParamsHook = (payload: OnParamsEventPayload) => PromiseOrValue<void>;
interface OnParamsEventPayload {
params: GraphQLParams;
request: Request;
setParams: (params: GraphQLParams) => void;
setResult: (result: ExecutionResult) => void;
fetchAPI: FetchAPI;
}
/**
* Hook called during result processing
*/
type OnResultProcess = (payload: OnResultProcessEventPayload) => PromiseOrValue<void>;
interface OnResultProcessEventPayload {
request: Request;
result: ResultProcessorInput;
setResult: (result: ResultProcessorInput) => void;
resultProcessor?: ResultProcessor;
acceptableMediaTypes: string[];
setResultProcessor: (resultProcessor: ResultProcessor, acceptedMediaType: string) => void;
}Usage Examples:
// Parameter modification plugin
const paramsPlugin: Plugin = {
onParams({ params, setParams, request }) {
// Add request metadata to extensions
setParams({
...params,
extensions: {
...params.extensions,
requestId: request.headers.get('x-request-id'),
timestamp: Date.now()
}
});
}
};
// Result processing plugin
const resultPlugin: Plugin = {
onResultProcess({ result, setResult, request }) {
// Add execution metadata to result
if ('data' in result || 'errors' in result) {
setResult({
...result,
extensions: {
...result.extensions,
executedAt: new Date().toISOString(),
requestId: request.headers.get('x-request-id')
}
});
}
}
};Types for custom request parsers that extract GraphQL parameters from HTTP requests.
/**
* Function type for parsing HTTP requests to GraphQL parameters
*/
type RequestParser = (
request: Request
) => PromiseOrValue<GraphQLParams> | PromiseOrValue<GraphQLParams[]>;
/**
* Hook called after request parsing is done
*/
type OnRequestParseDoneHook = (
payload: OnRequestParseDoneEventPayload
) => PromiseOrValue<void>;
interface OnRequestParseDoneEventPayload {
requestParserResult: GraphQLParams | GraphQLParams[];
setRequestParserResult: (params: GraphQLParams | GraphQLParams[]) => void;
}Usage Examples:
// Custom request parser
const customParser: RequestParser = async (request) => {
const contentType = request.headers.get('content-type');
if (contentType?.includes('application/graphql')) {
// Parse GraphQL query directly from body
const query = await request.text();
return { query };
}
if (contentType?.includes('application/json')) {
const body = await request.json();
return body;
}
throw new Error('Unsupported content type');
};
// Plugin using custom parser
const customParserPlugin: Plugin = {
onRequestParse({ request, setRequestParser }) {
if (request.url.includes('/custom-graphql')) {
setRequestParser(customParser);
}
}
};Types for custom result processors that format GraphQL execution results into HTTP responses.
/**
* Function type for processing GraphQL results into HTTP responses
*/
type ResultProcessor = (
result: ResultProcessorInput,
fetchAPI: FetchAPI,
acceptedMediaType: string
) => PromiseOrValue<Response>;
/**
* GraphQL execution result with optional serialization
*/
type ExecutionResultWithSerializer<TData = any, TExtensions = any> = ExecutionResult<
TData,
TExtensions
> & {
stringify?: (result: ExecutionResult<TData, TExtensions>) => string;
};
/**
* Input type for result processors
*/
type ResultProcessorInput =
| MaybeArray<ExecutionResultWithSerializer>
| AsyncIterable<ExecutionResultWithSerializer<any, { http?: GraphQLHTTPExtensions }>>;Usage Examples:
// Custom result processor
const xmlProcessor: ResultProcessor = (result, fetchAPI, acceptedMediaType) => {
if (acceptedMediaType.includes('application/xml')) {
const xmlResult = convertToXML(result);
return new fetchAPI.Response(xmlResult, {
headers: { 'Content-Type': 'application/xml' }
});
}
throw new Error('Unsupported media type');
};
// Plugin using custom result processor
const xmlPlugin: Plugin = {
onResultProcess({ request, setResultProcessor }) {
const acceptHeader = request.headers.get('accept');
if (acceptHeader?.includes('application/xml')) {
setResultProcessor(xmlProcessor, 'application/xml');
}
}
};Patterns for composing and organizing multiple plugins.
/**
* Plugin array type for server configuration
*/
type PluginArray = Array<
Plugin<any> | Plugin | {}
>;Usage Examples:
// Plugin composition
const authPlugin: Plugin = {
onParams({ params, request, setResult }) {
const token = request.headers.get('authorization');
if (!token) {
setResult({
errors: [new GraphQLError('Authentication required')]
});
}
}
};
const metricsPlugin: Plugin = {
onYogaInit({ yoga }) {
setupMetrics(yoga);
},
onParams({ params }) {
incrementOperationCount(params.operationName);
}
};
const loggingPlugin: Plugin = {
onParams({ params, request }) {
console.log(`GraphQL operation: ${params.operationName}`);
},
onResultProcess({ result }) {
console.log(`Result processed: ${JSON.stringify(result)}`);
}
};
// Use multiple plugins
const yoga = createYoga({
plugins: [
authPlugin,
metricsPlugin,
loggingPlugin,
// Third-party plugins
useDepthLimit(10),
useDisableIntrospection()
]
});tessl i tessl/npm-graphql-yoga@4.0.0