module Types
class PostType < Types::BaseObject
field :id, ID, null: false
field :title, String, null: false
field :body, String, null: false
field :excerpt, String, null: true
field :published_at, GraphQL::Types::ISO8601DateTime, null: true
field :author, Types::UserType, null: false
field :comments, [Types::CommentType], null: false
field :views, Integer, null: false
field :likes_count, Integer, null: false
def excerpt
object.body&.truncate(200)
end
def comments
# Use dataloader to batch load comments
dataloader.with(Sources::AssociationSource, Comment, :post_id).load(object.id)
end
end
end
module Types
class QueryType < Types::BaseObject
field :posts, [Types::PostType], null: false do
argument :first, Integer, required: false, default_value: 20
argument :offset, Integer, required: false, default_value: 0
end
field :post, Types::PostType, null: true do
argument :id, ID, required: true
end
def posts(first:, offset:)
Post.published.includes(:author).limit(first).offset(offset)
end
def post(id:)
Post.find(id)
end
end
end
GraphQL provides clients flexibility to request exactly the data they need, reducing over-fetching and under-fetching compared to REST. The graphql-ruby gem integrates GraphQL into Rails with a schema-first approach. I define types for each model, fields for attributes, and resolvers for custom logic. The N+1 query problem is more pronounced in GraphQL, so I use graphql-batch for automatic batching and caching of database queries. Authorization happens at the field level using policies or pundit integration. GraphQL's introspection enables powerful tooling like GraphiQL for exploration. The trade-off is increased complexity—REST is simpler for straightforward CRUD, but GraphQL shines for complex frontend requirements with varied data needs.