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

schema-transformation.mddocs/

Schema Transformation

Core schema transformation utilities for filtering, mapping, healing, and modifying GraphQL schemas. These tools are essential for schema stitching, federation, schema evolution, and creating custom schema processing pipelines.

Capabilities

Schema Mapping

Transform GraphQL schemas using mapper functions for different schema elements.

/**
 * Transform a GraphQL schema using provided mapper functions
 * @param schema - The schema to transform
 * @param schemaMapper - Object containing mapper functions for different schema elements
 * @returns Transformed schema
 */
function mapSchema(schema: GraphQLSchema, schemaMapper: SchemaMapper): GraphQLSchema;

interface SchemaMapper {
  [MapperKind.TYPE]?: TypeMapper;
  [MapperKind.SCALAR_TYPE]?: ScalarTypeMapper;
  [MapperKind.ENUM_TYPE]?: EnumTypeMapper;
  [MapperKind.COMPOSITE_TYPE]?: CompositeTypeMapper;
  [MapperKind.OBJECT_TYPE]?: ObjectTypeMapper;
  [MapperKind.INPUT_OBJECT_TYPE]?: InputObjectTypeMapper;
  [MapperKind.ABSTRACT_TYPE]?: AbstractTypeMapper;
  [MapperKind.UNION_TYPE]?: UnionTypeMapper;
  [MapperKind.INTERFACE_TYPE]?: InterfaceTypeMapper;
  [MapperKind.ROOT_OBJECT]?: ObjectTypeMapper;
  [MapperKind.QUERY]?: ObjectTypeMapper;
  [MapperKind.MUTATION]?: ObjectTypeMapper;
  [MapperKind.SUBSCRIPTION]?: ObjectTypeMapper;
  [MapperKind.OBJECT_FIELD]?: FieldMapper;
  [MapperKind.ROOT_FIELD]?: FieldMapper;
  [MapperKind.QUERY_ROOT_FIELD]?: FieldMapper;
  [MapperKind.MUTATION_ROOT_FIELD]?: FieldMapper;
  [MapperKind.SUBSCRIPTION_ROOT_FIELD]?: FieldMapper;
  [MapperKind.INTERFACE_FIELD]?: FieldMapper;
  [MapperKind.COMPOSITE_FIELD]?: FieldMapper;
  [MapperKind.ARGUMENT]?: ArgumentMapper;
  [MapperKind.INPUT_OBJECT_FIELD]?: InputFieldMapper;
  [MapperKind.DIRECTIVE]?: DirectiveMapper;
  [MapperKind.ENUM_VALUE]?: EnumValueMapper;
}

enum MapperKind {
  TYPE = 'MapperKind.TYPE',
  SCALAR_TYPE = 'MapperKind.SCALAR_TYPE',
  ENUM_TYPE = 'MapperKind.ENUM_TYPE',
  COMPOSITE_TYPE = 'MapperKind.COMPOSITE_TYPE',
  OBJECT_TYPE = 'MapperKind.OBJECT_TYPE',
  INPUT_OBJECT_TYPE = 'MapperKind.INPUT_OBJECT_TYPE',
  ABSTRACT_TYPE = 'MapperKind.ABSTRACT_TYPE',
  UNION_TYPE = 'MapperKind.UNION_TYPE',
  INTERFACE_TYPE = 'MapperKind.INTERFACE_TYPE',
  ROOT_OBJECT = 'MapperKind.ROOT_OBJECT',
  QUERY = 'MapperKind.QUERY',
  MUTATION = 'MapperKind.MUTATION',
  SUBSCRIPTION = 'MapperKind.SUBSCRIPTION',
  OBJECT_FIELD = 'MapperKind.OBJECT_FIELD',
  ROOT_FIELD = 'MapperKind.ROOT_FIELD',
  QUERY_ROOT_FIELD = 'MapperKind.QUERY_ROOT_FIELD',
  MUTATION_ROOT_FIELD = 'MapperKind.MUTATION_ROOT_FIELD',
  SUBSCRIPTION_ROOT_FIELD = 'MapperKind.SUBSCRIPTION_ROOT_FIELD',
  INTERFACE_FIELD = 'MapperKind.INTERFACE_FIELD',
  COMPOSITE_FIELD = 'MapperKind.COMPOSITE_FIELD',
  ARGUMENT = 'MapperKind.ARGUMENT',
  INPUT_OBJECT_FIELD = 'MapperKind.INPUT_OBJECT_FIELD',
  DIRECTIVE = 'MapperKind.DIRECTIVE',
  ENUM_VALUE = 'MapperKind.ENUM_VALUE'
}

type FieldMapper = (
  fieldConfig: GraphQLFieldConfig<any, any>,
  fieldName: string,
  typeName: string,
  schema: GraphQLSchema
) => GraphQLFieldConfig<any, any> | [string, GraphQLFieldConfig<any, any>] | null | undefined;

type ObjectTypeMapper = (
  type: GraphQLObjectType,
  schema: GraphQLSchema
) => GraphQLObjectType | null | undefined;

type ArgumentMapper = (
  argumentConfig: GraphQLArgumentConfig,
  fieldName: string,
  typeName: string,
  schema: GraphQLSchema
) => GraphQLArgumentConfig | [string, GraphQLArgumentConfig] | null | undefined;

Usage Examples:

import { mapSchema, MapperKind } from "@graphql-tools/utils";

