I have a situation that frontend will send a graphql to backend to fetch the data from another rest service. So the query field is like this:
query_type.rb
field :usersForProject, function: Functions::UsersForProject.new(type: types[Types::UserType])
users_for_project.rb
module Functions
class UsersForProject < GraphQL::Function
attr_reader :type
def initialize(type:)
@type = type
end
argument :projectId, !types.Int, as: :project_id
def call(_, args, _)
data = RestClient.get("https://someservice.com/project/#{args[:project_id]}/users")
JSON.parse(data.body)['users']
end
end
end
user_type.rb
Types::UserType = GraphQL::ObjectType.define do
name 'User'
description 'Represents a user'
field :id, !Types::JsonType
end
Now at this point i get the error as
"exception": "#<NoMethodError: undefined method `id' for #<Hash:0x00007f1f940f29f8>>
Can you guide me what needs to be done or what am i missing ? I'm confused about how to return json object on which selections can be made.
If your resolver returns a hash, you can do:
Types::UserType = GraphQL::ObjectType.define do
name 'User'
description 'Represents a user'
field :id, !Types::JsonType, hash_key: :id
end
See http://www.rubydoc.info/gems/graphql/1.7.13/GraphQL/Field#use-the-hash_key-keyword for more details
Ok for me the string helps like this hash_key: 'id'.
My json_type.rb file is like this:
Types::JsonType = GraphQL::ScalarType.define do
name "JSON"
coerce_input -> (x, ctx) { JSON.parse(x) }
coerce_result -> (x, ctx) { x }
end
I dont understand why i have to parse the json twice: here in the coerce_input and in the call method in functions file here:
def call(_, args, _)
data = RestClient.get("https://someservice.com/project/#{args[:project_id]}/users")
JSON.parse(data.body)['users']
end
Do you know why ?
You don't have to, depending on your API's response you can update your id field's return type to int or string. Only if the id is some JSON object you have to do the coerce_input. If you want to use a string as id, your user type is going to look like:
Types::UserType = GraphQL::ObjectType.define do
name 'User'
description 'Represents a user'
field :id, !types.String, hash_key: 'id'
end
You can also choose to return "real" objects from your resolvers so you don't have to use hash_key.
Something like this would work:
JSON.parse(data.body, object_class: OpenStruct).users
It uses OpenStruct to initialize your objects and that allows you (and this gem) to do user.id instead of user['id']. If you do this, your user type can look like:
Types::UserType = GraphQL::ObjectType.define do
name 'User'
description 'Represents a user'
field :id, !types.String
end
Does this help?
Both approaches are working for me. Any comments about performance when we are dealing with millions of records ?
Use connections!
Most helpful comment
You don't have to, depending on your API's response you can update your
idfield's return type to int or string. Only if the id is some JSON object you have to do the coerce_input. If you want to use a string as id, your user type is going to look like:You can also choose to return "real" objects from your resolvers so you don't have to use hash_key.
Something like this would work:
It uses OpenStruct to initialize your objects and that allows you (and this gem) to do
user.idinstead ofuser['id']. If you do this, your user type can look like:Does this help?