Skip to content

Commit d98fd27

Browse files
committed
init graphql
1 parent ba8a3b6 commit d98fd27

21 files changed

+212
-0
lines changed

Diff for: Gemfile

+3
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,6 @@ end
5454

5555
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
5656
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
57+
58+
gem "graphql", "~> 2.0"
59+
gem 'graphiql-rails', group: :development

Diff for: Gemfile.lock

+6
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ GEM
8383
ffi (1.15.5)
8484
globalid (1.0.0)
8585
activesupport (>= 5.0)
86+
graphiql-rails (1.8.0)
87+
railties
88+
sprockets-rails
89+
graphql (2.0.1)
8690
i18n (1.10.0)
8791
concurrent-ruby (~> 1.0)
8892
jbuilder (2.11.5)
@@ -208,6 +212,8 @@ DEPENDENCIES
208212
bootsnap (>= 1.4.4)
209213
byebug
210214
capybara (>= 3.26)
215+
graphiql-rails
216+
graphql (~> 2.0)
211217
jbuilder (~> 2.7)
212218
listen (~> 3.3)
213219
puma (~> 5.0)

Diff for: app/controllers/graphql_controller.rb

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
class GraphqlController < ApplicationController
2+
# If accessing from outside this domain, nullify the session
3+
# This allows for outside API access while preventing CSRF attacks,
4+
# but you'll have to authenticate your user separately
5+
# protect_from_forgery with: :null_session
6+
7+
def execute
8+
variables = prepare_variables(params[:variables])
9+
query = params[:query]
10+
operation_name = params[:operationName]
11+
context = {
12+
# Query context goes here, for example:
13+
# current_user: current_user,
14+
}
15+
result = BooksSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
16+
render json: result
17+
rescue StandardError => e
18+
raise e unless Rails.env.development?
19+
handle_error_in_development(e)
20+
end
21+
22+
private
23+
24+
# Handle variables in form data, JSON body, or a blank value
25+
def prepare_variables(variables_param)
26+
case variables_param
27+
when String
28+
if variables_param.present?
29+
JSON.parse(variables_param) || {}
30+
else
31+
{}
32+
end
33+
when Hash
34+
variables_param
35+
when ActionController::Parameters
36+
variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables.
37+
when nil
38+
{}
39+
else
40+
raise ArgumentError, "Unexpected parameter: #{variables_param}"
41+
end
42+
end
43+
44+
def handle_error_in_development(e)
45+
logger.error e.message
46+
logger.error e.backtrace.join("\n")
47+
48+
render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500
49+
end
50+
end

Diff for: app/graphql/books_schema.rb

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
class BooksSchema < GraphQL::Schema
2+
mutation(Types::MutationType)
3+
query(Types::QueryType)
4+
5+
# For batch-loading (see https://graphql-ruby.org/dataloader/overview.html)
6+
use GraphQL::Dataloader
7+
8+
# GraphQL-Ruby calls this when something goes wrong while running a query:
9+
def self.type_error(err, context)
10+
# if err.is_a?(GraphQL::InvalidNullError)
11+
# # report to your bug tracker here
12+
# return nil
13+
# end
14+
super
15+
end
16+
17+
# Union and Interface Resolution
18+
def self.resolve_type(abstract_type, obj, ctx)
19+
# TODO: Implement this method
20+
# to return the correct GraphQL object type for `obj`
21+
raise(GraphQL::RequiredImplementationMissingError)
22+
end
23+
24+
# Relay-style Object Identification:
25+
26+
# Return a string UUID for `object`
27+
def self.id_from_object(object, type_definition, query_ctx)
28+
# For example, use Rails' GlobalID library (https://github.com/rails/globalid):
29+
object_id = object.to_global_id.to_s
30+
# Remove this redundant prefix to make IDs shorter:
31+
object_id = object_id.sub("gid://#{GlobalID.app}/", "")
32+
encoded_id = Base64.urlsafe_encode64(object_id)
33+
# Remove the "=" padding
34+
encoded_id = encoded_id.sub(/=+/, "")
35+
# Add a type hint
36+
type_hint = type_definition.graphql_name.first
37+
"#{type_hint}_#{encoded_id}"
38+
end
39+
40+
# Given a string UUID, find the object
41+
def self.object_from_id(encoded_id_with_hint, query_ctx)
42+
# For example, use Rails' GlobalID library (https://github.com/rails/globalid):
43+
# Split off the type hint
44+
_type_hint, encoded_id = encoded_id_with_hint.split("_", 2)
45+
# Decode the ID
46+
id = Base64.urlsafe_decode64(encoded_id)
47+
# Rebuild it for Rails then find the object:
48+
full_global_id = "gid://#{GlobalID.app}/#{id}"
49+
GlobalID::Locator.locate(full_global_id)
50+
end
51+
end

