Fully-featured GraphQL Server with focus on easy setup, performance & great developer experience
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Flexible result processing system supporting different response formats including regular JSON, Server-Sent Events for subscriptions, and multipart responses for file uploads with content negotiation.
Main plugin for handling result processing with different output formats.
/**
* Result processors plugin for handling different response formats
* @returns Plugin instance
*/
function useResultProcessors(): Plugin;Core types and interfaces for result processing functionality.
/**
* Result processor function type
*/
type ResultProcessor = (
executionResult: ExecutionResultWithSerializer,
fetchAPI: FetchAPI
) => Response | Promise<Response>;
/**
* Input types for result processors
*/
type ResultProcessorInput =
| ExecutionResult
| AsyncIterable<ExecutionResult>
| Response;
/**
* Execution result with serializer support
*/
type ExecutionResultWithSerializer<TData = any, TExtensions = any> = ExecutionResult<TData, TExtensions> & {
stringify?: (result: ExecutionResult) => string;
};Functions for processing standard JSON GraphQL responses.
/**
* Process regular GraphQL execution results as JSON responses
* @param input - Execution result to process
* @param fetchAPI - Fetch API implementation
* @returns HTTP response with JSON content
*/
function processRegularResult(
input: ResultProcessorInput,
fetchAPI: FetchAPI
): Response;Functions for processing subscription results as Server-Sent Events.
/**
* Get Server-Sent Events result processor for subscriptions
* @returns Result processor for SSE format
*/
function getSSEProcessor(): ResultProcessor;Functions for processing multipart responses with file attachments.
/**
* Process multipart results for file uploads and complex responses
* @param result - Result to process as multipart
* @param fetchAPI - Fetch API implementation
* @returns HTTP response with multipart content
*/
function processMultipartResult(
result: ResultProcessorInput,
fetchAPI: FetchAPI
): Response;Utility functions for serializing execution results to JSON strings.
/**
* Stringify GraphQL execution result without internal properties
* @param result - Execution result to stringify
* @returns JSON string without internal properties
*/
function jsonStringifyResultWithoutInternals(result: ExecutionResult): string;
/**
* Remove internal properties from result errors
* @param result - Execution result to clean
* @returns Execution result with cleaned errors
*/
function omitInternalsFromResultErrors(result: ExecutionResult): ExecutionResult;Functions for handling Accept header content negotiation.
/**
* Get media types from request Accept header in priority order
* @param request - HTTP request object
* @returns Array of media types in preference order
*/
function getMediaTypesForRequestInOrder(request: Request): string[];
/**
* Check if requested media type matches processor media type
* @param askedMediaType - Media type requested by client
* @param processorMediaType - Media type supported by processor
* @returns True if media types are compatible
*/
function isMatchingMediaType(
askedMediaType: string,
processorMediaType: string
): boolean;Hook types for customizing result processing behavior.
/**
* Result processing hook type
*/
type OnResultProcess<TServerContext> = (
payload: OnResultProcessEventPayload<TServerContext>
) => void | Promise<void>;
/**
* Result processing event payload
*/
interface OnResultProcessEventPayload<TServerContext> {
/** Result to be processed */
result: ResultProcessorInput;
/** Function to modify the result */
setResult: (result: ResultProcessorInput) => void;
/** Server context */
serverContext?: TServerContext;
/** Original HTTP request */
request: Request;
/** Fetch API implementation */
fetchAPI: FetchAPI;
}
/**
* Execution result hook type
*/
type OnExecutionResultHook<TServerContext> = (
payload: OnExecutionResultEventPayload<TServerContext>
) => void | Promise<void>;
/**
* Execution result event payload
*/
interface OnExecutionResultEventPayload<TServerContext> {
/** Execution result from GraphQL engine */
result: ExecutionResult;
/** Function to modify the result */
setResult: (result: ExecutionResult | AsyncIterable<ExecutionResult>) => void;
/** Server context */
serverContext?: TServerContext;
}Usage Examples:
import {
createYoga,
useResultProcessors,
processRegularResult,
getSSEProcessor,
processMultipartResult,
jsonStringifyResultWithoutInternals
} from 'graphql-yoga';
// Basic result processing setup
const yoga = createYoga({
schema: mySchema,
plugins: [
useResultProcessors()
]
});
// Custom result processor for CSV format
function createCSVProcessor(): ResultProcessor {
return (executionResult, fetchAPI) => {
if (executionResult.data && Array.isArray(executionResult.data.items)) {
const csv = convertToCSV(executionResult.data.items);
return new fetchAPI.Response(csv, {
headers: {
'Content-Type': 'text/csv',
'Content-Disposition': 'attachment; filename="data.csv"'
}
});
}
// Fall back to regular processing
return processRegularResult(executionResult, fetchAPI);
};
}
// Server with custom result processing
const customResultYoga = createYoga({
schema: mySchema,
plugins: [
{
onResultProcess({ result, setResult, request, fetchAPI }) {
const acceptHeader = request.headers.get('accept');
if (acceptHeader?.includes('text/csv')) {
const processor = createCSVProcessor();
const response = processor(result as ExecutionResult, fetchAPI);
setResult(response);
}
}
},
useResultProcessors()
]
});
// Subscription handling with SSE
const subscriptionYoga = createYoga({
schema: subscriptionSchema,
plugins: [
{
onResultProcess({ result, setResult, request, fetchAPI }) {
const acceptHeader = request.headers.get('accept');
if (acceptHeader?.includes('text/event-stream')) {
const processor = getSSEProcessor();
const response = processor(result as ExecutionResult, fetchAPI);
setResult(response);
}
}
},
useResultProcessors()
]
});
// File upload with multipart responses
const uploadYoga = createYoga({
schema: uploadSchema,
multipart: true,
plugins: [
{
onResultProcess({ result, setResult, request, fetchAPI }) {
// Check if result contains file data
if (hasFileData(result)) {
const response = processMultipartResult(result, fetchAPI);
setResult(response);
}
}
},
useResultProcessors()
]
});
// Custom result transformation
const transformYoga = createYoga({
schema: mySchema,
plugins: [
{
onExecutionResult({ result, setResult }) {
// Add metadata to all responses
const transformedResult = {
...result,
extensions: {
...result.extensions,
timestamp: new Date().toISOString(),
version: '1.0.0'
}
};
setResult(transformedResult);
},
onResultProcess({ result, setResult, request }) {
// Custom serialization
if (typeof result === 'object' && 'data' in result) {
const cleanResult = omitInternalsFromResultErrors(result);
const jsonString = jsonStringifyResultWithoutInternals(cleanResult);
const response = new Response(jsonString, {
headers: {
'Content-Type': 'application/json',
'X-Request-ID': request.headers.get('x-request-id') || 'unknown'
}
});
setResult(response);
}
}
},
useResultProcessors()
]
});
// Content negotiation example
function createContentNegotiationPlugin(): Plugin {
return {
onResultProcess({ result, setResult, request, fetchAPI }) {
const mediaTypes = getMediaTypesForRequestInOrder(request);
for (const mediaType of mediaTypes) {
if (isMatchingMediaType(mediaType, 'application/json')) {
const response = processRegularResult(result, fetchAPI);
setResult(response);
return;
}
if (isMatchingMediaType(mediaType, 'text/event-stream')) {
const processor = getSSEProcessor();
const response = processor(result as ExecutionResult, fetchAPI);
setResult(response);
return;
}
if (isMatchingMediaType(mediaType, 'multipart/mixed')) {
const response = processMultipartResult(result, fetchAPI);
setResult(response);
return;
}
}
// Default to JSON
const response = processRegularResult(result, fetchAPI);
setResult(response);
}
};
}
// Error handling in result processing
const errorHandlingYoga = createYoga({
schema: mySchema,
plugins: [
{
onExecutionResult({ result, setResult }) {
if (result.errors) {
// Log errors
console.error('GraphQL execution errors:', result.errors);
// Transform errors for client
const transformedResult = {
...result,
errors: result.errors.map(error => ({
message: error.message,
locations: error.locations,
path: error.path,
// Remove sensitive error details
extensions: {
code: error.extensions?.code || 'INTERNAL_ERROR'
}
}))
};
setResult(transformedResult);
}
}
},
useResultProcessors()
]
});