or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

ast-operations.mddirectives.mdexecution-resolution.mdfield-type-operations.mdindex.mdschema-transformation.mdutilities.md
tile.json

execution-resolution.mddocs/

Execution & Resolution

Execution framework types, resolver interfaces, and validation utilities for GraphQL execution and resolution. These components provide the foundation for GraphQL request processing, resolver management, and execution customization.

Capabilities

Execution Framework

Core types and utilities for GraphQL execution requests and results.

/**
 * GraphQL execution request structure with full type safety
 */
interface ExecutionRequest<TVariables = any, TContext = any, TRootValue = any, TExtensions = any> {
  /** The GraphQL document to execute */
  document: DocumentNode;
  /** Variable values for the request */
  variables?: TVariables;
  /** Operation name to execute (for multi-operation documents) */
  operationName?: string;
  /** Root value passed to root resolvers */
  rootValue?: TRootValue;
  /** Context object shared across all resolvers */
  context?: TContext;
  /** Extensions object for additional metadata */
  extensions?: TExtensions;
}

/**
 * GraphQL execution result structure with full type safety
 */
interface ExecutionResult<TData = any, TExtensions = any> {
  /** The execution result data */
  data?: TData | null;
  /** Array of execution errors */
  errors?: ReadonlyArray<GraphQLError>;
  /** Extensions returned by the execution */
  extensions?: TExtensions;
}

/**
 * Function type for executing GraphQL requests
 */
type Executor<TBaseContext = any, TBaseExtensions = any> = <
  TReturn = any,
  TArgs = any,
  TContext = TBaseContext,
  TRoot = any,
  TExtensions = TBaseExtensions
>(
  request: ExecutionRequest<TArgs, TContext, TRoot, TExtensions>
) => PromiseOrValue<ExecutionResult<TReturn>>;

/**
 * Normalize an execution request to ensure consistent format
 * @param request - The request to normalize
 * @returns Normalized execution request
 */
function normalizeExecutionRequest<T extends ExecutionRequest>(request: T): T;

Usage Examples:

import { ExecutionRequest, ExecutionResult, Executor, normalizeExecutionRequest } from "@graphql-tools/utils";
import { execute, parse } from "graphql";

// Create a typed execution request
const request: ExecutionRequest<{ id: string }, { user: User }> = {
  document: parse(`
    query GetUser($id: ID!) {
      user(id: $id) {
        name
        email
      }
    }
  `),
  variables: { id: "123" },
  context: { user: currentUser },
  operationName: "GetUser"
};

// Normalize the request
const normalizedRequest = normalizeExecutionRequest(request);

// Create a custom executor
const myExecutor: Executor<{ user: User }> = async (request) => {
  console.log('Executing operation:', request.operationName);
  return execute({
    schema: mySchema,
    document: request.document,
    variableValues: request.variables,
    contextValue: request.context,
    rootValue: request.rootValue,
    operationName: request.operationName
  });
};

// Execute the request
const result = await myExecutor(request);
if (result.errors) {
  console.error('Execution errors:', result.errors);
} else {
  console.log('Result:', result.data);
}

Resolver Interfaces

Comprehensive resolver type definitions and interfaces for GraphQL schema resolution.

/**
 * Main resolver interface for GraphQL schemas
 */
interface IResolvers<TSource = any, TContext = any, TArgs = any> {
  [typeName: string]: 
    | IResolverObject<TSource, TContext, TArgs>
    | GraphQLScalarType
    | IEnumResolver
    | IResolverOptions<TSource, TContext, TArgs>;
}

/**
 * Resolver object for a specific GraphQL type
 */
interface IResolverObject<TSource = any, TContext = any, TArgs = any> {
  [fieldName: string]: 
    | IFieldResolver<TSource, TContext, TArgs>
    | IResolverOptions<TSource, TContext, TArgs>;
}

/**
 * Individual field resolver function
 */
type IFieldResolver<TSource = any, TContext = any, TArgs = any, TReturn = any> = (
  source: TSource,
  args: TArgs,
  context: TContext,
  info: GraphQLResolveInfo & { signal?: AbortSignal }
) => TReturn;

