Table of Contents
Alternatives
Here you will see all the possible ways you can define your schema fields. Keep in mind that this was developed to provide SRP and DRY principles gracefully.
The examples will focus on queries, but you can do the same with mutations and subscriptions.
Direct Definition
Directly defining your fields means that you will add them directly into your schema. This is the simplest way, but the one you should use as less as possible.
# app/graphql/app_schema.rb
query_fields do
field(:rails_version, :string).resolve { Rails.version }
end
This is a great example of a direct definition of a field. The value is immutable, and there is no reason to add a method. A direct resolve block is enough.
# app/graphql/app_schema.rb
query_fields do
field(:me, 'User').resolve { context.current_user }
end
This is also an excellent example because there is no logic involved in resolving the field’s value, and there is no need to add more code than this.
This approach is not recommended for mutations in any case.
Read more about fields and recommendations.
Set Definition
This is one level higher. When a set of fields share a common ground, you can define a class where they will all live. Then you can add shared methods and the list of fields. Just remember to import this as a dependency in your schema.
# app/graphql/queries/migrations_set.rb
class GraphQL::Queries::MigrationsSet < GraphQL::QuerySet
field :last_migration, :int, null: false
field :all_migrations, :int, null: false, array: true
field :needs_migration, :boolean, null: false
def last_migration
context.current_version
end
def all_migrations
context.get_all_versions
end
def needs_migration
context.needs_migration?
end
private
def context
ActiveRecord::Base.connection.migration_context
end
end
Read more about local dependencies.
Available Classes
GraphQL::FieldSet
GraphQL::QuerySet
GraphQL::MutationSet
GraphQL::SubscriptionSet
Standalone Definition
This is one level even higher. When the complexity of a field is so significant that it requires several methods and several parts to get its resolution, you can define a single class for a single field. If you are used to using Rails Services, you might feel at home here.
# app/graphql/queries/migrations_set.rb
class GraphQL::Queries::Permissions < GraphQL::Query
desc <<~DESC
Returns all the actions that the current can do
in the given section provided as an argument.
DESC
argument :section, null: false
returns :string, array: true
delegate :current_user, to: :context
def resolve
# ...
end
# ...
end
You will also need to import these kinds of classes into your schema.
Note For mutations, besides the
resolve
method, you also have theperform
method as entry point. Read more about it here.
Available Classes
GraphQL::Field
GraphQL::Query
GraphQL::Mutation
GraphQL::Subscription
Source Definition
This is the highest level you can get. This approach combines several things into one place. Think of it as the abstraction level of the definition process. Instead of defining each query field, plus mutations, plus object type, plus input type, sources can translate other classes into several GraphQL things.
Sources are considered the highest level because instead of writing each individual element, you would write a translator. Once you have the translator, then all the objects that fall into the same patterns can all be threaded the same way.
One great example is ActiveRecord, which already has its source implemented in this gem. All your models can be easily turned into all their counterparts in GraphQL.
Note Different from the previous examples, sources do not require importing because they have their own mechanism for publishing their fields.
# app/graphql/sources/user_source.rb
class GraphQL::UserSource < GraphQL::ActiveRecordSource
build_all
end
# OR
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
source User
end
end
The above will produce something like:
schema {
query: _Query
mutation: _Mutation
}
type _Query {
users: [User!]!
user(id: ID!): User!
}
type _Mutation {
createUser(user: UserInput!): User!
updateUser(id: ID!, user: UserInput!): User!
deleteUser(id: ID!): Boolean!
}
type User {
# ... All user fields
}
input UserInput {
# ... All user fields as input
}
Sources are one the most powerful features of this gem.
Read more about sources.