Graphene: Allow separate schema definition & implementation

Created on 28 Oct 2017  路  3Comments  路  Source: graphql-python/graphene

I would like to separate my Graphene schema definition from its implementation.

  • By "definition" I mean the names of my object types, their fields, and custom scalar types.
  • By "implementation" I mean the resolve_* functions and mutate functions.

(Why do I want to do this? Lots of reasons. First, it's super nice to be able to reference all the types and documentation in one place -- putting code in there too makes it far less readable. Second, the implementation requires fancy imports, like accessing my database, that the definition doesn't -- it's nice to be able to import the schema without being forced to import all the database code.)

Currently Graphene makes this quite a challenge.

Example of what I want to do --

(in graphene_models.py:)

class User(ObjectType):
    name = String()

class Signup(Mutation):
    class Arguments:
        name = String()
    id = ID()

(in graphene_impl.py:)

@resolver_for(User, 'name')
def resolve_user_name(self, info):
    return 'Cam Newton'

@mutator_for(Signup)
def mutate_signup(self, info, name):
    # do something fancy with the db
    return Signup(id=...)

As things stand today, I can do this, but it's terribly awkward:

  • Write the resolver_for decorator to monkeypatch the function into, e.g., User.resolve_name. This only works if I delay the Schema construction till after the implementation module is imported, because it's the TypeMap's job to figure out the resolve functions for the fields and that happens at schema construction time.
  • For mutations, I had to look at the resolveinfo's field name and use that to figure out what class's resolver was trying to be executed, and then look for a mutator on that class. (This because the Mutation base class statically binds the mutate method at class creation time, so it's not overridable.)

I'm mainly curious about whether people agree that having the ability to separate declaration and implementation is desirable, and if so, whether there are any better ways to do it than this, or if there are any plans to improve the situation.

Thanks for your consideration and thanks for writing a very nice piece of software!

Most helpful comment

One potential win from separating the two is being able to define types with the GraphQL Schema Definition Language for easier interop with clientside code, while defining resolvers in Python-land.

All 3 comments

Hi,

personally I don't see the big win in separating these things, when it comes to readability I'd almost argue the opposite is true. But anyway if you want to separate type declarations from resolve logic I guess you could do like this:

class UserResolver(object):
    def resolve_full_name(self, info):
        return 'John Doe'

class UserObject(UserResolver, ObjectType):
    name = graphene.String()

(or the other way around: UserResolver is ObjectType and inherits from UserObject)

A better way to decouple your data fetching logic I think would be to make separate functions that you use inside the resolvers. (See starwars example https://github.com/graphql-python/graphene/tree/master/examples/starwars)

One potential win from separating the two is being able to define types with the GraphQL Schema Definition Language for easier interop with clientside code, while defining resolvers in Python-land.

@lincolnq all fields take a resolver parameter so your example can become (without the need for decorators):

# graphene_models.py

from graphene_impl import resolve_user_name, mutate_signup

class User(ObjectType):
    name = String(resolver=resolve_user_name)

class Signup(Mutation):
    class Arguments:
        name = String()
    id = ID()

class Mutations(ObjectType):
    signup = Signup.Field(resolver=mutate_signup)

Otherwise @HeyHugo 's suggestion would work. Also https://github.com/graphql-python/graphene/issues/448#issuecomment-356777997 has some code that would allow you define your server using the GraphQL Schema Definition Language like graphql-tools.

If any of these solutions don't work for you do say and I'll open this issue again.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dfee picture dfee  路  4Comments

tricoder42 picture tricoder42  路  4Comments

ghoshabhi picture ghoshabhi  路  3Comments

mandx picture mandx  路  4Comments

japrogramer picture japrogramer  路  4Comments