# GraphQL : GraphQL-core 3 ## Overview A Python port of GraphQL.js, the JavaScript reference implementation for GraphQL. > [!NOTE] Version Correlations > GraphQL-core v3.2.4 is up-to-date with GraphQL.js v16.8.2 and supports Python version 3.6 to 3.12. > > Changes in the major version of GraphQL.js are reflected in the minor version of GraphQL-core. This means there can be breaking changes in the API when the minor version changes, and only patch releases are fully backward compatible. Therefore, we recommend something like `=~ 3.2.0` as version specifier. ```bash $ pip install graphql-core ``` **Components** - a type system that you define - a query language that you use to query the API - an execution and validation engine #### Imports ```python graphql.error. GraphQLError graphql.execution. ExecutionContext graphql.execution. Middleware graphql.execution. subscribe graphql.graphql. graphql # coro that accepts coros (and *only* coros?) graphql.graphql. graphql_sync # all resolvers must be sync functions graphql.language. DocumentNode graphql.language. Source graphql.language. parse graphql.type. GraphQLArgument graphql.type. GraphQLBoolean graphql.type. GraphQLEnumType graphql.type. GraphQLEnumValue graphql.type. GraphQLField graphql.type. GraphQLFloat graphql.type. GraphQLID graphql.type. GraphQLInputField graphql.type. GraphQLInputObjectType graphql.type. GraphQLInt graphql.type. GraphQLInterfaceType graphql.type. GraphQLList graphql.type. GraphQLNonNull graphql.type. GraphQLObjectType graphql.type. GraphQLScalarType graphql.type. GraphQLSchema graphql.type. GraphQLString graphql.type. GraphQLUnionType graphql.utilities. build_ast_schema graphql.utilities. build_client_schema graphql.utilities. build_schema graphql.utilities. extend_schema graphql.utilities. get_introspection_query graphql.utilities. print_schema graphql.validation. validate ``` ## Schemas ``` str -> SDL DocumentNode -> AST; result of parse() GraphQLSchema -> tree of type objects; result of build_schema() ``` ```python schema = GraphQLSchema( query = GraphQLObjectType( name = "RootQueryType" fields = { "hello": GraphQLField( GraphQLString resolve = lambda obj, info: "world" ) } ) ) sdl = """...""" schema = build_schema( sdl ) -> GraphQLSchema doc = parse( sdl ) -> Document # works with schemas and queries schema = build_ast_schema( doc ) -> GraphQLSchema sdl = print_schema( schema ) -> str ``` #### Extending ```python schema = extend_schema( schema, parse( more_sdl ) ) ``` ## Resolvers - Resolvers can return a value, a coro, or a list of these. - You don’t need to provide resolvers for simple attribute access or for fetching items from Python dictionaries. #### Defining The standard args (obj+info) are passed as positional, field args are passed as kwargs. - obj -> root | parent object - info -> GraphQLResolveInfo — contains info about query execution state + "context" dict with per-request state (like auth) ```python [async] def resolve_foo( obj, info, ... ): ... ``` #### Binding ```python # Including in type definition: GraphQLField( GraphQLString, resolve=resolve_foo ) # Manually attaching to parsed schema: schema.query_type.fields[ "hero" ].resolve = my_func1 schema.get_type( "Character" ).resolve_type = my_func2 # Manually populate enum values in parsed schema: for name, value in schema.get_type( "Episode" ).values.items(): value.value = EpisodeEnum[ name ].value ``` #### Resolvers on Objects Instead of attaching resolver functions to the schema, you can define resolver methods on the resolved objects, starting with the `root_value` object that you can pass to the `graphql()` function when executing a query. ```python class Root: def hero( self, info, episode ): return luke if episode == 5 else artoo def human( self, info, id ): return human_data.get( id ) def droid( self, info, id ): return droid_data.get( id ) graphql_sync( schema, "...", Root() ) ``` ## Execution ```python await graphql( ... ) -> ExecutionResult graphql_sync( ... ) -> ExecutionResult schema # GraphQLSchema source # str | graphql.language.Source (query) root_value = None # Any context_value = None # Any; will be attached to `info` resolver arg variable_values = None # { str -> Any } operation_name = None # str; only needed if `source` contains multiple operations field_resolver = None # GraphQLFieldResolver type_resolver = None # GraphQLTypeResolver middleware = None # Middleware execution_context_class = None # ExecutionContext # only graphql() is_awaitable = None # Callable( Any, bool ) # only graphql_sync() check_sync = False # field_resolver – A resolver function to use when one is not provided by the schema. # If not provided, the default field resolver is used (which looks for a value or method on the source value with the field’s name). # type_resolver – A type resolver function to use when none is provided by the schema. # If not provided, the default type resolver is used (which looks for a `__typename` field or alternatively calls the `is_type_of()` method). ``` #### Results ```python ExecutionResult( data={ "hello": "world" }, errors=None ) ExecutionResult( data = None errors = [ GraphQLError( "Cannot query field 'mello' on type 'RootQueryType'." locations = [ SourceLocation( line=1, column=3 ) ] ) ] ) ExecutionResult( data = { "hero": { "name": "Luke Skywalker", "secretBackstory": None } } errors = [ GraphQLError( "secretBackstory is secret" locations = [ SourceLocation( line=5, column=9 ) ] path = [ "hero", "secretBackstory" ] ) ] ) ``` ## Subscriptions You'll need to maintain a persistent channel to the client (often realized via WebSockets) to push these results back. ```python subscribe() -> aiter: ExecutionResult ``` ## Building a Type Schema The example from the docs. #### SDL ``` enum Episode { NEWHOPE, EMPIRE, JEDI } interface Character { id : String! name : String friends : [ Character ] appearsIn : [ Episode ] } type Human implements Character { id : String! name : String friends : [ Character ] appearsIn : [ Episode ] homePlanet : String } type Droid implements Character { id : String! name : String friends : [ Character ] appearsIn : [ Episode ] primaryFunction : String } type Query { hero( episode: Episode ): Character human( id: String! ): Human droid( id: String! ): Droid } ``` #### enum Episode ```python episode_enum = GraphQLEnumType( "Episode" { "NEWHOPE" : 4 "EMPIRE" : 5 "JEDI" : 6 } description = "One of the films in the Star Wars Trilogy" ) -or- episode_enum = GraphQLEnumType( "Episode" { "NEWHOPE" : GraphQLEnumValue( 4, description="Released in 1977." ), "EMPIRE" : GraphQLEnumValue( 5, description="Released in 1980." ), "JEDI" : GraphQLEnumValue( 6, description="Released in 1983." ) } description = "One of the films in the Star Wars Trilogy" ) -or- class EpisodeEnum( enum.Enum ): NEWHOPE = 4 EMPIRE = 5 JEDI = 6 episode_enum = GraphQLEnumType( "Episode" EpisodeEnum description = "One of the films in the Star Wars Trilogy" ) ``` #### interface Character ```python character_interface = GraphQLInterfaceType( "Character" lambda: dict( id = GraphQLField( GraphQLNonNull( GraphQLString ), description="The id of the character." ) name = GraphQLField( GraphQLString, description="The name of the character." ) friends = GraphQLField( GraphQLList( character_interface ), description="The friends of the character, or an empty list if they have none." ) appearsIn = GraphQLField( GraphQLList( episode_enum ), description="Which movies they appear in." ) secretBackstory = GraphQLField( GraphQLString, description="All secrets about their past." ) ) resolve_type = get_character_type description = "A character in the Star Wars Trilogy" ) ``` > [!NOTE] Thunks > Note that we did not pass the dictionary of fields to the GraphQLInterfaceType directly, but using a lambda function (a so-called “thunk”). This is necessary because the fields are referring back to the character interface that we are just defining. Otherwise, you can pass everything directly. #### type Human ```python human_type = GraphQLObjectType( "Human" lambda: dict( id = GraphQLField( GraphQLNonNull( GraphQLString ), description="The id of the human." ) name = GraphQLField( GraphQLString, description="The name of the human." ) friends = GraphQLField( GraphQLList( character_interface ), resolve=get_friends, description="The friends of the human, or an empty list if they have none." ) appearsIn = GraphQLField( GraphQLList( episode_enum ), description="Which movies they appear in." ) homePlanet = GraphQLField( GraphQLString, description="The home planet of the human, or null if unknown." ) secretBackstory = GraphQLField( GraphQLString, resolve=get_secret_backstory, description="Where are they from and how they came to be who they are." ) ), interfaces = [ character_interface ] description = "A humanoid creature in the Star Wars universe." ) ``` #### type Droid ```python droid_type = GraphQLObjectType( "Droid" lambda: dict( id = GraphQLField( GraphQLNonNull( GraphQLString ), description="The id of the droid." ) name = GraphQLField( GraphQLString, description="The name of the droid." ) friends = GraphQLField( GraphQLList( character_interface ), resolve=get_friends, description="The friends of the droid, or an empty list if they have none." ) appearsIn = GraphQLField( GraphQLList( episode_enum ), description="Which movies they appear in." ) secretBackstory = GraphQLField( GraphQLString, resolve=get_secret_backstory, description="Construction date and the name of the designer." ) primaryFunction = GraphQLField( GraphQLString, description="The primary function of the droid." ) }, interfaces = [ character_interface ] description = "A mechanical creature in the Star Wars universe." ) ``` #### type Query ```python query_type = GraphQLObjectType( "Query" lambda: dict( hero = GraphQLField( character_interface, resolve=get_hero, args=dict( episode = GraphQLArgument( episode_enum, description="If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode." ) ) ) human = GraphQLField( human_type, resolve=get_human, args=dict( id = GraphQLArgument( GraphQLNonNull( GraphQLString ), description="id of the human" ) ) ) droid = GraphQLField( droid_type, resolve=get_droid, args=dict( id = GraphQLArgument( GraphQLNonNull( GraphQLString ), description="id of the droid" ) ) ) ) ) ```