Skip to content

Instantly share code, notes, and snippets.

@bahodge
Created September 25, 2019 13:32
Show Gist options
  • Save bahodge/8958ba90a8b32cb62d018a482f97c4ef to your computer and use it in GitHub Desktop.
Save bahodge/8958ba90a8b32cb62d018a482f97c4ef to your computer and use it in GitHub Desktop.
Creating a custom mutation return payload with Graphql-Rails
# app/graphql/mutations/base_mutation.rb
module Mutations
# This class is used as a parent for all mutations, and it is the place to have common utilities
class BaseMutation < GraphQL::Schema::Mutation
null false
def enforced_policy
...
end
def set_attributes(args)
end
def check_permissions(args)
end
def validate_work(args)
end
def mutate!
end
# this method looks at the mutation,
# finds all of the defined fields
# makes them keys in the hash
# attaches their actual values to those keys
def build_response_payload
response_hash = {}
return_fields = self.class.fields
return_fields.each do |name, value|
if name == 'errors'
response_hash[:errors] = []
next
end
response_hash[name] = self.send(name.to_sym)
end
response_hash
end
# I opted for sequentially calling a series of methods
# that can be overrided by the child mutation class.
def resolve(**args)
begin
set_attributes(args)
check_permissions(args)
validate_work(args)
mutate!
build_response_payload
rescue ::Exceptions::PolicyError => e
# if an exception is raised,
# attach errors to the returning payload
payload = build_response_payload
payload[:errors] = [{type: 'PERMISSION_ERROR', message: e.message}]
payload
end
end
def add_error(new_error: )
errors ||= []
errors << new_error
end
end
end
# app/graphql/mutations/clients/create.rb
module Mutations
class Clients::Create < BaseMutation
# Adding the graphql_name will rename the payload from:
# 'CreatePayload' - without
# 'CreateClientPayload' - with
graphql_name "CreateClient"
argument :name, String, required: true
# These will generate custom payoad that will ultimately return
###
### "data": {
### "createClient": {
### "client": { ... },
### "errors": [...]
### }
### }
###
field :client, Types::ClientType, null: true
field :errors, [Types::ErrorType], null: false
attr_accessor :name, :client
def set_attributes(args)
self.name = args[:name]
end
def check_permissions(args)
enforced_policy.check_policy(test: :is_creator?)
end
def mutate!
self.client = Client.create!(name: self.name)
self.client
end
end
end
// An example mutation using react & gql
import gql from 'graphql-tag'
export default gql`
mutation CREATE_CLIENT($name: String!) {
createClient(name: $name) {
client {
id
}
errors {
type
message
}
}
}
`
# app/graphql/types/error_type.rb
module Types
class ErrorType < BaseObject
graphql_name "ErrorType"
field :message, String, null: false, description: "A description of the error"
field :type, String, null: true, description: "Which input value this error came from"
end
end
import React from 'react';
import { useMutation } from "@apollo/react-hooks";
import CREATE_CLIENT from "../../../graphql/mutations/clients/CreateClientMutation";
const onSubmit = props => {
const [createClientMutation] = useMutation(CREATE_CLIENT);
// for readabillity i'll destructure 1 level at a time
const { data } await createClientMutation({
variables: props.sanitizedValues
});
// It returns 'createClient' because that is what I named it in 'app/graphql/types/mutation_type.rb
const { createClient } = data
const { client, errors } = createClient
if (errors.length > 0) return //some error handling procedure
// Using react router -> Send to the client page
props.history.push(`/clients/${client.id}`);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment