Ruby graphql-ruby

From wikinotes
Revision as of 20:49, 5 September 2021 by Will (talk | contribs) (Will moved page Ruby graphql to Ruby graphql-ruby without leaving a redirect)

graphql gem for ruby.

TODO:

needs revisiting

Documentation

wiki graphql
docs https://graphql-ruby.org/guides
API docs https://graphql-ruby.org/api-doc/1.12.8/
github https://github.com/rmosolgo/graphql-ruby
homepage https://graphql-ruby.org/

Basics

Read graphql to get a general understanding of how graphql works before reading this.

Schema

  • schemas are assigned a root object for queries(queryable object), and optionally mutations(operations with side effects).
  • objects expose fields(objects) which can have arguments
  • fields/arguments (exposed methods) are written in snake_case, but converted to camelCase when used in queries.
class MySchema < GraphQL::Schema
  mutation(MyMutationType)  # fields on MyMutationType accessible to schema
  query(MyQueryType)        # fields on MyQueryType accessible to schema
  # ...
end

class MyQueryType
end

class MyMutationType
end

Queries(objects)

field docs https://graphql-ruby.org/fields/introduction
field arg docs https://graphql-ruby.org/fields/arguments

TODO:

split this example into multiple examples to more clearly describe one concept at a time

Objects are described heirarchically starting with a single/root GraphApi::ObjectType which exposes other GraphApi::ObjectType objects as fields
This root object is assigned to the schema using it's method query(YourObject).

class MyGraph < GraphQL::Schema::ObjectType

  # ==========
  # user query
  # ==========
  field :user, User do |field|
    field.argument(:name, String, required: true, description: "...")
    field.argument(:family, FamilyType)
  end

  def user(name, family)
    User.find_by(
      firstname: name, 
      lastname: family[:last_name],  # fields from input graphql types accessible as snake_cased hash-keys
    )
  end

  # =============
  # project query
  # =============
  field :projectDetails, ProjectDetailsType

  def project
    # you may return a hash with keys satisfying return-type
    { name: "myproject", foo: "bar" }
  end
end

class FamilyType
  field :lastName, String   # fields expected in camelCase
end

class User < GraphQL::Schema::ObjectType
  field # ...
end

class ProjectDetailsType < GraphQL::Schema::ObjectType
  field :name, String
  field :foo, String
end

Mutations

Mutations perform jobs with side-effects (like POST, PATCH, DELETE, ...)

class MyMutation < GraphQL::Schema::RelayClassicMutation
  field :foo, String, null: false  # exposes attribute 'foo', of type String, non-nullable
  field :bar, String, null: true

  argument :id, Integer, description: '...'

  # method that runs when mutation is executed
  def resolve(id)
    return { foo: "abc", bar: "def" }  # returns all fields defined on class
  end
end


Syntax

Objects

Fields with Arguments

class User < GraphQL::Schema::Object
  field :id, Integer    # name, returntype
  field :age, Integer   # name, returntype
end

class MyGraph < GraphQL::Schema::Object
  field :user, User, do |field|
    field.argument(:id, Integer, required: true)
  end
end

Mutations

Mutations are graphql fields that trigger side effects.

  • mutations return a hash, that contains all of the fields defined on it.
  • mutations may optionally accept arguments
class MyMutation < GraphQL::Schema::RelayClassicMutation
  argument :username, String, required: true

  field :message, String, null: true    # returns string, allows null
  field :errors, [String], null, false  # returns list of strings, disallows null

  def resolve(username)
    # do thing that produces side effect
    { message: "Hello #{username}",
      errors: [] }
  end
end


class MyMutationRoot < GraphQL::Schema::RelayClassicMutation
  field :mymutation, 
    mutation: MyMutation  # TODO: needs arg??
end


class MyGraph < GraphQL::Schema
  mutation(MyMutationRoot)
end

Connections

Connections between nodes are expressed as edges.
Edges can be abstracted as connections to support query params, filters, pagination, etc.

See https://graphql-ruby.org/relay/connections.html

# Defines a `ClassType`, with the connection `StudentType`.

class ClassType < GraphQL::Schema::Object
  field :students, StudentConnectionType,
    connection: true,
    description: "Students that belong to a class"
end

class StudentConnectionType < GraphQL::Types::Relay::BaseConnection
  edge_type StudentEdgeType
end

class StudentEdgeType < GraphQL::Types::Relay::BaseEdge
  node_type StudentType
end

class StudentType < GraphQL::Schema::Object
  field :id, Integer
  field :age, Integer
  field :username, String
end