// Add logging to all field resolvers
const schemaWithLogging = mapSchema(schema, {
  [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
    const originalResolve = fieldConfig.resolve;
    fieldConfig.resolve = async (source, args, context, info) => {
      console.log(`Resolving ${typeName}.${fieldName}`);
      return originalResolve ? originalResolve(source, args, context, info) : source[fieldName];
    };
    return fieldConfig;
  }
});

// Rename all fields to camelCase
const camelCaseSchema = mapSchema(schema, {
  [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName) => {
    const camelFieldName = fieldName.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
    return [camelFieldName, fieldConfig];
  }
});

Schema Filtering

Filter GraphQL schemas to remove unwanted types, fields, or other elements.

/**
 * Filter a GraphQL schema based on provided filter functions
 * @param options - Filtering options and filter functions
 * @returns Filtered schema
 */
function filterSchema(options: FilterSchemaOptions): GraphQLSchema;

interface FilterSchemaOptions {
  schema: GraphQLSchema;
  typeFilter?: FilterTypes;
  fieldFilter?: FilterObjectFields;
  rootFieldFilter?: FilterRootFields;
  objectFieldFilter?: FilterObjectFields;
  interfaceFieldFilter?: FilterInterfaceFields;
  inputFieldFilter?: FilterInputObjectFields;
  argumentFilter?: FilterArguments;
  directiveFilter?: FilterDirectives;
}

type FilterTypes = (typeName: string, type: GraphQLNamedType) => boolean;
type FilterObjectFields = (typeName: string, fieldName: string, fieldConfig: GraphQLFieldConfig<any, any>) => boolean;
type FilterRootFields = (operation: 'Query' | 'Mutation' | 'Subscription', fieldName: string, fieldConfig: GraphQLFieldConfig<any, any>) => boolean;
type FilterInterfaceFields = (typeName: string, fieldName: string, fieldConfig: GraphQLFieldConfig<any, any>) => boolean;
type FilterInputObjectFields = (typeName: string, fieldName: string, inputFieldConfig: GraphQLInputFieldConfig) => boolean;
type FilterArguments = (typeName: string, fieldName: string, argName: string, argConfig: GraphQLArgumentConfig) => boolean;
type FilterDirectives = (directiveName: string, directive: GraphQLDirective) => boolean;

Usage Examples:

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

// Remove deprecated fields and types
const cleanSchema = filterSchema({
  schema: originalSchema,
  fieldFilter: (typeName, fieldName, fieldConfig) => {
    return !fieldConfig.deprecationReason;
  },
  typeFilter: (typeName, type) => {
    return !type.description?.includes('@deprecated');
  }
});

// Keep only specific root fields
const publicSchema = filterSchema({
  schema: originalSchema,
  rootFieldFilter: (operation, fieldName) => {
    const publicFields = ['user', 'posts', 'comments'];
    return publicFields.includes(fieldName);
  }
});

Schema Healing

Heal GraphQL schemas by restoring proper type references and fixing inconsistencies.

/**
 * Heal a GraphQL schema by restoring proper type references and fixing inconsistencies
 * @param schema - The schema to heal
 * @returns Healed schema with restored references
 */
function healSchema(schema: GraphQLSchema): GraphQLSchema;

Usage Examples:

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

// Heal schema after manual modifications
const modifiedSchema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      // ... manually created fields that might have broken references
    }
  })
});

const healedSchema = healSchema(modifiedSchema);

Schema Pruning

Remove unused types and fields from GraphQL schemas to optimize schema size.

/**
 * Remove unused types and fields from a GraphQL schema
 * @param schema - The schema to prune
 * @param options - Pruning options
 * @returns Pruned schema with unused elements removed
 */
function pruneSchema(schema: GraphQLSchema, options?: PruneSchemaOptions): GraphQLSchema;

interface PruneSchemaOptions {
  skipPruning?: PruneSchemaFilter;
  skipEmptyCompositeTypePruning?: boolean;
  skipEmptyUnionPruning?: boolean;
  skipUnimplementedAbstractTypePruning?: boolean;
  skipUnusedTypesPruning?: boolean;
}

type PruneSchemaFilter = (typeName: string) => boolean;

Usage Examples:

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

// Remove all unused types and fields
const prunedSchema = pruneSchema(schema);

// Prune but keep specific types
const selectivelyPruned = pruneSchema(schema, {
  skipPruning: (typeName) => {
    const keepTypes = ['User', 'Post', 'Comment'];
    return keepTypes.includes(typeName);
  }
});

Type Rewiring

Update type references throughout a GraphQL schema.

/**
 * Update type references throughout a GraphQL schema
 * @param schema - The schema to rewire
 * @param typeMap - Map of old type names to new types
 * @returns Schema with updated type references
 */
function rewireTypes(schema: GraphQLSchema, typeMap: Record<string, GraphQLNamedType>): GraphQLSchema;

Adding Types

Add new type definitions to an existing GraphQL schema.

/**
 * Add type definitions to an existing GraphQL schema
 * @param schema - The base schema
 * @param newTypes - Array of new types to add
 * @returns Schema with added types
 */
function addTypes(schema: GraphQLSchema, newTypes: GraphQLNamedType[]): GraphQLSchema;

Usage Examples:

import { addTypes } from "@graphql-tools/utils";
import { GraphQLObjectType, GraphQLString } from "graphql";

// Add a new type to existing schema
const newType = new GraphQLObjectType({
  name: 'NewType',
  fields: {
    message: { type: GraphQLString }
  }
});

const extendedSchema = addTypes(schema, [newType]);