GraphQL Decorate
graphql-decorate adds an easy-to-use interface for decorating types in graphql-ruby. It lets
you move logic out of your type files and keep them declarative.
Installation
Add this line to your application's Gemfile:
gem 'graphql-decorate'And then execute:
$ bundle
Or install it yourself as:
$ gem install graphql-decorate
Once the gem is installed, you need to add the plugin to your schema and the integration into your base object class.
class Schema < GraphQL::Schema
use GraphQL::Decorate
end
class BaseObject < GraphQL::Schema::Object
include GraphQL::Decorate::ObjectIntegration
endNote that use GraphQL::Decorate must be included in the schema after query and mutation
so that the fields to be extended are initialized first.
Usage
Basic use case
class Rectangle
attr_reader :length
def initialize(length)
@length = length
end
end
class RectangleDecorator < BaseDecorator
def area
length * 2
end
end
class RectangleType < BaseObject
decorate_with RectangleDecorator
field :area, Int, null: false
endIn this example, the Rectangle type is being decorated with a RectangleDecorator. Whenever a
Rectangle gets resolved in the graph, the underlying object will be wrapped with a
RectangleDecorator. All of the methods on the decorator are accessible on the type.
Decorators
By default, graphql-decorate is set up to work with draper style decorators. These decorators
provide a decorate method that wraps the original object and returns an instance of the
decorator. They can also take in additional metadata.
RectangleDecorator.decorate(rectangle, context: metadata)If you are using a different decorator pattern then you can override this default behavior in the configuration.
GraphQL::Decorate.configure do |config|
config.decorate do |decorator_class, object, _metadata|
decorator_class.decorate_differently(object)
end
endTypes
Two methods are made available on your type classes: decorate_with and decorate_metadata.
Every method that yields the underlying object will also yield the current GraphQL context.
If decoration depends on some context in the current query then you can access it when the field is resolved.
decorate_with
decorate_with accepts a decorator class that will decorate every instance of your type.
class Rectangle < GraphQL::Schema::Object
decorate_with RectangleDecorator
enddecorate_with optionally accepts a block which yields the underlying object. If you have multiple
possible decorator classes you can return the one intended for the underling object.
class Rectangle < GraphQL::Schema::Object
decorate_with do |object, _graphql_context|
if object.length == object.width
SquareDecorator
else
RectangleDecorator
end
end
enddecorate_metadata
If your decorator pattern allows additional metadata to be passed into the decorators, you can
define it here. By default every metadata hash will contain { graphql: true }. This is
useful if your decorator logic needs to diverge when used in a GraphQL context. Ideally your
decorators are agnostic to where they are being used, but it is available if needed.
decorate_metadata yields a GraphQL::Decorate::Metadata metadata instance. It responds to two
methods: unscoped and scoped. unscoped sets metadata for a resolved field. scoped sets
metadata for a resolved field and all of its child fields. unscoped and scoped are expected
to return Hashs.
class Rectangle < GraphQL::Schema::Object
decorate_metadata do |metadata|
metadata.unscoped do |object, _graphql_context|
{
name: object.name
}
end
metadata.scoped do |object, _graphql_context|
{
inside_rectangle: true
}
end
end
endRectangleDecorator will be initialized with metadata { name: <object_name>, inside_rectangle: true, graphql: true }. All child fields of Rectangle will be initialized
with metadata { inside_rectangle: true, graphql: true }.
Combinations
You can mix and match these methods to suit your needs. Note that if unscoped and
scoped are both provided for metadata that scoped will override any shared keys.
class Rectangle < GraphQL::Schema::Object
decorate_with RectangleDecorator
decorate_metadata do |metadata|
metadata.scoped do |object, _graphql_context|
{
name: object.name
}
end
end
endCollections
By default graphql-decorate recognizes Array and ActiveRecord::Relation object types and
decorates every element in the collection. If you have other collection types that should have
their elements decorated, you can add them in the configuration. Custom collection classes must
respond to #map.
GraphQL::Decorate.configure do |config|
config.custom_collection_classes = [Mongoid::Relations::Targets::Enumerable]
endDevelopment
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to
run the tests. You can also run bin/console for an interactive prompt that will allow you to
experiment.
License
The gem is available as open source under the terms of the MIT License.