GraphQL-core is a Python port of GraphQL.js, the JavaScript reference implementation for GraphQL.
—
Build schemas from SDL, perform introspection, compare schemas, and manipulate GraphQL type information for tooling and development. Provides comprehensive utilities for schema analysis, transformation, and development workflows.
Build GraphQL schemas from various sources including Schema Definition Language (SDL) and introspection results.
def build_schema(
source: Union[str, Source],
assume_valid: bool = False,
assume_valid_sdl: bool = False,
no_location: bool = False,
allow_legacy_fragment_variables: bool = False,
) -> GraphQLSchema
def build_ast_schema(document_ast: DocumentNode, assume_valid: bool = False, assume_valid_sdl: bool = False) -> GraphQLSchema
def build_client_schema(introspection: Dict[str, Any], assume_valid: bool = False) -> GraphQLSchema
def extend_schema(schema: GraphQLSchema, document_ast: DocumentNode, assume_valid: bool = False, assume_valid_sdl: bool = False) -> GraphQLSchemaParameters:
source: SDL string or Source objectdocument_ast: Parsed SDL documentintrospection: Introspection query resultassume_valid: Skip validation for performanceassume_valid_sdl: Skip SDL validationno_location: Don't include location information in ASTallow_legacy_fragment_variables: Allow legacy fragment variable syntaxReturns: GraphQLSchema instance
from graphql import build_schema, build_ast_schema, parse, build_client_schema
# Build from SDL string
schema = build_schema('''
type Query {
user(id: ID!): User
users: [User]
}
type User {
id: ID!
name: String!
email: String
posts: [Post]
}
type Post {
id: ID!
title: String!
content: String
author: User
}
''')
# Build from parsed AST
sdl_document = parse('''
type Query {
hello: String
}
''')
schema_from_ast = build_ast_schema(sdl_document)
# Build from introspection result
introspection_result = {
'data': {
'__schema': {
'queryType': {'name': 'Query'},
'types': [
{
'name': 'Query',
'kind': 'OBJECT',
'fields': [
{
'name': 'hello',
'type': {'name': 'String', 'kind': 'SCALAR'},
'args': []
}
]
}
]
}
}
}
client_schema = build_client_schema(introspection_result['data'])Extend existing schemas with additional type definitions and modifications.
def extend_schema(
schema: GraphQLSchema,
document_ast: DocumentNode,
assume_valid: bool = False,
assume_valid_sdl: bool = False
) -> GraphQLSchemafrom graphql import build_schema, extend_schema, parse
base_schema = build_schema('''
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String!
}
''')
extension = parse('''
extend type User {
email: String
posts: [Post]
}
type Post {
id: ID!
title: String!
author: User
}
extend type Query {
posts: [Post]
}
''')
extended_schema = extend_schema(base_schema, extension)Generate and process GraphQL introspection queries for schema discovery and tooling.
def get_introspection_query(
description: bool = True,
specified_by_url: bool = False,
directive_is_repeatable: bool = False,
schema_description: bool = False,
input_value_deprecation: bool = False,
) -> str
def introspection_from_schema(
schema: GraphQLSchema,
options: Optional[IntrospectionOptions] = None
) -> Dict[str, Any]
class IntrospectionOptions:
description: bool = True
specified_by_url: bool = False
directive_is_repeatable: bool = False
schema_description: bool = False
input_value_deprecation: bool = False
IntrospectionQuery = Dict[str, Any]from graphql import get_introspection_query, introspection_from_schema, graphql_sync
# Get introspection query
introspection_query = get_introspection_query(
description=True,
specified_by_url=True
)
# Execute introspection against schema
result = graphql_sync(schema, introspection_query)
schema_info = result.data
# Or get introspection directly from schema
introspection_result = introspection_from_schema(schema)
print(introspection_result['__schema']['queryType']['name'])Convert GraphQL schemas and types back to SDL representation.
def print_schema(schema: GraphQLSchema, options: Optional[SchemaPrintOptions] = None) -> str
def print_type(type_: GraphQLNamedType, options: Optional[SchemaPrintOptions] = None) -> str
def print_introspection_schema(schema: GraphQLSchema) -> str
class SchemaPrintOptions:
comment_descriptions: bool = False
include_directives: Optional[Callable[[GraphQLDirective], bool]] = Nonefrom graphql import print_schema, print_type, print_introspection_schema
# Print entire schema as SDL
sdl = print_schema(schema)
print(sdl)
# Print specific type
user_type = schema.type_map['User']
user_sdl = print_type(user_type)
print(user_sdl)
# Print introspection schema
introspection_sdl = print_introspection_schema(schema)
print(introspection_sdl)Sort schema types and fields lexicographically for consistent output.
def lexicographic_sort_schema(schema: GraphQLSchema) -> GraphQLSchemafrom graphql import lexicographic_sort_schema, print_schema
# Sort schema for consistent output
sorted_schema = lexicographic_sort_schema(schema)
sorted_sdl = print_schema(sorted_schema)Extract and analyze operations from GraphQL documents.
def get_operation_ast(document: DocumentNode, operation_name: Optional[str] = None) -> Optional[OperationDefinitionNode]
def get_operation_root_type(schema: GraphQLSchema, operation: OperationDefinitionNode) -> GraphQLObjectTypefrom graphql import get_operation_ast, get_operation_root_type, parse
document = parse('''
query GetUser($id: ID!) {
user(id: $id) { name }
}
mutation CreateUser($input: UserInput!) {
createUser(input: $input) { id }
}
''')
# Get specific operation
query_op = get_operation_ast(document, 'GetUser')
mutation_op = get_operation_ast(document, 'CreateUser')
# Get root type for operation
query_root = get_operation_root_type(schema, query_op) # Returns Query type
mutation_root = get_operation_root_type(schema, mutation_op) # Returns Mutation typeConvert between AST representations and Python objects, manipulate documents.
def ast_to_dict(node: Node) -> Dict[str, Any]
def value_from_ast(value_node: Optional[ValueNode], type_: GraphQLInputType, variables: Optional[Dict[str, Any]] = None) -> Any
def value_from_ast_untyped(value_node: ValueNode, variables: Optional[Dict[str, Any]] = None) -> Any
def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]
def type_from_ast(schema: GraphQLSchema, type_node: TypeNode) -> Optional[GraphQLType]
def coerce_input_value(input_value: Any, type_: GraphQLInputType, on_error: Optional[Callable[[List[str], Any, GraphQLError], None]] = None) -> CoercionResultfrom graphql import ast_to_dict, value_from_ast, ast_from_value, parse_value, GraphQLString
# Convert AST to dictionary
value_ast = parse_value('"hello world"')
value_dict = ast_to_dict(value_ast)
print(value_dict) # {'kind': 'StringValue', 'value': 'hello world'}
# Extract value from AST with type
string_value = value_from_ast(value_ast, GraphQLString)
print(string_value) # 'hello world'
# Create AST from value
new_ast = ast_from_value('hello', GraphQLString)
print(new_ast.value) # 'hello'Manipulate and transform GraphQL documents.
def concat_ast(*documents: DocumentNode) -> DocumentNode
def separate_operations(document: DocumentNode) -> Dict[str, DocumentNode]
def strip_ignored_characters(source: str) -> strfrom graphql import concat_ast, separate_operations, strip_ignored_characters, parse
# Concatenate documents
doc1 = parse('query A { user { name } }')
doc2 = parse('query B { posts { title } }')
combined = concat_ast(doc1, doc2)
# Separate operations
multi_op_doc = parse('''
query GetUser { user { name } }
query GetPosts { posts { title } }
''')
separated = separate_operations(multi_op_doc)
print(separated.keys()) # ['GetUser', 'GetPosts']
# Strip ignored characters (comments, extra whitespace)
minified = strip_ignored_characters('''
# This is a comment
query {
user {
name
}
}
''')
print(minified) # 'query{user{name}}'Get detailed type information for AST traversal and analysis.
class TypeInfo:
def __init__(self, schema: GraphQLSchema, initial_type: Optional[GraphQLType] = None, get_field_def_fn: Optional[Callable] = None)
def get_type(self) -> Optional[GraphQLType]
def get_parent_type(self) -> Optional[GraphQLCompositeType]
def get_input_type(self) -> Optional[GraphQLInputType]
def get_parent_input_type(self) -> Optional[GraphQLInputType]
def get_field_def(self) -> Optional[GraphQLField]
def get_default_value(self) -> Optional[Any]
def get_directive(self) -> Optional[GraphQLDirective]
def get_argument(self) -> Optional[GraphQLArgument]
def get_enum_value(self) -> Optional[GraphQLEnumValue]
def enter(self, node: Node) -> None
def leave(self, node: Node) -> None
class TypeInfoVisitor:
def __init__(self, type_info: TypeInfo, visitor: Visitor)from graphql import TypeInfo, visit, Visitor
class FieldCollector(Visitor):
def __init__(self, type_info):
self.type_info = type_info
self.fields = []
def enter_field(self, node, key, parent, path, ancestors):
field_def = self.type_info.get_field_def()
parent_type = self.type_info.get_parent_type()
if field_def and parent_type:
self.fields.append(f"{parent_type.name}.{field_def.name}")
type_info = TypeInfo(schema)
collector = FieldCollector(type_info)
visitor = TypeInfoVisitor(type_info, collector)
document = parse('{ user { name email } posts { title } }')
visit(document, visitor)
print(collector.fields) # ['Query.user', 'User.name', 'User.email', 'Query.posts', 'Post.title']Compare and analyze relationships between GraphQL types.
def is_equal_type(type_a: GraphQLType, type_b: GraphQLType) -> bool
def is_type_sub_type_of(schema: GraphQLSchema, maybe_sub_type: GraphQLType, super_type: GraphQLType) -> bool
def do_types_overlap(schema: GraphQLSchema, type_a: GraphQLCompositeType, type_b: GraphQLCompositeType) -> boolfrom graphql import is_equal_type, is_type_sub_type_of, do_types_overlap, GraphQLNonNull, GraphQLString
# Compare types
print(is_equal_type(GraphQLString, GraphQLString)) # True
print(is_equal_type(GraphQLString, GraphQLNonNull(GraphQLString))) # False
# Check subtype relationships
print(is_type_sub_type_of(schema, GraphQLNonNull(GraphQLString), GraphQLString)) # True
print(is_type_sub_type_of(schema, GraphQLString, GraphQLNonNull(GraphQLString))) # False
# Check type overlap (for unions/interfaces)
user_type = schema.type_map['User']
admin_type = schema.type_map.get('Admin')
if admin_type:
print(do_types_overlap(schema, user_type, admin_type))Find breaking and dangerous changes between schema versions.
def find_breaking_changes(old_schema: GraphQLSchema, new_schema: GraphQLSchema) -> List[BreakingChange]
def find_dangerous_changes(old_schema: GraphQLSchema, new_schema: GraphQLSchema) -> List[DangerousChange]
class BreakingChange:
type: BreakingChangeType
description: str
class DangerousChange:
type: DangerousChangeType
description: str
class BreakingChangeType(Enum):
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"
class DangerousChangeType(Enum):
VALUE_ADDED_TO_ENUM = "VALUE_ADDED_TO_ENUM"
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"
ARG_DEFAULT_VALUE_CHANGE = "ARG_DEFAULT_VALUE_CHANGE"from graphql import find_breaking_changes, find_dangerous_changes, build_schema
old_schema = build_schema('''
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String!
}
''')
new_schema = build_schema('''
type Query {
user(id: ID!): User
users: [User]
}
type User {
id: ID!
name: String!
email: String
}
''')
breaking_changes = find_breaking_changes(old_schema, new_schema)
dangerous_changes = find_dangerous_changes(old_schema, new_schema)
print(f"Breaking changes: {len(breaking_changes)}")
print(f"Dangerous changes: {len(dangerous_changes)}")
for change in dangerous_changes:
print(f"{change.type.value}: {change.description}")Validate GraphQL names according to specification rules.
def assert_valid_name(name: str) -> str
def is_valid_name_error(name: str) -> Optional[GraphQLError]from graphql import assert_valid_name, is_valid_name_error
# Valid names
assert_valid_name('User') # Returns 'User'
assert_valid_name('_internal') # Returns '_internal'
# Invalid names
try:
assert_valid_name('123Invalid') # Raises GraphQLError
except GraphQLError as e:
print(e.message)
error = is_valid_name_error('invalid-name')
if error:
print(error.message) # Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "invalid-name" does not.# Import required types
from typing import Any, Dict, List, Optional, Union, Callable, TypedDict
from graphql.type import GraphQLSchema, GraphQLType, GraphQLNamedType, GraphQLInputType, GraphQLCompositeType, GraphQLObjectType, GraphQLDirective, GraphQLField, GraphQLEnumValue
from graphql.language import DocumentNode, Source, Node, ValueNode, TypeNode, OperationDefinitionNode
from graphql.error import GraphQLError
# Schema construction types
SchemaPrintOptions = class SchemaPrintOptions
IntrospectionOptions = class IntrospectionOptions
IntrospectionQuery = Dict[str, Any]
# Type analysis types
TypeInfo = class TypeInfo
TypeInfoVisitor = class TypeInfoVisitor
# Schema comparison types
BreakingChange = class BreakingChange
DangerousChange = class DangerousChange
BreakingChangeType = Enum
DangerousChangeType = Enum
# Value coercion types
class CoercionResult:
value: Any
errors: List[GraphQLError]Install with Tessl CLI
npx tessl i tessl/pypi-graphql-core