graphql-core Documentation

repository·main·Indexed 19 days ago

https://github.com/graphql-python/graphql-core

A Python 3.7+ port of the GraphQL.js reference implementation. graphql-core provides the core machinery for building GraphQL schemas and executing queries in Python, featuring support for both synchronous (graphql_sync) and asynchronous (graphql) execution, custom middleware, and a Pythonic API for accessing GraphQL type properties.

Tokens
19.9K
Snippets
58
Records
107
Agent score
67%

What's inside graphql-core

  1. Core capabilities of GraphQL-core

    main

    GraphQL-core is designed to provide two primary capabilities for GraphQL implementations:

    1. Building a type schema: Defining the structure of your data, including types, fields, queries, and mutations.
    2. Serving queries: Executing incoming GraphQL queries against a defined type schema and returning the appropriate data.
  2. Understand resolver function signatures

    main

    A resolver function in GraphQL-core is responsible for returning a value, a coroutine, or a list of these. It accepts two positional arguments:

    1. obj: The root or the resolved parent field.
    2. info: A GraphQLResolveInfo object containing the execution state, including a context attribute for per-request state (like authentication or database sessions).

    Any GraphQL arguments defined on the field are passed to the resolver as individual keyword arguments.

    Note: This signature differs from GraphQL.js; in GraphQL.js, context is a separate argument and arguments are passed as a single object.

  3. Workflow: Convert between Schema, Introspection, and SDL

    main

    GraphQL-core 3 allows for easy conversion between three primary schema representations using the graphql.utilities module:

    1. Built Schema $\rightarrow$ Introspection: Use get_introspection_query to create a query, then execute it via graphql_sync.
    2. Introspection $\rightarrow$ Client Schema: Use build_client_schema on the query result's .data dictionary.
    3. Client Schema $\rightarrow$ SDL: Use print_schema to generate the SDL string.
    from graphql import get_introspection_query, graphql_sync, build_client_schema, print_schema
    
    # 1. Generate query
    query = get_introspection_query(descriptions=True)
    
    # 2. Execute against an existing schema to get introspection data
    introspection_query_result = graphql_sync(schema, query)
    
    # 3. Reconstruct a schema from the data
    client_schema = build_client_schema(introspection_query_result.data)
    
    # 4. Convert reconstructed schema to SDL
    sdl = print_schema(client_schema)
  4. Build a GraphQL type schema

    main

    To use GraphQL-core, you first define a GraphQLSchema. This involves creating a GraphQLObjectType for your root query and defining GraphQLField objects. Each field can have a resolve function that handles the data fetching logic.

    from graphql import (
        GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
    
    schema = GraphQLSchema(
        query=GraphQLObjectType(
            name='RootQueryType',
            fields={
                'hello': GraphQLField(
                    GraphQLString,
                    resolve=lambda obj, info: 'world')
            }))
  5. How GraphQL-core 3 is structured

    main

    GraphQL-core 3 is a Python port of the JavaScript reference implementation and follows the GraphQL specification. It is organized into sub-packages that correspond to the specification's sections:

    • Language: The query language itself.
    • Type System: Defining types and schemas.
    • Introspection: Querying the schema.
    • Validation: Ensuring queries are valid against the schema.
    • Execution: The engine that runs queries.
    • Response: The format of the results.

    Most functionality can be accessed via the top-level graphql package, which acts as a proxy to these sub-packages.

  6. Traverse the AST using a Visitor

    main

    The Visitor pattern allows you to traverse the AST and perform actions on specific nodes. You can implement a custom visitor by subclassing Visitor or ParallelVisitor and overriding the relevant visit methods.

    To control the traversal flow, your visitor methods should return a value from VisitorActionEnum (or its direct exported values):

    • BREAK (or True): Stop all further node visitation immediately.
    • SKIP (or False): Skip the children of the current node but continue with the rest of the tree.
    • REMOVE (or ...): Signal that the current node should be deleted from the tree.
    • IDLE (or None): No additional action is taken; continue normal traversal.
    from graphql.language import Visitor, BREAK
    
    class MyVisitor(Visitor):
        def enter_field(self, node, key, parent, child):
            if node.name.value == 'secret':
                return BREAK  # Stop everything if we find a 'secret' field
            return None
    
    # Use visit(ast, visitor_instance)
  7. Access GraphQL type properties directly

    main

    Unlike GraphQL.js which uses getter methods, graphql-core allows direct attribute access for several key properties on GraphQL types. This makes the API more Pythonic.

    Use direct attribute access for:

    • fields on GraphQLObjectType, GraphQLInterfaceType, and GraphQLInputObjectType (e.g., obj.fields instead of obj.getFields()).
    • interfaces on GraphQLObjectType.
    • types on GraphQLUnionType.
    • values on GraphQLEnumType.
    • query, mutation, subscription, and type_map on GraphQLSchema.
  8. Dependency management and versioning note

    main

    GraphQL-core does not follow SemVer in the same way as GraphQL.js. Major version changes in GraphQL.js are reflected as minor version changes in GraphQL-core. Consequently, breaking changes can occur during minor version updates.

    To ensure stability, it is recommended to use a compatible version specifier such as ~= 3.2.0 in your dependencies.

  9. Understand Incremental Execution for streaming results

    main

    The experimental_execute_incrementally function provides a way to execute queries that can return results in chunks (incremental execution). This is useful for streaming large datasets or using @defer directives. The API uses several specialized classes to manage these states:

    • InitialIncrementalExecutionResult: The first part of an incremental response.
    • SubsequentIncrementalExecutionResult: Parts of the response that follow the initial result.
    • IncrementalResult: A container for the various parts of the incremental stream.
    • IncrementalDeferResult: Specifically handles results related to @defer directives.
  10. How to handle recursive type definitions using thunks

    main

    When defining GraphQL types (like GraphQLInterfaceType or GraphQLObjectType), you may encounter situations where a field refers to a type that is still being defined (e.g., a Character interface having a field friends that returns a list of Character).

    To resolve this circular dependency, you must pass the dictionary of fields as a thunk (a lambda function) rather than a direct dictionary. This allows the GraphQL-core engine to evaluate the fields only after the types have been initialized.

    # Using a lambda (thunk) to allow the interface to refer to itself
    character_interface = GraphQLInterfaceType('Character', lambda: {
        'id': GraphQLField(GraphQLNonNull(GraphQLString)),
        'friends': GraphQLField(GraphQLList(character_interface)), # Recursive reference
        # ... other fields
    }, resolve_type=get_character_type)
  11. Use ValidationContext and ValidationRule to customize validation

    main

    Validation in graphql-core is driven by ValidationRule objects.

    • ValidationRule: The base class for all validation rules.
    • ValidationContext: The base class for managing the state and execution of validation rules.
    • ASTValidationContext: A context specifically designed for validating an Abstract Syntax Tree (AST).
    • SDLValidationContext: A context specifically designed for validating Schema Definition Language (SDL).

    You can extend or customize validation by implementing your own ValidationRule or by providing a specific ValidationContext to the validate function.