Graphql-ruby: how to do selections on a json response

Created on 7 Mar 2018  路  5Comments  路  Source: rmosolgo/graphql-ruby

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.

Most helpful comment

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?

All 5 comments

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 ?

Was this page helpful?
0 / 5 - 0 ratings