Skip to content

Instantly share code, notes, and snippets.

@rmosolgo
Created July 13, 2023 15:29
Show Gist options
  • Save rmosolgo/bd885348da2e39615b0ac55f369ce44c to your computer and use it in GitHub Desktop.
Save rmosolgo/bd885348da2e39615b0ac55f369ce44c to your computer and use it in GitHub Desktop.
Hiding introspection fields with GraphQL-Ruby
require "bundler/inline"
gemfile do
gem "graphql", "2.0.24"
end
class MySchema < GraphQL::Schema
module CustomIntrospection
module HideIntrospectionByContext
def visible?(ctx)
super &&
if introspection?
ctx[:introspection_permitted]
else
true
end
end
end
class IntrospectionField < GraphQL::Schema::Field
include HideIntrospectionByContext
end
class DynamicFields < GraphQL::Introspection::DynamicFields
field_class(IntrospectionField)
field :__typename, String, null: false
end
class EntryPoints < GraphQL::Introspection::EntryPoints
field_class(IntrospectionField)
field :__type, GraphQL::Introspection::TypeType do
argument :name, String
end
end
class SchemaType < GraphQL::Introspection::SchemaType
extend HideIntrospectionByContext
end
end
introspection(CustomIntrospection)
class Thing < GraphQL::Schema::Object
field :name, String
end
class Query < GraphQL::Schema::Object
field :things, [Thing], null: true
def things
[
{ name: "Pogo Stick" },
{ name: "Immersion Blender" },
{ name: "Ceiling Fan" },
]
end
end
query(Query)
end
things_query_str = "{ things { name } }"
# Normal queries are permitted:
pp MySchema.execute(things_query_str, context: {}).to_h
# {"data"=>
# {"things"=>
# [{"name"=>"Pogo Stick"},
# {"name"=>"Immersion Blender"},
# {"name"=>"Ceiling Fan"}]}}
# But Introspection is not visible by default:
pp MySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY, context: {}).to_h
# {"errors"=>
# [{"message"=>"Field '__schema' doesn't exist on type 'Query'",
# ...
# Introspection can be permitted by a context flag:
pp MySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY, context: { introspection_permitted: true }).to_h
# {"code"=>"useAndDefineFragment", "fragmentName"=>"FullType"}}]}
# {"data"=>
# {"__schema"=>
# {"queryType"=>{"name"=>"Query"},
# "mutationType"=>nil,
# "subscriptionType"=>nil,
# "types"=> # ...
# Mixed introspection and query also requires that context flag:
mixed_query_str = "{ things { name __typename } }"
# It's not visible without the flag:
pp MySchema.execute(mixed_query_str, context: {}).to_h
# {"errors"=>
# [{"message"=>"Field '__typename' doesn't exist on type 'Thing'",
# # ...
# But it _is_ permitted if the flag is present:
pp MySchema.execute(mixed_query_str, context: { introspection_permitted: true }).to_h
# {"data"=>
# {"things"=>
# [{"name"=>"Pogo Stick", "__typename"=>"Thing"},
# {"name"=>"Immersion Blender", "__typename"=>"Thing"},
# {"name"=>"Ceiling Fan", "__typename"=>"Thing"}]}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment