Graphql-ruby: Dynamic Generation in new class api

Created on 17 Apr 2018  路  6Comments  路  Source: rmosolgo/graphql-ruby

Hi Guys

Since I have been a big user of this API but haven't contributed much I thought I share some of the new helpers I have wrote. @rmosolgo happy to create a pull request to added them to documentation if you think these are good, but happy for feedback either way.

I am mostly using Relay these are very much relay centric, also I really don't like declaring my types in my model classes I rather have simple way to extend model class into graphql type

class BaseObject < GraphQL::Schema::Object
  implements GraphQL::Relay::Node.interface
  global_id_field :id

  def self.field(*args, **kwargs, &block)
    # Note(hemantv): this is for historical reason GraphqlRuby used not to camelize
    # fields
    kwargs[:camelize] ||= false
    super
  end

end
class BaseModelObject < BaseObject
  def self.setup(model_name, included_fields)
    model = model_name.to_s.classify.constantize

    model_fields = model.columns_hash.except('id')

    exposed_fields = model_fields.select do |column|
      included_fields.include?(column.to_sym) || included_fields.include?(column.to_s)
    end

    # Make the fields available for this model
    exposed_fields.each do |column, type|
      field column, convert_type(type), null: true
    end

    field :errors, [String], null: true,
      resolve: ->(obj, _args, _ctx) { obj.errors.full_messages }

    field :model_id, Int, null: false,
      resolve: ->(obj, _args, _ctx) { obj.id }
  end

  private
  def self.convert_type(database_type)
    case database_type.type
      when :integer
        Int
      when :number
        Int
      when :decimal
        Float
      when :boolean
        Boolean
      else
        String
    end
  end
end

example usage

class DemoType < BaseModelObject
  setup :demo, %i[name email company_name]
end

Most helpful comment

btw love the new class-based API 馃挴

All 6 comments

btw love the new class-based API 馃挴

That's really cool! Thanks for sharing, I'm happy to see examples like that, where the new API empowers some really creative systems :D

My only suggestion would be to remove the resolve: procs where possible. Sometime in the future, execution may not support that kind of function. Fortunately in this particular case, the refactor is straightforward:

-   field :errors, [String], null: true,
-     resolve: ->(obj, _args, _ctx) { obj.errors.full_messages }
+   field :errors, [String], null: true 
+   # Add a method for implementing this field:
+   define_method(:errors) { object.errors.full_messages)

    field :model_id, Int, null: false,
+     method: :id # this will call `object.id`  
-     resolve: ->(obj, _args, _ctx) { obj.id }

Thanks for sharing these examples! It will be nice to have them here, and if more folks think these would be good to add to the website, I'm open to it. But first, we should see if any other uses emerge after the stable release.

@fameoflight Thanks for sharing, it helps with migration. Any way to disable camelize for arguments?

you might be able to do

def self.argument(*args, **kwargs, &block)
    # Note(hemantv): this is for historical reason GraphqlRuby used not to camelize
    # fields
    kwargs[:camelize] ||= false
    super
  end

@fameoflight Thanks it works with BaseInputObject.

Still couldn't use it with BaseObject and argument nested within field

    class PageQueryType < Types::BaseObject
      field :pages, [Page], null: false, resolve: Graph::Resolvers::Pages do
        argument :per_page, Integer, default_value: 20, required: false #, camelize: false
      end
    end

Would that be possible to use such approach to make dynamic resolvers and mutations ?

Was this page helpful?
0 / 5 - 0 ratings