Diff for: app/graphql/mutations/.keep

Whitespace-only changes.

Diff for: app/graphql/mutations/base_mutation.rb

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module Mutations
2+
class BaseMutation < GraphQL::Schema::RelayClassicMutation
3+
argument_class Types::BaseArgument
4+
field_class Types::BaseField
5+
input_object_class Types::BaseInputObject
6+
object_class Types::BaseObject
7+
end
8+
end

Diff for: app/graphql/types/.keep

Whitespace-only changes.

Diff for: app/graphql/types/base_argument.rb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Types
2+
class BaseArgument < GraphQL::Schema::Argument
3+
end
4+
end

Diff for: app/graphql/types/base_connection.rb

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Types
2+
class BaseConnection < Types::BaseObject
3+
# add `nodes` and `pageInfo` fields, as well as `edge_type(...)` and `node_nullable(...)` overrides
4+
include GraphQL::Types::Relay::ConnectionBehaviors
5+
end
6+
end

Diff for: app/graphql/types/base_edge.rb

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Types
2+
class BaseEdge < Types::BaseObject
3+
# add `node` and `cursor` fields, as well as `node_type(...)` override
4+
include GraphQL::Types::Relay::EdgeBehaviors
5+
end
6+
end

Diff for: app/graphql/types/base_enum.rb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Types
2+
class BaseEnum < GraphQL::Schema::Enum
3+
end
4+
end

Diff for: app/graphql/types/base_field.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Types
2+
class BaseField < GraphQL::Schema::Field
3+
argument_class Types::BaseArgument
4+
end
5+
end

Diff for: app/graphql/types/base_input_object.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Types
2+
class BaseInputObject < GraphQL::Schema::InputObject
3+
argument_class Types::BaseArgument
4+
end
5+
end

Diff for: app/graphql/types/base_interface.rb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module Types
2+
module BaseInterface
3+
include GraphQL::Schema::Interface
4+
edge_type_class(Types::BaseEdge)
5+
connection_type_class(Types::BaseConnection)
6+
7+
field_class Types::BaseField
8+
end
9+
end

Diff for: app/graphql/types/base_object.rb

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Types
2+
class BaseObject < GraphQL::Schema::Object
3+
edge_type_class(Types::BaseEdge)
4+
connection_type_class(Types::BaseConnection)
5+
field_class Types::BaseField
6+
end
7+
end

Diff for: app/graphql/types/base_scalar.rb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Types
2+
class BaseScalar < GraphQL::Schema::Scalar
3+
end
4+
end

Diff for: app/graphql/types/base_union.rb

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Types
2+
class BaseUnion < GraphQL::Schema::Union
3+
edge_type_class(Types::BaseEdge)
4+
connection_type_class(Types::BaseConnection)
5+
end
6+
end

Diff for: app/graphql/types/mutation_type.rb

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module Types
2+
class MutationType < Types::BaseObject
3+
# TODO: remove me
4+
field :test_field, String, null: false,
5+
description: "An example field added by the generator"
6+
def test_field
7+
"Hello World"
8+
end
9+
end
10+
end

Diff for: app/graphql/types/node_type.rb

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Types
2+
module NodeType
3+
include Types::BaseInterface
4+
# Add the `id` field
5+
include GraphQL::Types::Relay::NodeBehaviors
6+
end
7+
end

Diff for: app/graphql/types/query_type.rb

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Types
2+
class QueryType < Types::BaseObject
3+
# Add `node(id: ID!) and `nodes(ids: [ID!]!)`
4+
include GraphQL::Types::Relay::HasNodeField
5+
include GraphQL::Types::Relay::HasNodesField
6+
7+
# Add root-level fields here.
8+
# They will be entry points for queries on your schema.
9+
10+
# TODO: remove me
11+
field :test_field, String, null: false,
12+
description: "An example field added by the generator"
13+
def test_field
14+
"Hello World!"
15+
end
16+
end
17+
end

Diff for: config/routes.rb

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
Rails.application.routes.draw do
2+
if Rails.env.development?
3+
mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
4+
end
5+
post "/graphql", to: "graphql#execute"
26
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
37
end

0 commit comments

Comments
 (0)