/**
 * Resolver options for advanced resolver configuration
 */
interface IResolverOptions<TSource = any, TContext = any, TArgs = any> {
  fragment?: string;
  resolve?: IFieldResolver<TSource, TContext, TArgs>;
  subscribe?: IFieldResolver<TSource, TContext, TArgs>;
  __resolveType?: GraphQLTypeResolver<TSource, TContext>;
  __isTypeOf?: GraphQLIsTypeOfFn<TSource, TContext>;
}

/**
 * Enum resolver for GraphQL enum types
 */
interface IEnumResolver {
  [enumValue: string]: string | number;
}

/**
 * Field resolver options passed to resolvers
 */
interface IFieldResolverOptions<TSource = any, TContext = any> {
  fragment: string;
  operation: OperationDefinitionNode;
  schema: GraphQLSchema;
  fragments: Record<string, FragmentDefinitionNode>;
  rootValue?: any;
  context: TContext;
  variableValues: Record<string, any>;
  fieldName: string;
  fieldNodes: ReadonlyArray<FieldNode>;
  returnType: GraphQLOutputType;
  parentType: GraphQLObjectType;
  path: ResponsePath;
  source: TSource;
  args: Record<string, any>;
}

/**
 * Function type for next resolver in resolver chain
 */
type NextResolverFn = () => Promise<any>;

Usage Examples:

import { IResolvers, IFieldResolver, IResolverOptions } from "@graphql-tools/utils";

// Define resolvers with full type safety
const resolvers: IResolvers<any, { user: User, db: Database }> = {
  Query: {
    user: async (source, args: { id: string }, context, info) => {
      return context.db.findUser(args.id);
    },
    posts: async (source, args, context, info) => {
      // Check for abort signal
      if (info.signal?.aborted) {
        throw new Error('Request was aborted');
      }
      return context.db.findPosts();
    }
  },
  
  User: {
    posts: async (user, args, context, info) => {
      return context.db.findPostsByUser(user.id);
    }
  },
  
  // Enum resolver
  Role: {
    ADMIN: 'admin',
    USER: 'user'
  },
  
  // Custom scalar
  DateTime: new GraphQLScalarType({
    name: 'DateTime',
    serialize: (value) => value.toISOString(),
    parseValue: (value) => new Date(value),
    parseLiteral: (ast) => new Date(ast.value)
  })
};

// Advanced resolver with options
const userResolver: IResolverOptions<User, Context> = {
  fragment: '... on User { id }',
  resolve: async (source, args, context, info) => {
    console.log('Resolving user with fragment');
    return context.db.findUser(source.id);
  }
};

Resolver Extraction

Extract resolver functions from existing GraphQL schemas.

/**
 * Extract all resolvers from a GraphQL schema
 * @param schema - The schema to extract resolvers from
 * @returns Resolver object containing all extracted resolvers
 */
function getResolversFromSchema(schema: GraphQLSchema): IResolvers;

Usage Examples:

import { getResolversFromSchema } from "@graphql-tools/utils";

// Extract resolvers from existing schema
const existingResolvers = getResolversFromSchema(schema);

// Merge with additional resolvers
const combinedResolvers = {
  ...existingResolvers,
  Query: {
    ...existingResolvers.Query,
    newField: () => 'Hello World'
  }
};

// Use extracted resolvers as a base for schema transformation
const transformedResolvers = Object.fromEntries(
  Object.entries(existingResolvers).map(([typeName, typeResolvers]) => [
    typeName,
    typeof typeResolvers === 'object' ? 
      Object.fromEntries(
        Object.entries(typeResolvers).map(([fieldName, resolver]) => [
          fieldName,
          async (...args) => {
            console.log(`Calling ${typeName}.${fieldName}`);
            return typeof resolver === 'function' ? resolver(...args) : resolver;
          }
        ])
      ) : typeResolvers
  ])
);

Validation Options

Configuration interfaces for resolver validation and schema options.

/**
 * Options for resolver validation
 */
interface IResolverValidationOptions {
  /** Require resolvers for all schema fields */
  requireResolversForResolveType?: boolean;
  /** Require resolvers for all non-scalar fields */
  requireResolversForNonScalar?: boolean;
  /** Require resolvers for all fields */
  requireResolversForAllFields?: boolean;
  /** Require resolvers for arguments */
  requireResolversForArgs?: boolean;
  /** Allow resolvers not present in schema */
  allowResolversNotInSchema?: boolean;
}

/**
 * Options for adding resolvers to a schema
 */
interface IAddResolversToSchemaOptions {
  /** The base schema to add resolvers to */
  schema: GraphQLSchema;
  /** Resolvers to add */
  resolvers: IResolvers;
  /** Default field resolver */
  defaultFieldResolver?: IFieldResolver;
  /** Resolver validation options */
  resolverValidationOptions?: IResolverValidationOptions;
  /** Whether to inherit resolvers from base schema */
  inheritResolversFromInterfaces?: boolean;
  /** Update existing resolvers if they exist */
  updateResolversInPlace?: boolean;
}

Usage Examples:

import { 
  IResolverValidationOptions, 
  IAddResolversToSchemaOptions,
  addResolversToSchema 
} from "@graphql-tools/utils";

// Configure strict resolver validation
const strictValidation: IResolverValidationOptions = {
  requireResolversForResolveType: true,
  requireResolversForNonScalar: true,
  requireResolversForAllFields: false,
  allowResolversNotInSchema: false
};

// Add resolvers with validation options
const schemaOptions: IAddResolversToSchemaOptions = {
  schema: baseSchema,
  resolvers: myResolvers,
  resolverValidationOptions: strictValidation,
  inheritResolversFromInterfaces: true,
  updateResolversInPlace: false,
  defaultFieldResolver: (source, args, context, info) => {
    console.log(`Default resolver for ${info.fieldName}`);
    return source[info.fieldName];
  }
};

const schemaWithResolvers = addResolversToSchema(schemaOptions);

Extended GraphQL Info

Enhanced GraphQL resolve info with additional utilities and abort signal support.

/**
 * Extended GraphQL resolve info with abort signal and additional utilities
 */
interface ExtendedGraphQLResolveInfo extends GraphQLResolveInfo {
  /** Abort signal for request cancellation */
  signal?: AbortSignal;
  /** Merged info from parent resolvers */
  mergeInfo?: MergeInfo;
}

/**
 * Merge info for schema stitching and federation
 */
interface MergeInfo {
  delegate: (
    operation: 'query' | 'mutation' | 'subscription',
    fieldName: string,
    args: Record<string, any>,
    context: any,
    info: GraphQLResolveInfo,
    transforms?: Transform[]
  ) => any;
}

/**
 * Get response key from GraphQL resolve info
 * @param info - GraphQL resolve info
 * @returns Response key for the current field
 */
function getResponseKeyFromInfo(info: GraphQLResolveInfo): string;

Usage Examples:

import { ExtendedGraphQLResolveInfo, getResponseKeyFromInfo } from "@graphql-tools/utils";

const resolverWithAbortSupport = async (
  source: any,
  args: any,
  context: any,
  info: ExtendedGraphQLResolveInfo
) => {
  // Check for abort signal
  if (info.signal?.aborted) {
    throw new Error('Request was aborted');
  }
  
  // Set up abort listener
  const abortPromise = new Promise((_, reject) => {
    info.signal?.addEventListener('abort', () => {
      reject(new Error('Request was aborted'));
    });
  });
  
  // Race between data fetching and abort
  const dataPromise = fetchUserData(args.id);
  
  try {
    const result = await Promise.race([dataPromise, abortPromise]);
    return result;
  } catch (error) {
    if (error.message === 'Request was aborted') {
      console.log('Request aborted, cleaning up resources');
    }
    throw error;
  }
};

// Use response key for caching
const resolverWithCaching = (source, args, context, info) => {
  const responseKey = getResponseKeyFromInfo(info);
  
  // Check cache
  const cached = context.cache.get(responseKey);
  if (cached) {
    return cached;
  }
  
  // Fetch and cache result
  const result = fetchData(args);
  context.cache.set(responseKey, result);
  return result;
};