GraphQL.js provides comprehensive utilities for building schemas from SDL, introspection, schema manipulation, comparison, and various transformation operations.
Build GraphQL schemas from Schema Definition Language strings and AST documents.
/**
* Build a GraphQL schema from SDL string
* @param source - SDL string or Source object
* @param options - Schema building options
* @returns Executable GraphQL schema
*/
function buildSchema(source: string | Source, options?: BuildSchemaOptions): GraphQLSchema;
/**
* Build a GraphQL schema from AST document
* @param documentAST - Parsed SDL document
* @param options - Schema building options
* @returns Executable GraphQL schema
*/
function buildASTSchema(documentAST: DocumentNode, options?: BuildSchemaOptions): GraphQLSchema;
interface BuildSchemaOptions {
/** Treat comment preceding definitions as descriptions */
commentDescriptions?: boolean;
/** Assume the SDL is valid and skip some validation */
assumeValidSDL?: boolean;
/** Assume the schema will be valid */
assumeValid?: boolean;
}Usage Examples:
import { buildSchema, buildASTSchema, parse } from "graphql";
// Build schema from SDL string
const schema = buildSchema(`
type Query {
user(id: ID!): User
posts: [Post!]!
}
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
publishedAt: String
}
type Mutation {
createPost(input: CreatePostInput!): Post!
}
input CreatePostInput {
title: String!
content: String!
authorId: ID!
}
`);
// Build schema from parsed AST
const sdlDocument = parse(sdlString);
const schemaFromAST = buildASTSchema(sdlDocument, {
commentDescriptions: true
});
// Build with options
const optimizedSchema = buildSchema(sdlString, {
assumeValidSDL: true, // Skip SDL validation for performance
assumeValid: true // Skip schema validation
});Build executable schemas from introspection query results.
/**
* Build a GraphQL schema from introspection result
* @param introspection - Introspection query result
* @param options - Schema building options
* @returns Executable GraphQL schema
*/
function buildClientSchema(
introspection: IntrospectionQuery,
options?: BuildSchemaOptions
): GraphQLSchema;Usage Examples:
import { buildClientSchema, getIntrospectionQuery, graphql } from "graphql";
// Get introspection from existing schema
const introspectionQuery = getIntrospectionQuery();
const introspectionResult = await graphql({
schema: existingSchema,
source: introspectionQuery
});
// Build new schema from introspection
const clientSchema = buildClientSchema(introspectionResult.data as IntrospectionQuery);
// Now clientSchema has the same type structure as existingSchema
// but without resolvers (useful for client-side tools)
// Build with options
const clientSchemaWithOptions = buildClientSchema(introspectionResult.data, {
assumeValid: true
});Extend existing schemas with additional type definitions.
/**
* Extend an existing GraphQL schema with additional type definitions
* @param schema - Base schema to extend
* @param documentAST - SDL document with extensions
* @param options - Schema building options
* @returns Extended GraphQL schema
*/
function extendSchema(
schema: GraphQLSchema,
documentAST: DocumentNode,
options?: BuildSchemaOptions
): GraphQLSchema;Usage Examples:
import { extendSchema, buildSchema, parse } from "graphql";
// Base schema
const baseSchema = buildSchema(`
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String!
}
`);
// Extension SDL
const extensionSDL = `
extend type Query {
posts: [Post!]!
search(query: String!): [SearchResult!]!
}
extend type User {
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
union SearchResult = User | Post
`;
// Extend schema
const extensionDocument = parse(extensionSDL);
const extendedSchema = extendSchema(baseSchema, extensionDocument);
// Multiple extensions
const additionalExtensions = parse(`
extend type Post {
tags: [String!]!
publishedAt: String
}
type Comment {
id: ID!
content: String!
author: User!
post: Post!
}
extend type Query {
comments: [Comment!]!
}
`);
const fullyExtendedSchema = extendSchema(extendedSchema, additionalExtensions);Generate introspection queries and extract introspection data from schemas.
/**
* Generate a GraphQL introspection query
* @param options - Introspection options
* @returns GraphQL introspection query string
*/
function getIntrospectionQuery(options?: IntrospectionOptions): string;
/**
* Get introspection result from a schema
* @param schema - GraphQL schema to introspect
* @param options - Introspection options
* @returns Introspection query result
*/
function introspectionFromSchema(
schema: GraphQLSchema,
options?: IntrospectionOptions
): IntrospectionQuery;
interface IntrospectionOptions {
/** Include field and argument descriptions */
descriptions?: boolean;
/** Include specifiedByUrl for custom scalars */
specifiedByUrl?: boolean;
/** Include isRepeatable flag for directives */
directiveIsRepeatable?: boolean;
/** Include schema description */
schemaDescription?: boolean;
/** Include deprecation info for input values */
inputValueDeprecation?: boolean;
}Usage Examples:
import {
getIntrospectionQuery,
introspectionFromSchema,
buildSchema,
graphql
} from "graphql";
const schema = buildSchema(`
"""
User management schema
"""
type Query {
"""Get user by ID"""
user(id: ID!): User
}
"""A user in the system"""
type User {
id: ID!
name: String!
"""User's email address"""
email: String!
}
`);
// Generate introspection query
const introspectionQuery = getIntrospectionQuery({
descriptions: true,
schemaDescription: true
});
// Execute introspection against schema
const result = await graphql({
schema,
source: introspectionQuery
});
// Get introspection directly from schema
const introspectionResult = introspectionFromSchema(schema, {
descriptions: true,
schemaDescription: true
});
// Minimal introspection for production
const minimalIntrospection = getIntrospectionQuery({
descriptions: false,
specifiedByUrl: false
});Convert schemas and types back to SDL strings.
/**
* Print a GraphQL schema as SDL string
* @param schema - GraphQL schema to print
* @returns SDL string representation
*/
function printSchema(schema: GraphQLSchema): string;
/**
* Print a single GraphQL type as SDL
* @param type - GraphQL type to print
* @returns SDL string for the type
*/
function printType(type: GraphQLNamedType): string;
/**
* Print the built-in introspection schema
* @returns SDL string for introspection schema
*/
function printIntrospectionSchema(): string;Usage Examples:
import { printSchema, printType, buildSchema } from "graphql";
const schema = buildSchema(`
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
author: User!
}
`);
// Print entire schema
const schemaSDL = printSchema(schema);
console.log(schemaSDL);
/*
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
author: User!
}
*/
// Print specific type
const userType = schema.getType('User');
const userSDL = printType(userType);
console.log(userSDL);
/*
type User {
id: ID!
name: String!
posts: [Post!]!
}
*/
// Print introspection schema
const introspectionSDL = printIntrospectionSchema();Transform and manipulate existing schemas.
/**
* Sort schema types and fields lexicographically
* @param schema - GraphQL schema to sort
* @returns New schema with sorted elements
*/
function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema;Usage Examples:
import { lexicographicSortSchema, buildSchema, printSchema } from "graphql";
const schema = buildSchema(`
type Query {
zebra: String
alpha: String
beta: String
}
type Zebra {
id: ID!
}
type Alpha {
name: String!
}
`);
// Sort schema alphabetically
const sortedSchema = lexicographicSortSchema(schema);
const sortedSDL = printSchema(sortedSchema);
console.log(sortedSDL);
/*
type Alpha {
name: String!
}
type Query {
alpha: String
beta: String
zebra: String
}
type Zebra {
id: ID!
}
*/Convert between AST nodes, GraphQL types, and JavaScript values.
/**
* Get operation AST from document
* @param documentAST - GraphQL document
* @param operationName - Operation name (optional)
* @returns Operation definition AST or undefined
*/
function getOperationAST(
documentAST: DocumentNode,
operationName?: Maybe<string>
): Maybe<OperationDefinitionNode>;
/**
* Get root type for an operation
* @param schema - GraphQL schema
* @param operation - Operation definition AST
* @returns Root object type for the operation
*/
function getOperationRootType(
schema: GraphQLSchema,
operation: OperationDefinitionNode
): GraphQLObjectType;
/**
* Convert AST type node to GraphQL type
* @param schema - GraphQL schema
* @param typeNode - AST type node
* @returns GraphQL type or undefined
*/
function typeFromAST(
schema: GraphQLSchema,
typeNode: TypeNode
): Maybe<GraphQLType>;
/**
* Convert AST value to JavaScript value
* @param valueNode - AST value node
* @param type - GraphQL input type
* @param variables - Variable values
* @returns JavaScript value
*/
function valueFromAST(
valueNode: Maybe<ValueNode>,
type: GraphQLInputType,
variables?: Maybe<{ [variable: string]: unknown }>
): unknown;
/**
* Convert JavaScript value to AST node
* @param value - JavaScript value
* @param type - GraphQL input type
* @returns AST value node or undefined
*/
function astFromValue(
value: unknown,
type: GraphQLInputType
): Maybe<ValueNode>;Usage Examples:
import {
getOperationAST,
getOperationRootType,
typeFromAST,
valueFromAST,
astFromValue,
parse
} from "graphql";
const document = parse(`
query GetUser($id: ID!) {
user(id: $id) { name }
}
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) { id }
}
`);
// Get specific operation
const queryOperation = getOperationAST(document, "GetUser");
const mutationOperation = getOperationAST(document); // Gets first operation
// Get root type for operation
const queryRootType = getOperationRootType(schema, queryOperation);
console.log(queryRootType.name); // "Query"
// Convert type AST to GraphQL type
const typeNode = parse('String!').definitions[0].type;
const graphqlType = typeFromAST(schema, typeNode);
// Convert AST value to JavaScript
const valueNode = parse('"hello"').definitions[0];
const jsValue = valueFromAST(valueNode, GraphQLString);
console.log(jsValue); // "hello"
// Convert JavaScript value to AST
const astNode = astFromValue("hello", GraphQLString);
console.log(print(astNode)); // "hello"Compare schemas to find breaking and dangerous changes.
/**
* Find breaking changes between schemas
* @param oldSchema - Original schema
* @param newSchema - New schema to compare
* @returns Array of breaking changes
*/
function findBreakingChanges(
oldSchema: GraphQLSchema,
newSchema: GraphQLSchema
): Array<BreakingChange>;
/**
* Find dangerous changes between schemas
* @param oldSchema - Original schema
* @param newSchema - New schema to compare
* @returns Array of dangerous changes
*/
function findDangerousChanges(
oldSchema: GraphQLSchema,
newSchema: GraphQLSchema
): Array<DangerousChange>;
interface BreakingChange {
type: BreakingChangeType;
description: string;
}
interface DangerousChange {
type: DangerousChangeType;
description: string;
}
enum BreakingChangeType {
TYPE_REMOVED = "TYPE_REMOVED",
TYPE_CHANGED_KIND = "TYPE_CHANGED_KIND",
TYPE_REMOVED_FROM_UNION = "TYPE_REMOVED_FROM_UNION",
VALUE_REMOVED_FROM_ENUM = "VALUE_REMOVED_FROM_ENUM",
REQUIRED_INPUT_FIELD_ADDED = "REQUIRED_INPUT_FIELD_ADDED",
IMPLEMENTED_INTERFACE_REMOVED = "IMPLEMENTED_INTERFACE_REMOVED",
FIELD_REMOVED = "FIELD_REMOVED",
FIELD_CHANGED_KIND = "FIELD_CHANGED_KIND",
REQUIRED_ARG_ADDED = "REQUIRED_ARG_ADDED",
ARG_REMOVED = "ARG_REMOVED",
ARG_CHANGED_KIND = "ARG_CHANGED_KIND",
DIRECTIVE_REMOVED = "DIRECTIVE_REMOVED",
DIRECTIVE_ARG_REMOVED = "DIRECTIVE_ARG_REMOVED",
REQUIRED_DIRECTIVE_ARG_ADDED = "REQUIRED_DIRECTIVE_ARG_ADDED",
DIRECTIVE_REPEATABLE_REMOVED = "DIRECTIVE_REPEATABLE_REMOVED",
DIRECTIVE_LOCATION_REMOVED = "DIRECTIVE_LOCATION_REMOVED"
}
enum DangerousChangeType {
ARG_ADDED = "ARG_ADDED",
VALUE_ADDED_TO_ENUM = "VALUE_ADDED_TO_ENUM",
INTERFACE_ADDED_TO_OBJECT = "INTERFACE_ADDED_TO_OBJECT",
TYPE_ADDED_TO_UNION = "TYPE_ADDED_TO_UNION",
OPTIONAL_INPUT_FIELD_ADDED = "OPTIONAL_INPUT_FIELD_ADDED",
OPTIONAL_ARG_ADDED = "OPTIONAL_ARG_ADDED",
IMPLEMENTED_INTERFACE_ADDED = "IMPLEMENTED_INTERFACE_ADDED"
}Usage Examples:
import {
findBreakingChanges,
findDangerousChanges,
buildSchema
} from "graphql";
const oldSchema = buildSchema(`
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String!
email: String!
}
enum Status {
ACTIVE
INACTIVE
}
`);
const newSchema = buildSchema(`
type Query {
user(id: ID!, includeEmail: Boolean = false): User
}
type User {
id: ID!
name: String!
email: String
profile: Profile
}
type Profile {
bio: String
}
enum Status {
ACTIVE
INACTIVE
PENDING
}
`);
// Find breaking changes
const breakingChanges = findBreakingChanges(oldSchema, newSchema);
console.log(breakingChanges);
/*
[
{
type: "FIELD_CHANGED_KIND",
description: "User.email changed type from String! to String"
}
]
*/
// Find dangerous changes
const dangerousChanges = findDangerousChanges(oldSchema, newSchema);
console.log(dangerousChanges);
/*
[
{
type: "ARG_ADDED",
description: "An optional argument includeEmail was added to Query.user"
},
{
type: "VALUE_ADDED_TO_ENUM",
description: "PENDING was added to enum Status"
}
]
*/
// Use in CI/CD pipeline
const validateSchemaChanges = (oldSchema, newSchema) => {
const breaking = findBreakingChanges(oldSchema, newSchema);
const dangerous = findDangerousChanges(oldSchema, newSchema);
if (breaking.length > 0) {
console.error("Breaking changes detected:");
breaking.forEach(change => console.error(`- ${change.description}`));
process.exit(1);
}
if (dangerous.length > 0) {
console.warn("Dangerous changes detected:");
dangerous.forEach(change => console.warn(`- ${change.description}`));
}
console.log("Schema changes validated successfully");
};Other useful schema manipulation utilities.
/**
* Coerce input value to GraphQL type
* @param inputValue - Value to coerce
* @param type - GraphQL input type
* @param onError - Error callback
* @returns Coerced value
*/
function coerceInputValue(
inputValue: unknown,
type: GraphQLInputType,
onError?: (path: ReadonlyArray<string | number>, invalidValue: unknown, error: GraphQLError) => void
): unknown;
/**
* Validate GraphQL schema and return array of errors
* @param schema - GraphQL schema to validate
* @returns Array of validation errors
*/
function validateSchema(schema: GraphQLSchema): ReadonlyArray<GraphQLError>;
/**
* Validate GraphQL schema and throw on errors
* @param schema - GraphQL schema to validate
* @throws Error if schema has validation errors
*/
function assertValidSchema(schema: GraphQLSchema): void;
/**
* Assert that a string is a valid GraphQL name
* @param name - Name to validate
* @returns The validated name
* @throws GraphQLError if name is invalid
*/
function assertName(name: string): string;
/**
* Assert that a string is a valid GraphQL enum value name
* @param name - Enum value name to validate
* @returns The validated name
* @throws GraphQLError if name is invalid
*/
function assertEnumValueName(name: string): string;
/**
* Resolve a thunk that returns a readonly array
* @param thunk - Function or array to resolve
* @returns Resolved readonly array
*/
function resolveReadonlyArrayThunk<T>(thunk: ThunkReadonlyArray<T>): ReadonlyArray<T>;
/**
* Resolve a thunk that returns an object map
* @param thunk - Function or object map to resolve
* @returns Resolved object map
*/
function resolveObjMapThunk<T>(thunk: ThunkObjMap<T>): ThunkObjMap<T>;
/**
* Check if two types are equal
* @param typeA - First type
* @param typeB - Second type
* @returns True if types are equal
*/
function isEqualType(typeA: GraphQLType, typeB: GraphQLType): boolean;
/**
* Check if type is subtype of another
* @param schema - GraphQL schema
* @param maybeSubType - Potential subtype
* @param superType - Super type
* @returns True if subtype relationship exists
*/
function isTypeSubTypeOf(
schema: GraphQLSchema,
maybeSubType: GraphQLType,
superType: GraphQLType
): boolean;
/**
* Check if two composite types can overlap
* @param schema - GraphQL schema
* @param typeA - First composite type
* @param typeB - Second composite type
* @returns True if types can overlap
*/
function doTypesOverlap(
schema: GraphQLSchema,
typeA: GraphQLCompositeType,
typeB: GraphQLCompositeType
): boolean;