0
# Schema Utilities and Introspection
1
2
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.
3
4
## Capabilities
5
6
### Schema Construction
7
8
Build GraphQL schemas from various sources including Schema Definition Language (SDL) and introspection results.
9
10
```python { .api }
11
def build_schema(
12
source: Union[str, Source],
13
assume_valid: bool = False,
14
assume_valid_sdl: bool = False,
15
no_location: bool = False,
16
allow_legacy_fragment_variables: bool = False,
17
) -> GraphQLSchema
18
def build_ast_schema(document_ast: DocumentNode, assume_valid: bool = False, assume_valid_sdl: bool = False) -> GraphQLSchema
19
def build_client_schema(introspection: Dict[str, Any], assume_valid: bool = False) -> GraphQLSchema
20
def extend_schema(schema: GraphQLSchema, document_ast: DocumentNode, assume_valid: bool = False, assume_valid_sdl: bool = False) -> GraphQLSchema
21
```
22
23
**Parameters:**
24
- `source`: SDL string or Source object
25
- `document_ast`: Parsed SDL document
26
- `introspection`: Introspection query result
27
- `assume_valid`: Skip validation for performance
28
- `assume_valid_sdl`: Skip SDL validation
29
- `no_location`: Don't include location information in AST
30
- `allow_legacy_fragment_variables`: Allow legacy fragment variable syntax
31
32
**Returns:** GraphQLSchema instance
33
34
#### Usage Examples
35
36
```python
37
from graphql import build_schema, build_ast_schema, parse, build_client_schema
38
39
# Build from SDL string
40
schema = build_schema('''
41
type Query {
42
user(id: ID!): User
43
users: [User]
44
}
45
46
type User {
47
id: ID!
48
name: String!
49
email: String
50
posts: [Post]
51
}
52
53
type Post {
54
id: ID!
55
title: String!
56
content: String
57
author: User
58
}
59
''')
60
61
# Build from parsed AST
62
sdl_document = parse('''
63
type Query {
64
hello: String
65
}
66
''')
67
schema_from_ast = build_ast_schema(sdl_document)
68
69
# Build from introspection result
70
introspection_result = {
71
'data': {
72
'__schema': {
73
'queryType': {'name': 'Query'},
74
'types': [
75
{
76
'name': 'Query',
77
'kind': 'OBJECT',
78
'fields': [
79
{
80
'name': 'hello',
81
'type': {'name': 'String', 'kind': 'SCALAR'},
82
'args': []
83
}
84
]
85
}
86
]
87
}
88
}
89
}
90
client_schema = build_client_schema(introspection_result['data'])
91
```
92
93
### Schema Extension
94
95
Extend existing schemas with additional type definitions and modifications.
96
97
```python { .api }
98
def extend_schema(
99
schema: GraphQLSchema,
100
document_ast: DocumentNode,
101
assume_valid: bool = False,
102
assume_valid_sdl: bool = False
103
) -> GraphQLSchema
104
```
105
106
#### Usage Example
107
108
```python
109
from graphql import build_schema, extend_schema, parse
110
111
base_schema = build_schema('''
112
type Query {
113
user(id: ID!): User
114
}
115
116
type User {
117
id: ID!
118
name: String!
119
}
120
''')
121
122
extension = parse('''
123
extend type User {
124
email: String
125
posts: [Post]
126
}
127
128
type Post {
129
id: ID!
130
title: String!
131
author: User
132
}
133
134
extend type Query {
135
posts: [Post]
136
}
137
''')
138
139
extended_schema = extend_schema(base_schema, extension)
140
```
141
142
### Schema Introspection
143
144
Generate and process GraphQL introspection queries for schema discovery and tooling.
145
146
```python { .api }
147
def get_introspection_query(
148
description: bool = True,
149
specified_by_url: bool = False,
150
directive_is_repeatable: bool = False,
151
schema_description: bool = False,
152
input_value_deprecation: bool = False,
153
) -> str
154
155
def introspection_from_schema(
156
schema: GraphQLSchema,
157
options: Optional[IntrospectionOptions] = None
158
) -> Dict[str, Any]
159
160
class IntrospectionOptions:
161
description: bool = True
162
specified_by_url: bool = False
163
directive_is_repeatable: bool = False
164
schema_description: bool = False
165
input_value_deprecation: bool = False
166
167
IntrospectionQuery = Dict[str, Any]
168
```
169
170
#### Usage Example
171
172
```python
173
from graphql import get_introspection_query, introspection_from_schema, graphql_sync
174
175
# Get introspection query
176
introspection_query = get_introspection_query(
177
description=True,
178
specified_by_url=True
179
)
180
181
# Execute introspection against schema
182
result = graphql_sync(schema, introspection_query)
183
schema_info = result.data
184
185
# Or get introspection directly from schema
186
introspection_result = introspection_from_schema(schema)
187
print(introspection_result['__schema']['queryType']['name'])
188
```
189
190
### Schema Printing
191
192
Convert GraphQL schemas and types back to SDL representation.
193
194
```python { .api }
195
def print_schema(schema: GraphQLSchema, options: Optional[SchemaPrintOptions] = None) -> str
196
def print_type(type_: GraphQLNamedType, options: Optional[SchemaPrintOptions] = None) -> str
197
def print_introspection_schema(schema: GraphQLSchema) -> str
198
199
class SchemaPrintOptions:
200
comment_descriptions: bool = False
201
include_directives: Optional[Callable[[GraphQLDirective], bool]] = None
202
```
203
204
#### Usage Example
205
206
```python
207
from graphql import print_schema, print_type, print_introspection_schema
208
209
# Print entire schema as SDL
210
sdl = print_schema(schema)
211
print(sdl)
212
213
# Print specific type
214
user_type = schema.type_map['User']
215
user_sdl = print_type(user_type)
216
print(user_sdl)
217
218
# Print introspection schema
219
introspection_sdl = print_introspection_schema(schema)
220
print(introspection_sdl)
221
```
222
223
### Schema Sorting
224
225
Sort schema types and fields lexicographically for consistent output.
226
227
```python { .api }
228
def lexicographic_sort_schema(schema: GraphQLSchema) -> GraphQLSchema
229
```
230
231
#### Usage Example
232
233
```python
234
from graphql import lexicographic_sort_schema, print_schema
235
236
# Sort schema for consistent output
237
sorted_schema = lexicographic_sort_schema(schema)
238
sorted_sdl = print_schema(sorted_schema)
239
```
240
241
### Operation Processing
242
243
Extract and analyze operations from GraphQL documents.
244
245
```python { .api }
246
def get_operation_ast(document: DocumentNode, operation_name: Optional[str] = None) -> Optional[OperationDefinitionNode]
247
def get_operation_root_type(schema: GraphQLSchema, operation: OperationDefinitionNode) -> GraphQLObjectType
248
```
249
250
#### Usage Example
251
252
```python
253
from graphql import get_operation_ast, get_operation_root_type, parse
254
255
document = parse('''
256
query GetUser($id: ID!) {
257
user(id: $id) { name }
258
}
259
260
mutation CreateUser($input: UserInput!) {
261
createUser(input: $input) { id }
262
}
263
''')
264
265
# Get specific operation
266
query_op = get_operation_ast(document, 'GetUser')
267
mutation_op = get_operation_ast(document, 'CreateUser')
268
269
# Get root type for operation
270
query_root = get_operation_root_type(schema, query_op) # Returns Query type
271
mutation_root = get_operation_root_type(schema, mutation_op) # Returns Mutation type
272
```
273
274
### AST Processing and Manipulation
275
276
Convert between AST representations and Python objects, manipulate documents.
277
278
```python { .api }
279
def ast_to_dict(node: Node) -> Dict[str, Any]
280
def value_from_ast(value_node: Optional[ValueNode], type_: GraphQLInputType, variables: Optional[Dict[str, Any]] = None) -> Any
281
def value_from_ast_untyped(value_node: ValueNode, variables: Optional[Dict[str, Any]] = None) -> Any
282
def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]
283
def type_from_ast(schema: GraphQLSchema, type_node: TypeNode) -> Optional[GraphQLType]
284
def coerce_input_value(input_value: Any, type_: GraphQLInputType, on_error: Optional[Callable[[List[str], Any, GraphQLError], None]] = None) -> CoercionResult
285
```
286
287
#### Usage Example
288
289
```python
290
from graphql import ast_to_dict, value_from_ast, ast_from_value, parse_value, GraphQLString
291
292
# Convert AST to dictionary
293
value_ast = parse_value('"hello world"')
294
value_dict = ast_to_dict(value_ast)
295
print(value_dict) # {'kind': 'StringValue', 'value': 'hello world'}
296
297
# Extract value from AST with type
298
string_value = value_from_ast(value_ast, GraphQLString)
299
print(string_value) # 'hello world'
300
301
# Create AST from value
302
new_ast = ast_from_value('hello', GraphQLString)
303
print(new_ast.value) # 'hello'
304
```
305
306
### Document Manipulation
307
308
Manipulate and transform GraphQL documents.
309
310
```python { .api }
311
def concat_ast(*documents: DocumentNode) -> DocumentNode
312
def separate_operations(document: DocumentNode) -> Dict[str, DocumentNode]
313
def strip_ignored_characters(source: str) -> str
314
```
315
316
#### Usage Example
317
318
```python
319
from graphql import concat_ast, separate_operations, strip_ignored_characters, parse
320
321
# Concatenate documents
322
doc1 = parse('query A { user { name } }')
323
doc2 = parse('query B { posts { title } }')
324
combined = concat_ast(doc1, doc2)
325
326
# Separate operations
327
multi_op_doc = parse('''
328
query GetUser { user { name } }
329
query GetPosts { posts { title } }
330
''')
331
separated = separate_operations(multi_op_doc)
332
print(separated.keys()) # ['GetUser', 'GetPosts']
333
334
# Strip ignored characters (comments, extra whitespace)
335
minified = strip_ignored_characters('''
336
# This is a comment
337
query {
338
user {
339
name
340
}
341
}
342
''')
343
print(minified) # 'query{user{name}}'
344
```
345
346
### Type Information and Analysis
347
348
Get detailed type information for AST traversal and analysis.
349
350
```python { .api }
351
class TypeInfo:
352
def __init__(self, schema: GraphQLSchema, initial_type: Optional[GraphQLType] = None, get_field_def_fn: Optional[Callable] = None)
353
354
def get_type(self) -> Optional[GraphQLType]
355
def get_parent_type(self) -> Optional[GraphQLCompositeType]
356
def get_input_type(self) -> Optional[GraphQLInputType]
357
def get_parent_input_type(self) -> Optional[GraphQLInputType]
358
def get_field_def(self) -> Optional[GraphQLField]
359
def get_default_value(self) -> Optional[Any]
360
def get_directive(self) -> Optional[GraphQLDirective]
361
def get_argument(self) -> Optional[GraphQLArgument]
362
def get_enum_value(self) -> Optional[GraphQLEnumValue]
363
364
def enter(self, node: Node) -> None
365
def leave(self, node: Node) -> None
366
367
class TypeInfoVisitor:
368
def __init__(self, type_info: TypeInfo, visitor: Visitor)
369
```
370
371
#### Usage Example
372
373
```python
374
from graphql import TypeInfo, visit, Visitor
375
376
class FieldCollector(Visitor):
377
def __init__(self, type_info):
378
self.type_info = type_info
379
self.fields = []
380
381
def enter_field(self, node, key, parent, path, ancestors):
382
field_def = self.type_info.get_field_def()
383
parent_type = self.type_info.get_parent_type()
384
if field_def and parent_type:
385
self.fields.append(f"{parent_type.name}.{field_def.name}")
386
387
type_info = TypeInfo(schema)
388
collector = FieldCollector(type_info)
389
visitor = TypeInfoVisitor(type_info, collector)
390
391
document = parse('{ user { name email } posts { title } }')
392
visit(document, visitor)
393
print(collector.fields) # ['Query.user', 'User.name', 'User.email', 'Query.posts', 'Post.title']
394
```
395
396
### Type Comparisons
397
398
Compare and analyze relationships between GraphQL types.
399
400
```python { .api }
401
def is_equal_type(type_a: GraphQLType, type_b: GraphQLType) -> bool
402
def is_type_sub_type_of(schema: GraphQLSchema, maybe_sub_type: GraphQLType, super_type: GraphQLType) -> bool
403
def do_types_overlap(schema: GraphQLSchema, type_a: GraphQLCompositeType, type_b: GraphQLCompositeType) -> bool
404
```
405
406
#### Usage Example
407
408
```python
409
from graphql import is_equal_type, is_type_sub_type_of, do_types_overlap, GraphQLNonNull, GraphQLString
410
411
# Compare types
412
print(is_equal_type(GraphQLString, GraphQLString)) # True
413
print(is_equal_type(GraphQLString, GraphQLNonNull(GraphQLString))) # False
414
415
# Check subtype relationships
416
print(is_type_sub_type_of(schema, GraphQLNonNull(GraphQLString), GraphQLString)) # True
417
print(is_type_sub_type_of(schema, GraphQLString, GraphQLNonNull(GraphQLString))) # False
418
419
# Check type overlap (for unions/interfaces)
420
user_type = schema.type_map['User']
421
admin_type = schema.type_map.get('Admin')
422
if admin_type:
423
print(do_types_overlap(schema, user_type, admin_type))
424
```
425
426
### Schema Comparison
427
428
Find breaking and dangerous changes between schema versions.
429
430
```python { .api }
431
def find_breaking_changes(old_schema: GraphQLSchema, new_schema: GraphQLSchema) -> List[BreakingChange]
432
def find_dangerous_changes(old_schema: GraphQLSchema, new_schema: GraphQLSchema) -> List[DangerousChange]
433
434
class BreakingChange:
435
type: BreakingChangeType
436
description: str
437
438
class DangerousChange:
439
type: DangerousChangeType
440
description: str
441
442
class BreakingChangeType(Enum):
443
TYPE_REMOVED = "TYPE_REMOVED"
444
TYPE_CHANGED_KIND = "TYPE_CHANGED_KIND"
445
TYPE_REMOVED_FROM_UNION = "TYPE_REMOVED_FROM_UNION"
446
VALUE_REMOVED_FROM_ENUM = "VALUE_REMOVED_FROM_ENUM"
447
REQUIRED_INPUT_FIELD_ADDED = "REQUIRED_INPUT_FIELD_ADDED"
448
IMPLEMENTED_INTERFACE_REMOVED = "IMPLEMENTED_INTERFACE_REMOVED"
449
FIELD_REMOVED = "FIELD_REMOVED"
450
FIELD_CHANGED_KIND = "FIELD_CHANGED_KIND"
451
REQUIRED_ARG_ADDED = "REQUIRED_ARG_ADDED"
452
ARG_REMOVED = "ARG_REMOVED"
453
ARG_CHANGED_KIND = "ARG_CHANGED_KIND"
454
DIRECTIVE_REMOVED = "DIRECTIVE_REMOVED"
455
DIRECTIVE_ARG_REMOVED = "DIRECTIVE_ARG_REMOVED"
456
REQUIRED_DIRECTIVE_ARG_ADDED = "REQUIRED_DIRECTIVE_ARG_ADDED"
457
DIRECTIVE_REPEATABLE_REMOVED = "DIRECTIVE_REPEATABLE_REMOVED"
458
DIRECTIVE_LOCATION_REMOVED = "DIRECTIVE_LOCATION_REMOVED"
459
460
class DangerousChangeType(Enum):
461
VALUE_ADDED_TO_ENUM = "VALUE_ADDED_TO_ENUM"
462
TYPE_ADDED_TO_UNION = "TYPE_ADDED_TO_UNION"
463
OPTIONAL_INPUT_FIELD_ADDED = "OPTIONAL_INPUT_FIELD_ADDED"
464
OPTIONAL_ARG_ADDED = "OPTIONAL_ARG_ADDED"
465
IMPLEMENTED_INTERFACE_ADDED = "IMPLEMENTED_INTERFACE_ADDED"
466
ARG_DEFAULT_VALUE_CHANGE = "ARG_DEFAULT_VALUE_CHANGE"
467
```
468
469
#### Usage Example
470
471
```python
472
from graphql import find_breaking_changes, find_dangerous_changes, build_schema
473
474
old_schema = build_schema('''
475
type Query {
476
user(id: ID!): User
477
}
478
479
type User {
480
id: ID!
481
name: String!
482
}
483
''')
484
485
new_schema = build_schema('''
486
type Query {
487
user(id: ID!): User
488
users: [User]
489
}
490
491
type User {
492
id: ID!
493
name: String!
494
email: String
495
}
496
''')
497
498
breaking_changes = find_breaking_changes(old_schema, new_schema)
499
dangerous_changes = find_dangerous_changes(old_schema, new_schema)
500
501
print(f"Breaking changes: {len(breaking_changes)}")
502
print(f"Dangerous changes: {len(dangerous_changes)}")
503
504
for change in dangerous_changes:
505
print(f"{change.type.value}: {change.description}")
506
```
507
508
### Name Validation
509
510
Validate GraphQL names according to specification rules.
511
512
```python { .api }
513
def assert_valid_name(name: str) -> str
514
def is_valid_name_error(name: str) -> Optional[GraphQLError]
515
```
516
517
#### Usage Example
518
519
```python
520
from graphql import assert_valid_name, is_valid_name_error
521
522
# Valid names
523
assert_valid_name('User') # Returns 'User'
524
assert_valid_name('_internal') # Returns '_internal'
525
526
# Invalid names
527
try:
528
assert_valid_name('123Invalid') # Raises GraphQLError
529
except GraphQLError as e:
530
print(e.message)
531
532
error = is_valid_name_error('invalid-name')
533
if error:
534
print(error.message) # Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "invalid-name" does not.
535
```
536
537
## Types
538
539
```python { .api }
540
# Import required types
541
from typing import Any, Dict, List, Optional, Union, Callable, TypedDict
542
from graphql.type import GraphQLSchema, GraphQLType, GraphQLNamedType, GraphQLInputType, GraphQLCompositeType, GraphQLObjectType, GraphQLDirective, GraphQLField, GraphQLEnumValue
543
from graphql.language import DocumentNode, Source, Node, ValueNode, TypeNode, OperationDefinitionNode
544
from graphql.error import GraphQLError
545
546
# Schema construction types
547
SchemaPrintOptions = class SchemaPrintOptions
548
IntrospectionOptions = class IntrospectionOptions
549
IntrospectionQuery = Dict[str, Any]
550
551
# Type analysis types
552
TypeInfo = class TypeInfo
553
TypeInfoVisitor = class TypeInfoVisitor
554
555
# Schema comparison types
556
BreakingChange = class BreakingChange
557
DangerousChange = class DangerousChange
558
BreakingChangeType = Enum
559
DangerousChangeType = Enum
560
561
# Value coercion types
562
class CoercionResult:
563
value: Any
564
errors: List[GraphQLError]
565
```