Setting Up A Base GraphQL Server And GraphiQL With Rails 7
Published: Feb 20, 2022
Last updated: Feb 20, 2022
This post will demonstrate a basic example of setting up a basic GraphQL server with the graphql-ruby gem.
The project itself will consist of a GraphQL schema that will demonstrate basic relationship between two models, Post and Comment, where each Post has a one-to-many relationship with the Comment entity.
A rudimentary understanding of GraphQL. I will mainly be operating a high-level of getting it working without explaining the queries.
Getting started
We will use Rails to initialize the project demo-graphql-server, adding the necessary GraphQL gems and then creating some base models and GraphQL objects:
# Create a new rails project
$ rails new demo-graphql-server
$ cd demo-graphql-server
# Add the required gems
$ bundler add graphql graphiql-rails
# Setup our base GraphQL files
$ bin/rails g graphql:install
# Create our models
$ bin/rails g model Post title rating
$ bin/rails g model Comment body post:references
# Make your first object type
$ bin/rails g graphql:object Post title:String rating:Int comments:[Comment]
$ bin/rails g graphql:object Comment body:String post:Post
Note: I am using Rails 7.x for this blog post.
The above does the following:
Create a new Rails project demo-graphql-server.
Add the required gems graphql and graphiql-rails to the project.
Create the base GraphQL files with graphql:install generator that was installed.
Create the models Post and Comment.
Create the GraphQL objects Post and Comment that relate to our models.
Editing our Post model
We need to update our Post model to add the has_many relationship to the Comment model. In the app/models/post.rb file, add the following:
class Post < ApplicationRecord
has_many :comments, dependent: :destroy
end
This lets the model know that we have a one-to-many relationship with the Comment model.
Exploring the generated GraphQL files
Within the app/graphql folder, there will be the base files that we scaffolded out.
The app/graphql/demo_graphql_server_schema.rb adds in our query and mutation type and sets up the schema, while the Post and Comment objects we created can be found under app/graphql/types as the post_type.rb and comment.rb files.
I've updated those files to look like the following.
For app/graphql/types/post_type.rb:
# frozen_string_literal: true
module Types
class PostType < Types::BaseObject
description 'A blog post'
field :id, ID, null: false
field :title, String, null: false
field :rating, Integer, null: false
field :comments, [Types::CommentType]
end
end
For app/graphql/types/comment_type.rb:
# frozen_string_literal: true
module Types
class CommentType < Types::BaseObject
field :id, ID, null: false
field :body, String, null: false
field :post, Types::PostType, null: false
end
end
To enable the resolvers for these queries, we can update our file app/graphql/types/query_type.rb:
module Types
class QueryType < Types::BaseObject
# Add `node(id: ID!) and `nodes(ids: [ID!]!)`
include GraphQL::Types::Relay::HasNodeField
include GraphQL::Types::Relay::HasNodesField
# Add root-level fields here.
# They will be entry points for queries on your schema.
field :posts,
[Types::PostType],
null: false,
description: 'Returns a list of all posts'
def posts
Post.all
end
field :comments,
[Types::CommentType],
null: false
def comments
Comment.all
end
end
end
The above code will help our queries resolve for the Post and Comment objects. The def posts and def comments methods will return all of the posts and comments as our contrived behavior. In practice, you will want to add pagination and filtering.
Creating our seeds
At this point, we are ready to add some seeds to see our GraphiQL interface in action. Add the following to db/seeds.rb:
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
#
# Examples:
#
# movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }])
# Character.create(name: "Luke", movie: movies.first)
# db/seeds.rb
post = Post.create!(
title: 'My first post',
rating: 4
)
post_two = Post.create!(
title: 'A second, less impressive post',
rating: 2
)
Comment.create!(
[
{
body: 'Loved it',
post: post
},
{
body: 'Gr8 m8',
post: post
},
{
body: 'Worst one yet',
post: post_two
},
{
body: 'I want my money back',
post: post_two
},
{
body: 'You could do better',
post: post_two
}
]
)
Here we are creating two different posts, then attaching comments to each of them (two for the first and three for the second respectively).
At this point, we are now ready to run some database migrations and start the server.
Running migrations and starting the server
# Run migrations and seeding
$ bin/rails db:create db:migrate db:seed
# Start the server on port 3000
$ bin/rails s
The above does the following:
Run the migrations and seed the database with db:create, db:migrate and db:seed.
Start the server on port 3000 with bin/rails s.
At this point, we can navigate to http://localhost:3000/graphiql to see our GraphiQL interface and start playing around.
A query to get our posts and comments
I won't go into detail on how the GraphiQL interface works. If you are not familiar, you can check out the Gatsby intro on GraphiQL.
For us, we are going to write in our two queries to see what we get back.
Our first query will be PostsWithComments:
query PostsWithComments {
posts {
id
title
rating
comments {
id
body
}
}
}
If we run this query, we will get back a list of posts and their comments.
{
"data": {
"posts": [
{
"id": "1",
"title": "My first post",
"rating": 4,
"comments": [
{
"id": "1",
"body": "Loved it"
},
{
"id": "2",
"body": "Gr8 m8"
}
]
},
{
"id": "2",
"title": "A second, less impressive post",
"rating": 2,
"comments": [
{
"id": "3",
"body": "Worst one yet"
},
{
"id": "4",
"body": "I want my money back"
},
{
"id": "5",
"body": "You could do better"
}
]
}
]
}
}
Result for our query PostsWithComments
Amazing! With a simple query, we are able to get back all the posts with their related comments.
A query mapping the comments to the posts
GraphQL is all about flexible relationships. A cool demonstration on the power of this is to go the other way and request comments as the primary source of data and attach the related post when required:
query CommentsWithPosts {
comments {
id
body
post {
id
title
rating
}
}
}
If we run our new CommentsWithPosts query, we will get back a list of comments and their related posts.
{
"data": {
"comments": [
{
"id": "1",
"body": "Loved it",
"post": {
"id": "1",
"title": "My first post",
"rating": 4
}
},
{
"id": "2",
"body": "Gr8 m8",
"post": {
"id": "1",
"title": "My first post",
"rating": 4
}
},
{
"id": "3",
"body": "Worst one yet",
"post": {
"id": "2",
"title": "A second, less impressive post",
"rating": 2
}
},
{
"id": "4",
"body": "I want my money back",
"post": {
"id": "2",
"title": "A second, less impressive post",
"rating": 2
}
},
{
"id": "5",
"body": "You could do better",
"post": {
"id": "2",
"title": "A second, less impressive post",
"rating": 2
}
}
]
}
}