class GraphQL::Schema::Field
Field definitions are the primary way to expose data from an object type. Keep field-specific behavior and examples here; the guide is only an entry point for the broader fields documentation.
Object fields expose data about that object or connect the object to other objects. You can add fields to your object types with the field(...) class method, for example:
field :name, String, "The unique name of this list", null: false
Objects and Interfaces have fields.
The different elements of field definition are addressed below:
-
Return types say what kind of data this field returns
-
Documentation includes description, comments and deprecation notes
-
Resolution behavior hooks up the
GraphQLfield to Ruby code -
Arguments allow fields to take input when they’re queried
-
Extra field metadata for low-level access to the GraphQL-Ruby runtime
Field Names
A field’s name is provided as the first argument or as the name: option:
field :team_captain, ... # or: field ..., name: :team_captain
Under the hood, GraphQL-Ruby camelizes field names, so field :team_captain, ... would be { teamCaptain } in GraphQL. You can disable this behavior by adding camelize: false to your field definition or to the default field options.
The field’s name is also used as the basis of field resolution.
Field Return Type
The second argument to field(...) is the return type. This can be:
-
A built-in
GraphQLtype (Integer,Float,String,ID, orBoolean) -
A
GraphQLtype from your application -
An array of any of the above, which denotes a list type.
Nullability is expressed with the null: keyword:
-
null: true(default) means that the field may returnnil -
null: falsemeans the field is non-nullable; it may not returnnil. If the implementation returnsnil, GraphQL-Ruby will return an error to the client.
Additionally, list types maybe nullable by adding [..., null: true] to the definition.
Here are some examples:
field :name, String # `String`, may return a `String` or `nil` field :id, ID, null: false # `ID!`, always returns an `ID`, never `nil` field :teammates, [Types::User], null: false # `[User!]!`, always returns a list containing `User`s field :scores, [Integer, null: true] # `[Int]`, may return a list or `nil`, the list may contain a mix of `Integer`s and `nil`s
Field Documentation
Fields may be documented with a description, comment and may be deprecated.
Descriptions can be added with the field(...) method as a positional argument, a keyword argument, or inside the block:
# 3rd positional argument field :name, String, "The name of this thing", null: false # `description:` keyword field :name, String, null: false, description: "The name of this thing" # inside the block field :name, String, null: false do description "The name of this thing" end
Comments can be added with the field(...) method as a keyword argument, or inside the block:
# `comment:` keyword field :name, String, null: false, comment: "Rename to full name" # inside the block field :name, String, null: false do comment "Rename to full name" end
Generates field name with comment above “Rename to full name” above.
type Foo {
# Rename to full name
name: String!
}
Deprecated fields can be marked by adding a deprecation_reason: keyword argument:
field :email, String, deprecation_reason: "Users may have multiple emails, use `User.emails` instead."
Fields with a deprecation_reason: will appear as “deprecated” in GraphiQL.
Field Resolution
In general, fields return Ruby values corresponding to their GraphQL return types. For example, a field with the return type String should return a Ruby string, and a field with the return type [User!]! should return a Ruby array with zero or more User objects in it.
By default, fields return values by:
-
Trying to call a method on the underlying object; OR
-
If the underlying object is a
Hash, lookup a key in that hash. -
An optional
:fallback_valuecan be supplied that will be used if the above fail.
The method name or hash key corresponds to the field name, so in this example:
field :top_score, Integer, null: false
The default behavior is to look for a #top_score method, or lookup a Hash key, :top_score (symbol) or "top_score" (string).
You can override the method name with the method: keyword, or override the hash key(s) with the hash_key: or dig: keyword, for example:
# Use the `#best_score` method to resolve this field field :top_score, Integer, null: false, method: :best_score # Lookup `hash["allPlayers"]` to resolve this field field :players, [User], null: false, hash_key: "allPlayers" # Use the `#dig` method on the hash with `:nested` and `:movies` keys field :movies, [Movie], null: false, dig: [:nested, :movies]
To pass-through the underlying object without calling a method on it, you can use method: :itself:
field :player, User, null: false, method: :itself
This is equivalent to:
field :player, User, null: false def player object end
If you don’t want to delegate to the underlying object, you can define a method for each field:
# Use the custom method below to resolve this field field :total_games_played, Integer, null: false def total_games_played object.games.count end
Inside the method, you can access some helper methods:
-
objectis the underlying application object (formerlyobjto resolve functions) -
contextis the query context (passed ascontext:when executing queries, formerlyctxto resolve functions)
Additionally, when you define arguments (see below), they’re passed to the method definition, for example:
# Call the custom method with incoming arguments field :current_winning_streak, Integer, null: false do argument :include_ties, Boolean, required: false, default_value: false end def current_winning_streak(include_ties:) # Business logic goes here end
As the examples above show, by default the custom method name must match the field name. If you want to use a different custom method, the resolver_method option is available:
# Use the custom method with a non-default name below to resolve this field field :total_games_played, Integer, null: false, resolver_method: :games_played def games_played object.games.count end
resolver_method has two main use cases:
-
resolver re-use between multiple fields
-
dealing with method conflicts (specifically if you have fields named
contextorobject)
Note that resolver_method cannot be used in combination with method or hash_key.
Field Arguments
Arguments allow fields to take input to their resolution. For example:
-
A
search()field may take aterm:argument, which is the query to use for searching, egsearch(term: "GraphQL") -
A
user()field may take anid:argument, which specifies which user to find, eguser(id: 1) -
An
attachments()field may take atype:argument, which filters the result by file type, egattachments(type: PHOTO)
Read more in the Arguments guide
Extra Field Metadata
Inside a field method, you can access some low-level objects from the GraphQL-Ruby runtime. Be warned, these APIs are subject to change, so check the changelog when updating.
A few extras are available:
-
ast_node -
graphql_name(the field’s name) -
owner(the type that this field belongs to) -
lookahead(see Lookahead) -
execution_errors, whose#add(err_or_msg)method should be used for adding errors -
argument_details(Interpreter only), an instance ofGraphQL::Execution::Interpreter::Argumentswith argument metadata -
parent(the previousobjectin the query) -
Custom extras, see below
To inject them into your field method, first, add the extras: option to the field definition:
field :my_field, String, null: false, extras: [:ast_node]
Then add ast_node: keyword to the method signature:
def my_field(ast_node:) # ... end
At runtime, the requested runtime object will be passed to the field.
Custom extras are also possible. Any method on your field class can be passed to extras: [...], and the value will be injected into the method. For example, extras: [:owner] will inject the object type who owns the field. Any new methods on your custom field class may be used, too.
Field Parameter Default Values
The field method requires you to pass null: keyword argument to determine whether the field is nullable or not. For another field you may want to override camelize, which is true by default. You can override this behavior by adding a custom field with overwritten camelize option, which is true by default.
class CustomField < GraphQL::Schema::Field # Add `null: false` and `camelize: false` which provide default values # in case the caller doesn't pass anything for those arguments. # **kwargs is a catch-all that will get everything else def initialize(*args, null: false, camelize: false, **kwargs, &block) # Then, call super _without_ any args, where Ruby will take # _all_ the args originally passed to this method and pass it to the super method. super end end
To use CustomField in your Objects and Interfaces, you’ll need to register it as a field_class on those classes. See Customizing Fields for more information on how to do so.
Attributes
Returns
-
String— theGraphQLname for this field, camelized unlesscamelize: falseis provided
Returns
-
String— Method or hash key on the underlying object to look up
Returns
-
Symbol— Method or hash key on the underlying object to look up
Returns
-
String— theGraphQLname for this field, camelized unlesscamelize: falseis provided
Returns
-
Symbol— the original name of the field, passed in by the user
Returns
-
Class— The thing this field was defined on (type, mutation, resolver)
Returns
-
Object— Boolean
Returns
-
Object— Boolean
Returns
-
Boolean— Apply tracing to this field? (Default: skip scalars, this is the override value)
Public Class Methods
# File lib/graphql/schema/field.rb, line 500 def self.connection_extension(new_extension_class = nil) if new_extension_class @connection_extension = new_extension_class else @connection_extension ||= find_inherited_value(:connection_extension, ConnectionExtension) end end
This extension is applied to fields when connection? is true.
You can override it in your base field definition.
Returns
-
Class— AFieldExtensionsubclass for implementing pagination behavior.
Examples
Example: Configuring a custom extension
class Types::BaseField < GraphQL::Schema::Field connection_extension(MyCustomExtension) end
# File lib/graphql/schema/field.rb, line 572 def initialize( type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, resolve_legacy_instance_method: nil, resolve_static: nil, resolve_each: nil, resolve_batch: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, # Keywords below this line must have literal defaults (`nil`, `true`, `false`, a number or a symbol). # Ruby uses a 31-bit bitmap to track which keywords with non-literal defaults were given. # If one comes after the 31st keyword, Ruby allocates a Hash on every call. dynamic_introspection: false, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, dataload: nil, ast_node: nil, &definition_block ) if name.nil? raise ArgumentError, "missing first `name` argument or keyword `name:`" end if !(resolver_class) if type.nil? && !block_given? raise ArgumentError, "missing second `type` argument, keyword `type:`, or a block containing `type(...)`" end end @original_name = name name_s = -name.to_s @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s) NameValidator.validate!(@name) @description = description @comment = comment @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary self.deprecation_reason = deprecation_reason if method && hash_key && dig raise ArgumentError, "Provide `method:`, `hash_key:` _or_ `dig:`, not multiple. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}, dig: #{dig.inspect}`)" end if resolver_method if method raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)" end if hash_key || dig raise ArgumentError, "Provide `hash_key:`, `dig:`, _or_ `resolver_method:`, not multiple. (called with: `hash_key: #{hash_key.inspect}, dig: #{dig.inspect}, resolver_method: #{resolver_method.inspect}`)" end end method_name = method || hash_key || name_s @dig_keys = dig if hash_key @hash_key = hash_key @hash_key_str = hash_key.to_s else @hash_key = NOT_CONFIGURED @hash_key_str = NOT_CONFIGURED end @method_str = -method_name.to_s @method_sym = method_name.to_sym @resolver_method = (resolver_method || name_s).to_sym if resolve_static @execution_mode = :resolve_static @execution_mode_key = resolve_static == true ? @method_sym : resolve_static elsif resolve_batch @execution_mode = :resolve_batch @execution_mode_key = resolve_batch == true ? @method_sym : resolve_batch elsif resolve_each @execution_mode = :resolve_each @execution_mode_key = resolve_each == true ? @method_sym : resolve_each elsif hash_key @execution_mode = :hash_key @execution_mode_key = hash_key elsif dig @execution_mode = :dig @execution_mode_key = dig elsif resolver_class @execution_mode = :resolver_class @execution_mode_key = resolver_class elsif resolve_legacy_instance_method @execution_mode = :resolve_legacy_instance_method @execution_mode_key = resolve_legacy_instance_method == true ? @method_sym : resolve_legacy_instance_method elsif dataload @execution_mode = :dataload @execution_mode_key = dataload else @execution_mode = :direct_send @execution_mode_key = @method_sym end @complexity = complexity @dynamic_introspection = dynamic_introspection @return_type_expr = type @return_type_null = if !null.nil? null elsif resolver_class nil else true end @connection = connection @max_page_size = max_page_size @default_page_size = default_page_size @introspection = introspection @extras = extras @broadcastable = broadcastable @resolver_class = resolver_class @scope = scope @trace = trace @relay_node_field = relay_node_field @relay_nodes_field = relay_nodes_field @ast_node = ast_node @method_conflict_warning = method_conflict_warning @fallback_value = fallback_value @definition_block = definition_block arguments.each do |name, arg| case arg when Hash argument(name: name, **arg) when GraphQL::Schema::Argument add_argument(arg) when Array arg.each { |a| add_argument(a) } else raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}" end end @owner = owner @subscription_scope = subscription_scope @extensions = EMPTY_ARRAY @call_after_define = false set_pagination_extensions(connection_extension: NOT_CONFIGURED.equal?(connection_extension) ? self.class.connection_extension : connection_extension) # Do this last so we have as much context as possible when initializing them: if !extensions.empty? self.extensions(extensions) end if resolver_class && !resolver_class.extensions.empty? self.extensions(resolver_class.extensions) end if !directives.empty? directives.each do |(dir_class, options)| self.directive(dir_class, **options) end end if !validates.empty? self.validates(validates) end if @definition_block.nil? self.extensions.each(&:after_define_apply) @call_after_define = true end end
Parameters
-
name(Symbol) — The underscore-cased version of this field name (will be camelized for theGraphQLAPI) -
type(Class, GraphQL::BaseType, Array) — The return type of this field -
owner(Class) — The type that this field belongs to -
null(Boolean) — (defaults totrue)trueif this field may returnnull,falseif it is nevernull -
description(String) —Fielddescription -
comment(String) —Fieldcomment -
deprecation_reason(String) — If present, the field is marked “deprecated” with this message -
method(Symbol) — The method to call on the underlying object to resolve this field (defaults toname) -
hash_key(String, Symbol) — The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults tonameorname.to_s) -
dig(Array<String, Symbol>) — The nested hash keys to lookup on the underlying hash to resolve this field using dig -
resolver_method(Symbol) — The method on the type to call to resolve this field (defaults toname) -
connection(Boolean) —trueif this field should get automagic connection behavior; default is to infer by*Connectionin the return type name -
connection_extension(Class) — The extension to add, to implement connections. Ifnil, no extension is added. -
resolve_static(Symbol, true, nil) — Used bySchema.execute_nextto produce a single value, shared by all objects which resolve this field. Called on the owner type class withcontext, **arguments -
resolve_batch(Symbol, true, nil) — Used bySchema.execute_nextmapobjectsto a same-sized Array of results. Called on the owner type class withobjects, context, **arguments. -
resolve_each(Symbol, true, nil) — Used bySchema.execute_nextto get a value value for each item. Called on the owner type class withobject, context, **arguments. -
resolve_legacy_instance_method(Symbol, true, nil) — Used bySchema.execute_nextto get a value value for each item. Calls an instance method on the object type class. -
dataload(Class, Hash) — Shorthand for making dataloader calls -
max_page_size(Integer, nil) — For connections, the maximum number of items to return from this field, ornilto allow unlimited results. -
default_page_size(Integer, nil) — For connections, the default number of items to return from this field, ornilto return unlimited results. -
introspection(Boolean) — If true, this field will be marked asintrospection?and the name may begin with__ -
resolver_class(Class) — (Private) ASchema::Resolverwhich this field was derived from. Useresolver:to create a field with a resolver. -
arguments({String=>GraphQL::Schema::Argument, Hash}) — Arguments for this field (may be added in the block, also) -
camelize(Boolean) — If true, the field name will be camelized when building the schema -
complexity(Numeric) — When provided, set the complexity for this field -
scope(Boolean) — If true, the return type’s.scope_itemsmethod will be called on the return value -
subscription_scope(Symbol, String) — A key incontextwhich will be used to scope subscription payloads -
extensions(Array<Class, Hash<Class => Object>>) — Named extensions to apply to this field (see also extension) -
directives(Hash{Class => Hash}) — Directives to apply to this field -
trace(Boolean) — If true, aGraphQL::Tracingtracer will measure this scalar field -
broadcastable(Boolean) — Whether or not this field can be distributed in subscription broadcasts -
ast_node(Language::Nodes::FieldDefinition, nil) — If this schema was parsed from definition, this AST node defined the field -
method_conflict_warning(Boolean) — If false, skip the warning if this field’s method conflicts with a built-in method -
validates(Array<Hash>) — Configurations for validating this field -
fallback_value(Object) — A fallback value if the method is not defined -
dynamic_introspection(Boolean) — (Private, used by GraphQL-Ruby) -
relay_node_field(Boolean) — (Private, used by GraphQL-Ruby) -
relay_nodes_field(Boolean) — (Private, used by GraphQL-Ruby) -
extras(Array<:ast_node, :parent, :lookahead, :owner, :execution_errors, :graphql_name, :argument_details, Symbol>) — Extra arguments to be injected into the resolver for this field -
definition_block(Proc) — an additional block for configuring the field. Receive the field as a block param, or, if no block params are defined, then the block isinstance_eval‘d on the newField.
Public Instance Methods
# File lib/graphql/schema/field.rb, line 797 def broadcastable? if !NOT_CONFIGURED.equal?(@broadcastable) @broadcastable elsif @resolver_class @resolver_class.broadcastable? else nil end end
If true, subscription updates with this field can be shared between viewers See GraphQL::Subscriptions::BroadcastAnalyzer.
Returns
-
Boolean, nil
# File lib/graphql/schema/field.rb, line 969 def calculate_complexity(query:, nodes:, child_complexity:) if respond_to?(:complexity_for) lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner) complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead) elsif connection? arguments = query.arguments_for(nodes.first, self) max_possible_page_size = nil if arguments.respond_to?(:[]) # It might have been an error if arguments[:first] max_possible_page_size = arguments[:first] end if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size) max_possible_page_size = arguments[:last] end elsif arguments.is_a?(GraphQL::ExecutionError) || arguments.is_a?(GraphQL::UnauthorizedError) raise arguments end if max_possible_page_size.nil? max_possible_page_size = default_page_size || query.schema.default_page_size || max_page_size || query.schema.default_max_page_size end if max_possible_page_size.nil? raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `default_page_size`, `max_page_size` or `default_max_page_size`" else metadata_complexity = 0 lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner) # `QueryComplexity` doesn't add skipped introspection fields to `child_complexity`, # so they must not be subtracted from it here, either. count_introspection_fields = query.schema.max_complexity_count_introspection_fields lookahead.selections.each do |next_lookahead| next if !count_introspection_fields && next_lookahead.field.introspection? # this includes `pageInfo`, `nodes` and `edges` and any custom fields # TODO this doesn't support procs yet -- unlikely to need it. metadata_complexity += next_lookahead.field.complexity if next_lookahead.name != :nodes && next_lookahead.name != :edges # subfields, eg, for pageInfo -- assumes no subselections if count_introspection_fields metadata_complexity += next_lookahead.selections.size else metadata_complexity += next_lookahead.selections.count { |s| !s.field.introspection? } end end end # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be? items_complexity = child_complexity - metadata_complexity subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity # Apply this field's own complexity apply_own_complexity_to(subfields_complexity, query, nodes) end else apply_own_complexity_to(child_complexity, query, nodes) end end
# File lib/graphql/schema/field.rb, line 839 def comment(text = nil) if text @comment = text elsif !NOT_CONFIGURED.equal?(@comment) @comment elsif @resolver_class @resolver_class.comment else nil end end
Parameters
-
text(String)
Returns
-
String, nil
# File lib/graphql/schema/field.rb, line 1027 def complexity(new_complexity = nil) case new_complexity when Proc if new_complexity.parameters.size != 3 fail( "A complexity proc should always accept 3 parameters: ctx, args, child_complexity. "\ "E.g.: complexity ->(ctx, args, child_complexity) { child_complexity * args[:limit] }" ) else @complexity = new_complexity end when Numeric @complexity = new_complexity when nil if @resolver_class @complexity || @resolver_class.complexity || 1 else @complexity || 1 end else raise("Invalid complexity: #{new_complexity.inspect} on #{@name}") end end
Source
# File lib/graphql/schema/field.rb, line 433 def connection? if @connection.nil? # Provide default based on type name return_type_name = if @return_type_expr Member::BuildType.to_type_name(@return_type_expr) elsif @resolver_class && @resolver_class.type Member::BuildType.to_type_name(@resolver_class.type) elsif type # As a last ditch, try to force loading the return type: type.unwrap.name end if return_type_name @connection = return_type_name.end_with?("Connection") && return_type_name != "Connection" else # TODO set this when type is set by method false # not loaded yet? end else @connection end end
Can be set with connection: true|false or inferred from a type name ending in *Connection
Returns
-
Boolean— if true, this field will be wrapped withRelayconnection behavior
# File lib/graphql/schema/field.rb, line 1093 def default_page_size if !NOT_CONFIGURED.equal?(@default_page_size) @default_page_size elsif @resolver_class && @resolver_class.has_default_page_size? @resolver_class.default_page_size else nil end end
Returns
-
Integer, nil— Applied to connections if has default page size?
# File lib/graphql/schema/field.rb, line 332 def deprecation_reason super || @resolver_class&.deprecation_reason end
Returns
-
String, nil
# File lib/graphql/schema/field.rb, line 817 def description(text = nil) if text @description = text elsif !NOT_CONFIGURED.equal?(@description) @description elsif @resolver_class @resolver_class.description else nil end end
Parameters
-
text(String)
Returns
-
String
Source
# File lib/graphql/schema/field.rb, line 336 def directives if @resolver_class && !(r_dirs = @resolver_class.directives).empty? if !(own_dirs = super).empty? new_dirs = own_dirs.dup r_dirs.each do |r_dir| if r_dir.class.repeatable? || ( (r_dir_name = r_dir.graphql_name) && (!new_dirs.any? { |d| d.graphql_name == r_dir_name }) ) new_dirs << r_dir end end new_dirs else r_dirs end else super end end
GraphQL::Schema::Member::HasDirectives#directives
# File lib/graphql/schema/field.rb, line 926 def extension(extension_class, **options) extension_inst = extension_class.new(field: self, options: options) if @extensions.frozen? @extensions = @extensions.dup end if @call_after_define extension_inst.after_define_apply end @extensions << extension_inst nil end
Add extension to this field, initialized with options if provided.
Examples
Example: adding an extension
extension(MyExtensionClass)
Example: adding an extension with options
extension(MyExtensionClass, filter: true)
Parameters
-
extension_class(Class) — subclass ofSchema::FieldExtension -
options(Hash) — if provided, given asoptions:when initializingextension.
Returns
-
void
# File lib/graphql/schema/field.rb, line 885 def extensions(new_extensions = nil) if new_extensions new_extensions.each do |extension_config| if extension_config.is_a?(Hash) extension_class, options = *extension_config.to_a[0] self.extension(extension_class, **options) else self.extension(extension_config) end end end @extensions end
Read extension instances from this field, or add new classes/options to be initialized on this field. Extensions are executed in the order they are added.
Examples
Example: adding an extension
extensions([MyExtensionClass])
Example: adding multiple extensions
extensions([MyExtensionClass, AnotherExtensionClass])
Example: adding an extension with options
extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
Parameters
-
extensions(Array<Class, Hash<Class => Hash>>) — Add extensions to this field. For hash elements, only the first key/value is used.
Returns
-
Array<GraphQL::Schema::FieldExtension>— extensions to apply to this field
# File lib/graphql/schema/field.rb, line 951 def extras(new_extras = nil) if new_extras.nil? # Read the value field_extras = @extras if @resolver_class && !@resolver_class.extras.empty? field_extras + @resolver_class.extras else field_extras end else if @extras.frozen? @extras = @extras.dup end # Append to the set of extras on this field @extras.concat(new_extras) end end
Read extras (as symbols) from this field, or add new extras to be opted into by this field’s resolver.
Parameters
-
new_extras(Array<Symbol>) — Add extras to this field
Returns
-
Array<Symbol>
# File lib/graphql/schema/field.rb, line 1334 def fetch_extra(extra_name, ctx) if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name) self.public_send(extra_name) elsif ctx.respond_to?(extra_name) ctx.public_send(extra_name) else raise GraphQL::RequiredImplementationMissingError, "Unknown field extra for #{self.path}: #{extra_name.inspect}" end end
Parameters
-
ctx(GraphQL::Query::Context)
Source
# File lib/graphql/schema/field.rb, line 1103 def freeze type owner_type arguments_statically_coercible? connection? super end
# File lib/graphql/schema/field.rb, line 1083 def has_default_page_size? !NOT_CONFIGURED.equal?(@default_page_size) || (@resolver_class && @resolver_class.has_default_page_size?) end
Returns
-
Boolean— True if this field’s default page size should override the schema default.
Source
# File lib/graphql/schema/field.rb, line 1057 def has_max_page_size? !NOT_CONFIGURED.equal?(@max_page_size) || (@resolver_class && @resolver_class.has_max_page_size?) end
Returns
-
Boolean— True if this field’s max page size should override the schema default.
Source
# File lib/graphql/schema/field.rb, line 403 def inspect "#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>" end
Source
# File lib/graphql/schema/field.rb, line 399 def introspection? @introspection end
Returns
-
Boolean— Is this field a predefined introspection field?
# File lib/graphql/schema/field.rb, line 1067 def max_page_size if !NOT_CONFIGURED.equal?(@max_page_size) @max_page_size elsif @resolver_class && @resolver_class.has_max_page_size? @resolver_class.max_page_size else nil end end
Returns
-
Integer, nil— Applied to connections if has max page size?
# File lib/graphql/schema/field.rb, line 523 def method_conflict_warning? @method_conflict_warning end
Returns
-
Boolean— Should we warn if this field’s name conflicts with a built-in method?
Source
# File lib/graphql/schema/field.rb, line 368 def owner_type @owner_type ||= if owner.nil? raise GraphQL::InvariantError, "Field #{original_name.inspect} (graphql name: #{graphql_name.inspect}) has no owner, but all fields should have an owner. How did this happen?!" elsif owner < GraphQL::Schema::Mutation owner.payload_type else owner end end
Returns
-
Class— TheGraphQLtype this field belongs to. (For fields defined on mutations, it’s the payload type)
# File lib/graphql/schema/field.rb, line 1223 def resolve(object, args, query_ctx) # Unwrap the GraphQL object to get the application object. application_object = object.object method_receiver = nil #: untyped method_to_call = nil #: untyped method_args = nil #: untyped @own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args) query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized| if is_authorized with_extensions(object, args, query_ctx) do |obj, ruby_kwargs| method_args = ruby_kwargs if @resolver_class if obj.is_a?(GraphQL::Schema::Object) obj = obj.object end obj = @resolver_class.new(object: obj, context: query_ctx, field: self) end inner_object = obj.object if !NOT_CONFIGURED.equal?(@hash_key) hash_value = if inner_object.is_a?(Hash) inner_object.key?(@hash_key) ? inner_object[@hash_key] : inner_object[@hash_key_str] elsif inner_object.respond_to?(:[]) inner_object[@hash_key] else nil end if hash_value == false hash_value else hash_value || (@fallback_value != NOT_CONFIGURED ? @fallback_value : nil) end elsif obj.respond_to?(resolver_method) method_to_call = resolver_method method_receiver = obj # Call the method with kwargs, if there are any if !ruby_kwargs.empty? obj.public_send(resolver_method, **ruby_kwargs) else obj.public_send(resolver_method) end elsif inner_object.is_a?(Hash) if @dig_keys inner_object.dig(*@dig_keys) elsif inner_object.key?(@method_sym) inner_object[@method_sym] elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil? inner_object[@method_str] elsif @fallback_value != NOT_CONFIGURED @fallback_value else nil end elsif inner_object.respond_to?(@method_sym) method_to_call = @method_sym method_receiver = obj.object if !ruby_kwargs.empty? inner_object.public_send(@method_sym, **ruby_kwargs) else inner_object.public_send(@method_sym) end elsif @fallback_value != NOT_CONFIGURED @fallback_value else raise <<-ERR Failed to implement #{@owner.graphql_name}.#{@name}, tried: - `#{obj.class}##{resolver_method}`, which did not exist - `#{inner_object.class}##{@method_sym}`, which did not exist - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{inner_object}`, but it wasn't a Hash To implement this field, define one of the methods above (and check for typos), or supply a `fallback_value`. ERR end end else raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: query_ctx, field: self) end end rescue GraphQL::UnauthorizedFieldError => err err.field ||= self begin query_ctx.schema.unauthorized_field(err) rescue GraphQL::ExecutionError => err err end rescue GraphQL::UnauthorizedError => err begin query_ctx.schema.unauthorized_object(err) rescue GraphQL::ExecutionError => err err end rescue ArgumentError if method_receiver && method_to_call assert_satisfactory_implementation(method_receiver, method_to_call, method_args) end # if the line above doesn't raise, re-raise raise rescue GraphQL::ExecutionError => err err end
This method is called by the interpreter for each field. You can extend it in your base field classes.
Parameters
-
object(GraphQL::Schema::Object) — An instance of some type class, wrapping an application object -
args(Hash) — A symbol-keyed hash of Ruby keyword arguments. (Empty if no args) -
ctx(GraphQL::Query::Context)
Source
# File lib/graphql/schema/field.rb, line 389 def resolver @resolver_class end
Returns
-
Class, nil— TheSchema::Resolverthis field was derived from, if there is one
Source
# File lib/graphql/schema/field.rb, line 318 def resolver_method if @resolver_class @resolver_class.resolver_method else @resolver_method end end
Returns
-
Symbol— The method on the type to look up
# File lib/graphql/schema/field.rb, line 1441 def run_next_extensions_before_resolve(objs, args, ctx, extended, idx: 0, &block) extension = @extensions[idx] if extension extension.resolve(objects: objs, arguments: args, context: ctx) do |extended_objs, extended_args, memo| if memo memos = extended.memos ||= {} memos[idx] = memo end if (extras = extension.added_extras) ae = extended.added_extras ||= [] ae.concat(extras) end extended.object = extended_objs extended.arguments = extended_args run_next_extensions_before_resolve(extended_objs, extended_args, ctx, extended, idx: idx + 1, &block) end else yield(objs, args) end end
Source
# File lib/graphql/schema/field.rb, line 461 def scoped? if !@scope.nil? # The default was overridden @scope elsif @return_type_expr # Detect a list return type, but don't call `type` since that may eager-load an otherwise lazy-loaded type @return_type_expr.is_a?(Array) || (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) || connection? elsif @resolver_class resolver_type = @resolver_class.type_expr resolver_type.is_a?(Array) || (resolver_type.is_a?(String) && resolver_type.include?("[")) || connection? else false end end
Returns
-
Boolean— if true, the return type’s.scope_itemsmethod will be applied to this field’s return value
# File lib/graphql/schema/field.rb, line 420 def subscription_scope @subscription_scope || (@resolver_class.respond_to?(:subscription_scope) ? @resolver_class.subscription_scope : nil) end
Returns
-
String, nil
# File lib/graphql/schema/field.rb, line 1128 def type(new_type = NOT_CONFIGURED) if NOT_CONFIGURED.equal?(new_type) if @resolver_class return_type = @return_type_expr || @resolver_class.type_expr if return_type.nil? raise MissingReturnTypeError, "Can't determine the return type for #{self.path} (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)" end nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null Member::BuildType.parse_type(return_type, null: nullable) elsif !@return_type_expr.nil? @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null) end else @return_type_expr = new_type # If `type` is set in the definition block, then the `connection_extension: ...` given as a keyword won't be used, hmm... # Also, arguments added by `connection_extension` will clobber anything previously defined, # so `type(...)` should go first. set_pagination_extensions(connection_extension: self.class.connection_extension) end rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err # Let this propagate up raise err rescue StandardError => err raise MissingReturnTypeError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace end
Get or set the return type of this field.
It may return nil if no type was configured or if the given definition block wasn’t called yet.
Parameters
-
new_type(Module, GraphQL::Schema::NonNull, GraphQL::Schema::List) — AGraphQLreturn type
Returns
-
Module, GraphQL::Schema::NonNull, GraphQL::Schema::List, nil— the configured type for this field
Source
# File lib/graphql/schema/field.rb, line 1154 def visible?(context) if @resolver_class @resolver_class.visible?(context) else true end end