GraphQL
If you want to build the app piece by piece clone the repo git clone https://github.com/DerekStride/graphql-workshop.git
and reset to the base rails app with a standard blog app configured git checkout 13cacb5 -b workshop
.
The application at that point has an Article and a Comment ActiveRecord models setup and a few changes in the routes and application config files. It also has some seed data to create some records for us to use.
rake db:migrate
rake db:seed
Step 1 - Setup the GraphQL Controller
Create a controller that will accept our graphql queries, you'll probably need to create an api controller too.
# app/controllers/api_controller.rb
class ApiController < ActionController::Base
protect_from_forgery with: :null_session
end
# app/controllers/graphql_controller.rb
class GraphqlController < ApiController
def create
query_string = params[:query]
query_variables = ensure_hash(params[:variables])
result = AppSchema.execute(query_string, variables: query_variables)
render json: result
end
private
def ensure_hash(query_variables)
if query_variables.blank?
{}
elsif query_variables.is_a?(String)
JSON.parse(query_variables)
else
query_variables
end
end
end
Step 2 - Creating a Schema
Create the AppSchema class that we refer to in the controller above.
# app/graph/app_schema.rb
AppSchema = GraphQL::Schema.define do
query QueryType
end
The schema is the entry point to the API it defines 2 types, a query root for retrieving data and a mutation type for modifying data. First we're going to create the query root.
# app/graph/types/query_type.rb
QueryType = GraphQL::ObjectType.define do
name 'Query'
description 'The query root for this schema'
field :simple do
type SimpleType
argument :id, !types.ID
resolve -> (_, args, _) { OpenStruct.new(data: SecureRandom.uuid, id: args[:id]) }
end
end
We'll also want to define an example type to get up running.
# app/graph/types/simple_type.rb
SimpleType = GraphQL::ObjectType.define do
name 'Simple'
field :id, !types.ID
field :data, !types.String
end
Now we can make a request to our graphql API via the GraphiQL editor at /graphiql
query {
simple(id: 1) {
id
data
}
}
Step 3 - ArticleType and CommentType
Lets add types for our Article model and our Comment model
# app/graph/types/article_type.rb
ArticleType = GraphQL::ObjectType.define do
name 'Article'
description 'An Article'
field :id, !types.ID
field :title, !types.String
field :content, !types.String
end
# app/graph/types/comment_type.rb
CommentType = GraphQL::ObjectType.define do
name 'Comment'
description 'A Comment'
field :id, !types.ID
field :author, !types.String
field :content, !types.String
end
Now we need to add them to the query root so that we can access them.
# app/graph/types/query_type.rb
QueryType = GraphQL::ObjectType.define do
name 'Query'
description 'The query root for this schema'
field :article do
type ArticleType
argument :id, !types.ID
resolve -> (_, args, _) { Article.find(args[:id]) }
end
field :comment do
type CommentType
argument :id, !types.ID
resolve -> (_, args, _) { Comment.find(args[:id]) }
end
field :simple do
type SimpleType
argument :id, !types.ID
resolve -> (_, args, _) { OpenStruct.new(data: SecureRandom.uuid, id: args[:id]) }
end
end
Now try the following query
query {
article(id: 1) {
title
content
}
comment(id: 1) {
author
content
}
}
Step 4 - Associations and GraphQL connections
To represent the has many relationship between articles and comments we need to add a connection field to our Article type and an article field to comments
# app/graph/types/article_type.rb
ArticleType = GraphQL::ObjectType.define do
name 'Article'
description 'An Article'
field :id, !types.ID
field :title, !types.String
field :content, !types.String
connection :comments do
type CommentType.connection_type
resolve -> (article, _, _) { article.comments }
end
end
# app/graph/types/comment_type.rb
CommentType = GraphQL::ObjectType.define do
name 'Comment'
description 'A Comment'
field :id, !types.ID
field :author, !types.String
field :content, !types.String
field :article do
type ArticleType
resolve -> (comment, _, _) { comment.article }
end
end
Now try the following query
query {
simple(id: 1) {
id
data
}
article(id: 1) {
title
content
comments(first: 2) {
edges {
cursor
node {
author
content
}
}
}
}
comment(id: 1) {
author
content
article {
title
}
}
}
Step 5 - Mutations
The first thing we need to do before creating a mutation is add the mutation root to our schema
# app/graph/app_schema.rb
AppSchema = GraphQL::Schema.define do
query QueryType
mutation MutationType
end
# app/graph/types/mutation_type.rb
MutationType = GraphQL::ObjectType.define do
name 'Mutation'
description 'The mutation root for this schema'
field :addArticle, field: AddArticleMutation.field
field :addComment, field: AddCommentMutation.field
end
Next we'll need to create the Add Article and Comment mutation types
# app/graph/types/add_comment_mutation.rb
AddCommentMutation = GraphQL::Relay::Mutation.define do
name 'CreateComment'
input_field :author, !types.String
input_field :content, !types.String
input_field :article_id, !types.ID
return_field :comment, CommentType
resolve -> (_, inputs, _) do
article = Article.find(inputs[:article_id])
{ comment: article.comments.create!(author: inputs[:author], content: inputs[:content]) }
end
end
# app/graph/types/add_article_mutation.rb
AddArticleMutation = GraphQL::Relay::Mutation.define do
name 'CreateArticle'
input_field :title, !types.String
input_field :content, !types.String
return_field :article, ArticleType
resolve -> (_, inputs, _) do
{ article: Article.create!(title: inputs[:title], content: inputs[:content]) }
end
end
Now you can try the following mutations
mutation createArticle {
addArticle(input: { title: "Hello, World!", content: "My first Mutation" }) {
article {
content
title
}
}
}
mutation createComment {
addComment(input: { content: "Great Work!", author: "Me", article_id: 2 }) {
comment {
author
content
article {
title
}
}
}
}