GRAPHQL on RAILS
Mexico on Rails, Mexico City, Mexico
May 14, 2019
- Brazilian
- Bachelor and Master on Computer Science (UFBA)
- Ruby On Rails since 2008
- http://ca.ios.ba
- Meedan Software Engineer since 2011
- San Francisco - California
- http://meedan.com
Check
Social media fact-checking
2012 - today
github.com/meedan/checkdesk
github.com/meedan/check-api
github.com/meedan/check-web
github.com/meedan/lapis
github.com/meedan/generator-keefer
Backend
Frontend
Ruby On Rails template
Yeoman generator
graphql
react.js
relay
ruby on rails
graphql
react.js
relay
ruby on rails
BACKEND
FRONTEND
GraphQL
"GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data"
REST
Media
Comment
Tag
1
*
User
*
*
*
1
GET /api/v1/medias/1
GET /api/v1/medias/1/comments
GET /api/v1/medias/1/tags
GET /api/v1/medias/1/comments/1
GET /api/v1/users/1?fields=avatar,name
GET /api/v1/users/2?fields=avatar,name
GET /api/v1/users/3?fields=avatar,name
...
Reusable endpoints
GET /api/v1/medias/1?include=comments&count=5
GET /api/v1/medias/1?include=comments,tags
&comments_count=5&tags_count=5
GET /api/v1/medias/1?fields=comments(text,date)
&tags(tag)
...
GET /api/v1/media_and_comments/1
GET /api/v1/media_comments_and_tags/1
GET /api/v1/media_comments_tags_and_users/1
GET /api/v1/medias/1?include=comments&count=5
GET /api/v1/medias/1?include=comments,tags
&comments_count=5&tags_count=5
GET /api/v1/medias/1?fields=comments(text,date)
&tags(tag)
...
GET /api/v1/media_and_comments/1
GET /api/v1/media_comments_and_tags/1
GET /api/v1/media_comments_tags_and_users/1
Too many requests!
GraphQL
One endpoint to rule them all
POST /graphql
POST /api/graphql?query=
{
media(id: 1) {
title
embed
tags(first: 3) {
tag
}
comments(first: 5) {
created_at
text
user {
name,
avatar
}
}
}
}
POST /api/graphql?query=
{
media(id: 1) {
title
embed
tags(first: 3) {
tag
}
comments(first: 5) {
created_at
text
user {
name,
avatar
}
}
}
}
Media
Comment
Tag
1
*
User
*
*
*
1
~
POST /api/graphql?query=
{
media(id: 1) {
title
embed
tags(first: 3) {
tag
}
comments(first: 5) {
created_at
text
user {
name,
avatar
}
}
}
}
{
"media": {
"title": "Avangers Hulk Smash",
"embed": "<iframe src=\"...\"></iframe>",
"tags": [
{ "tag": "avengers" },
{ "tag": "operation" }
],
"comments": [
{
"text": "This is true",
"created_at": "2016-09-18 15:04:39",
"user": {
"name": "Ironman",
"avatar": "http://[...].png"
}
},
...
]
}
}
GraphQL
Ruby On Rails
Creating a new Rails API with GraphQL support
$ git clone https://github.com/meedan/lapis.git
$ rails new <name> -m <path to lapis_template.rb>
- No view (headless API)
- REST & GraphQL
- Swagger UI & GraphiQL
- Docker
- Client gem generation
- Webhooks & tokens
Adding GraphQL support to existing application
gem 'graphql'
gem 'graphql-relay'
Gemfile
class GraphqlController < BaseApiController
def create
vars = params[:variables] || {}
query = GraphQL::Query.new(RelayOnRailsSchema, params[:query], variables: vars)
render json: query.result
end
end
app/controllers/graphql_controller.rb (add route too)
RelayOnRailsSchema = GraphQL::Schema.new(query: QueryType, mutation: MutationType)
config/initializers/relay.rb
type Media {
title: String
embed: String
user: User
comments: [Comment]
}
# "Type" to differentiate from models
MediaType = GraphQL::ObjectType.define do
name 'Media'
description 'Media type'
field :title, types.String
field :embed, types.String
field :user do
type UserType
resolve -> (media, _args, _ctx) {
media.user
}
end
connection :comments, ->{ CommentType.connection_type }
do
argument :user_id, types.Int
resolve -> (media, args, ctx) {
media.comments(args[:user_id], ctx[:current_user])
}
end
end
Defining a GraphQL type
app/graph/types/media_type.rb
# "Type" to differentiate from models
# MediaType = GraphQL::ObjectType.define do
# name 'Media'
# description 'Media type'
field :title, types.String
field :embed, types.String
# field :user do
# type UserType
# resolve -> (media, _args, _ctx) {
# media.user
# }
# end
# connection :comments, ->{ CommentType.connection_type }
# do
# argument :user_id, types.Int
#
# resolve -> (media, args, ctx) {
# media.comments(args[:user_id], ctx[:current_user])
# }
# end
# end
Defining a GraphQL type
By default, fields resolve to model instance methods
# "Type" para diferenciar dos models
# MediaType = GraphQL::ObjectType.define do
# name 'Media'
# description 'Media type'
# field :title, types.String
# field :embed, types.String
# field :user do
# type UserType
# resolve -> (media, _args, _ctx) {
# media.user
# }
# end
connection :comments, ->{ CommentType.connection_type }
do
argument :user_id, types.Int
resolve -> (media, args, ctx) {
media.comments(args[:user_id], ctx[:current_user])
}
end
# end
Defining a GraphQL type
Relationships reference connections of other GraphQL types, can receive arguments and have access to the context
Mutations
mutation {
createMedia(
input: {
url: "http://youtu.be/7a_insd29fk"
clientMutationId: "1"
}
)
{
media {
id
}
}
}
Mutations make changes on your server side.
CRUD:
Queries: Read
Mutations:
- Create
- Update
- Delete
# mutation {
createMedia(
# input: {
url: "http://youtu.be/7a_insd29fk"
# clientMutationId: "1"
# }
# )
{
media {
id
}
}
# }
Mutation name
Input parameters
Desired output
CreateMedia = GraphQL::Relay::Mutation.define do
input_field :url, !types.String
return_field :media, MediaType
resolve -> (inputs, ctx) {
media = Media.new
media.url = inputs[:url]
media.save!
}
end
On Rails, a mutation must define the input fields, return fields and a resolution method (what the mutation actually does)
Gracias! :)
slides.com/caiosba/mexico19
http://ca.ios.ba
@caiosba
GraphQL on Rails
By Caio Sacramento