CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-graphql-yoga

Fully-featured GraphQL Server with focus on easy setup, performance & great developer experience

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

result-processing.mddocs/

Result Processing

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.

Capabilities

Result Processors Plugin

Main plugin for handling result processing with different output formats.

/**
 * Result processors plugin for handling different response formats
 * @returns Plugin instance
 */
function useResultProcessors(): Plugin;

Result Processor Types

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;
};

Regular Result Processing

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;

Server-Sent Events Processing

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;

Multipart Result Processing

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;

Result Stringification

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;

Content Negotiation

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;

Result Processing Hook Types

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()
  ]
});

docs

caching-performance.md

context-types.md

error-handling.md

index.md

logging-system.md

plugin-system.md

request-processing.md

result-processing.md

schema-management.md

server-configuration.md

subscription-system.md

tile.json