4

I'm trying to wrap my head around GraphQL/Relay and I'm finding hard to get started on how to correctly setup a Relay compliant GraphQL API using Ruby on Rails.

I've found multiple tutorials on how to do this:

https://medium.com/react-weekly/relay-facebook-on-rails-8b4af2057152#.gd8p6tbwi

https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-getting-started-955a49d251de#.m05xjvi82

But they all refer to a graphql-relay gem that doesn't seem to be available at this moment: https://github.com/rmosolgo/graphql-relay-ruby

The grahql-ruby gem has a section in the documentation specific to relay, but I'm finding hard to understand what is needed to set this up to be consumed by a Relay client.

What is necessary to implement a GraphQL API for a Relay client in Rails?

Carlos Martinez
  • 4,350
  • 5
  • 32
  • 62

2 Answers2

17

I just want to leave here my findings for anyone who gets stuck with this in the future and wants to get pointed to a better direction.

First, the graphql-ruby gem includes everything that is needed to implement a Relay compatible GraphQL API. In includes everything that was before in the graphql-relay gem.

You need to provide 2 things in your Schema in order to make the Relay refetching feature to work well, an id_from_object method that converts an object in your domain, into a global id and also a object_from_id method that will decode the global id into an object in your application:

ApplicationSchema = GraphQL::Schema.define do
  /* Create IDs by joining the type name & ID, then base64-encoding it */
  id_from_object ->(object, type_definition, query_ctx) {
    GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
  }

  object_from_id ->(id, query_ctx) {
    type_name, object_id = GraphQL::Schema::UniqueWithinType.decode(id)
    # Now, based on `type_name` and `id`
    # find an object in your application 
    # This will give the user access to all records in your db
    # so you might want to restrict this properly
    Object.const_get(type_name).find(object_id)
  }
end

Also, all your types should implement the NodeInterface provided by the ruby gem, and expose a global_id_field instead of an ID type:

PostType = GraphQL::ObjectType.define do
  name "Post"
  # Implements the "Node" interface for Relay
  interfaces [GraphQL::Relay::Node.interface]
  # exposes the  global id
  global_id_field :id
  field :name, types.String
end

This will allow Relay to refetch data like this:

query {
  node(id: "RmFjdGlvbjox") {
    id
    ... on Post {
      name
    }
  }
}

Relay also uses a babel-relay-plugin which requires a schema.json to be generated and available to the client, if you're building an isolated API with no view rendering, the way to go is to let the clients fetch the schema and not to do that work in the server, something like apollo-codegen can work. However, if you are building a rails app and need the schema in the same app, then you can run an instrospection query and save the result to a json file using a rake task:

Schema.execute GraphQL::Introspection::INTROSPECTION_QUERY

Lastly, you'll need to understand that Relay expresses one-to-many relationships with connections:

PostType = GraphQL::ObjectType.define do
  # default connection
  # obj.comments by default
  connection :comments, CommentType.connection_type

  # custom connection
  connection :featured_comments, CommentType.connection_type do
    resolve ->(post, args, ctx) {
      comments = post.comments.featured

      if args[:since]
        comments = comments.where("created_at >= ?", since)
      end

      comments
    }
  end
end

Connections support some arguments out of the box, you can use first, last, before and after in your connection queries:

query {
  posts(first: 5) {
    edges {
      node {
        name
      }
    }
  }
}

All of this is documented in the Relay documentation so make sure you read it as well as the graphql-ruby documentation.

Carlos Martinez
  • 4,350
  • 5
  • 32
  • 62
-3

have you tried installing it?

vagrant$ bundle install
Fetching gem metadata from https://rubygems.org/............
Fetching version metadata from https://rubygems.org/...
Fetching dependency metadata from https://rubygems.org/..
Resolving dependencies...
Installing graphql 0.19.4
Using bundler 1.11.2
Installing graphql-relay 0.12.0
Bundle complete! 1 Gemfile dependency, 3 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.

in Gemfile:

gem 'graphql-relay'
korada
  • 576
  • 4
  • 10
  • 1
    yes it does work, but it uses an old version of `graphql`. I found out that `graphql-ruby` and `graphql-relay` were merged. I'm trying to get more comfortable building the api to give a better answer to this question, as it is definitely a pain to get started with it – Carlos Martinez Feb 14 '17 at 17:34