Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide an obvious way to register custom scalar type implementations #20

Closed
itkach opened this issue Jan 16, 2019 · 4 comments
Closed

Comments

@itkach
Copy link

itkach commented Jan 16, 2019

I was following "create schema from SDL" approach in https://cito.github.io/blog/shakespeare-with-graphql/ and wanted to use custom scalar types. Looks like in GraphQL.js custom scalar type implementation can be set by simply assigning in type map (https://stackoverflow.com/questions/47824603/graphql-custom-scalar-definition-without-graphql-tools), so equivalent here would be something like this

schema_src = """
scalar DateTime
...
"""
schema = build_schema(schema_src)
schema.type_map["DateTime] = myscalars.DateTime

This doesn't work though as schema.type_map is not consulted when serializing output types or parsing arguments. The following workaround using extend_schema seems to produce the desired effect of assigning custom scalar type implementation to a scalar name declared in schema and having it used when serializing and parsing:

import typing
import graphql.language
from graphql import GraphQLScalarType
from graphql import GraphQLSchema
from graphql.utilities import extend_schema

def register_scalar_types(schema: GraphQLSchema, types: typing.List[GraphQLScalarType]):
    for scalar_type in types:
        schema.type_map[scalar_type.name] = scalar_type

    #using a name that already exists in the schema is an error
    #so need to make something up
    dummy_scalar_name = "_extension_dummy"
    extended_schema = extend_schema(
        schema, graphql.language.parse("scalar %s" % dummy_scalar_name)
    )
    #this scalar we extended schema with is not used though
    del extended_schema.type_map[dummy_scalar_name]
    return extended_schema

and then use it like this:

schema = register_scalar_types(schema, [myscalars.DateTime])

Surely this is a hack and there has to be a better way?

@Cito
Copy link
Member

Cito commented Mar 10, 2019

Hi @itkach, sorry for the late answer.

The reason why it works in the GraphQL.js example is because it sets the type like this:

Object.assign(schema._typeMap.MyScalar, MyScalar)

Note that this is different from simply setting _typeMap.MyScalar = MyScalar, because it keeps the MyScalar object and replaces only its properties. So all references to this type will see the changes as well. The direct equivalent in Python would be doing this:

schema.type_map["MyScalar"].__dict__.update(MyScalar.__dict__)

Or, you can do it like this:

my_scalar = schema.type_map["MyScalar"]
my_scalar.serialize = ...
my_scalar.pare_literal = ...
my_scalar.pare_value = ...

Or, create a function that copies these methods from one GraphQLScalarType to another.

@Cito Cito closed this as completed Mar 10, 2019
@itkach
Copy link
Author

itkach commented Mar 11, 2019

@Cito Thank you for the explanation, I see now how it works. It's unfortunate that GraphQL.js thinks that mutating guts of an object inside of a not-quite-public other object is an acceptable API. I understand that graphql-core-next is a port, but perhaps there's a room for a better API here?

@Cito
Copy link
Member

Cito commented Mar 11, 2019

@itkach There is actually a better API for creating executable schemas from SDL, but it's in graphql-tools which is not part of GraphQL.js. It provides a function makeExecutableSchema that can add the resolvers to a schema given by SDL, and also allows using custom Scalar and Enum types.

It would be good to have something like that in Python, either as a separate package like graphql-tools or as a subpackage of graphql-core-next. I will create a ticket to move that forward.

@Cito
Copy link
Member

Cito commented Mar 11, 2019

But note that you can of course build executable schemas directly in graphql-core-next using the provided classes, as I have shown in the blog article above. This is considered the primary way of building them in graphql-core-next.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants