Arguments allow you to pass specific values to fields and directives, making them dynamic and configurable. They work like Ruby’s named parameters, enabling flexible field resolution based on provided values.
Adding Arguments to Fields
Add arguments to any output field using the argument method:
query_fields do
field :user, 'User' do
argument :id, :id, null: false
end
field :users, 'User', array: true do
argument :limit, :int, default: 10
argument :offset, :int, default: 0
argument :role, 'Role'
end
end
The resulting GraphQL:
type Query {
user(id: ID!): User
users(limit: Int = 10, offset: Int = 0, role: Role): [User]
}
Argument Syntax
Basic argument definition:
argument :name, :type, options
The argument name (automatically converted to camelCase in GraphQL)
The argument type - can be a scalar, enum, or input object
Whether the argument accepts null values
Whether the argument accepts an array of values
Whether array elements can be null
Shortcut for null: false, array: true, nullable: false
Default value when argument is not provided
Description for documentation and introspection
Argument Types
Arguments accept input-compatible types:
Scalars
field :search, 'SearchResult', array: true do
argument :query, :string, null: false
argument :limit, :int, default: 20
argument :offset, :int, default: 0
argument :include_archived, :boolean, default: false
end
Enums
enum 'Episode', values: %i[new_hope empire jedi]
query_fields do
field :hero, 'Character' do
argument :episode, 'Episode',
desc: 'Return for a specific episode'
end
end
input 'UserFilter' do
field :role, 'Role'
field :active, :boolean
end
query_fields do
field :users, 'User', array: true do
argument :filter, 'UserFilter'
end
end
Arrays
field :users_by_ids, 'User', array: true do
argument :ids, :id, array: true, null: false
end
# With non-nullable elements
field :create_posts, 'Post', array: true do
argument :titles, :string, full: true # [String!]!
end
Accessing Arguments
Multiple ways to access arguments in resolvers:
Named Parameters (Recommended)
def user(id:)
User.find(id)
end
def users(limit: 10, offset: 0, role: nil)
scope = User.limit(limit).offset(offset)
scope = scope.where(role: role.to_sym) if role
scope
end
Using argument Method
def user
User.find(argument(:id))
end
In Blocks
field(:user, 'User').resolve do |id:|
User.find(id)
end
# Or using argument method
field(:user, 'User').resolve do
User.find(argument(:id))
end
Default Values
Provide default values for optional arguments:
field :users, 'User', array: true do
argument :limit, :int, default: 10
argument :order, 'SortOrder', default: 'ASC'
argument :active, :boolean, default: true
end
def users(limit:, order:, active:)
# limit defaults to 10 if not provided
# order defaults to 'ASC' if not provided
# active defaults to true if not provided
User.where(active: active)
.limit(limit)
.order(created_at: order.to_s.downcase)
end
Argument Helpers
Use the arg helper for inline argument definition:
query_fields do
field :hero, 'Character', method_name: :find_hero,
arguments: arg(:episode, 'Episode', desc: 'Return for a specific episode'),
desc: 'Find the hero of the whole saga'
end
# Multiple arguments
field :human, 'Human', method_name: :find_human,
arguments: [
arg(:id, :id, null: false, desc: 'ID of the human'),
arg(:include_friends, :boolean, default: false)
]
The arg helper is perfect for simple arguments. Use the block syntax for complex arguments with documentation.
Array Arguments
Handle array arguments with proper nullability:
# Array that can be null, elements can be null
argument :tags, :string, array: true
# tags: [String]
# Array cannot be null, elements can be null
argument :tags, :string, array: true, null: false
# tags: [String]!
# Array can be null, elements cannot be null
argument :tags, :string, array: true, nullable: false
# tags: [String!]
# Array cannot be null, elements cannot be null
argument :tags, :string, full: true
# tags: [String!]!
Rails GraphQL currently only supports one-dimensional arrays. For multi-dimensional data, use nested input objects.
Mutation Arguments
Common mutation argument patterns:
mutation_fields do
field :update_user, 'User', null: false do
argument :id, :id, null: false
argument :input, 'UpdateUserInput', null: false
end
end
def update_user(id:, input:)
user = User.find(id)
user.update!(input.params)
user
end
Built-in ID Argument
mutation_fields do
field :change_human, 'Character', full: true do
desc 'Change the episodes of a human and return a set of characters'
id_argument desc: 'The ID of the human to be changed'
argument :episodes, 'Episode', array: true, nullable: false
perform :change_episodes, :humans, episodes: %w[NEW_HOPE EMPIRE]
resolve :character_set
end
end
Descriptions
Document arguments for better API understanding:
query_fields do
field :droid, 'Droid', method_name: :find_droid do
desc 'Find a droid character'
argument :id, :id, null: false,
desc: 'ID of the droid'
argument :include_friends, :boolean, default: false,
desc: 'Whether to preload friends for efficiency'
end
end
Validation
Arguments validate automatically:
field :user, 'User' do
argument :id, :id, null: false
end
# Valid queries
{ user(id: "1") { name } }
{ user(id: 1) { name } }
# Invalid queries
{ user { name } } # Missing required argument
{ user(id: null) { name } } # Null not allowed
Argument Injection
Controlled by configuration settings:
# config/initializers/graphql.rb
Rails::GraphQL.configure do |config|
# Enable automatic argument injection
config.callback_inject_arguments = true
config.callback_inject_named_arguments = true
end
With injection enabled:
def user(id:, request:, current_user:)
# Arguments are automatically injected based on parameter names
return User.find(id) if current_user.admin?
current_user
end
Read more about argument injection.
Common Patterns
query_fields do
field :posts, 'Post', array: true do
argument :first, :int, default: 10,
desc: 'Number of items to return'
argument :after, :string,
desc: 'Cursor for pagination'
end
end
Filtering
query_fields do
field :users, 'User', array: true do
argument :role, 'Role'
argument :active, :boolean
argument :search, :string
argument :created_after, :date
end
end
def users(role: nil, active: nil, search: nil, created_after: nil)
scope = User.all
scope = scope.where(role: role.to_sym) if role
scope = scope.where(active: active) unless active.nil?
scope = scope.search(search) if search
scope = scope.where('created_at > ?', created_after) if created_after
scope
end
Sorting
enum 'SortOrder', values: %i[asc desc]
query_fields do
field :users, 'User', array: true do
argument :sort_by, :string, default: 'created_at'
argument :order, 'SortOrder', default: 'DESC'
end
end
def users(sort_by:, order:)
User.order(sort_by => order.to_s.downcase)
end
Optional Fields
object 'Human' do
field :greeting, :string do
argument :name, :string, null: false,
desc: 'Name to greet'
end
def greeting(name:)
format(current_value.greeting, name)
end
end
Query Examples
query {
# Simple argument
user(id: "1") {
name
}
# Multiple arguments
users(limit: 5, role: ADMIN) {
name
email
}
# Array argument
usersByIds(ids: ["1", "2", "3"]) {
name
}
# Enum argument
hero(episode: EMPIRE) {
name
}
# Input object argument
searchUsers(filter: {
role: ADMIN
active: true
}) {
name
}
}
Best Practices
Argument best practices:
- Use
null: false for required arguments
- Provide sensible defaults for optional arguments
- Use enums for constrained choices
- Use input objects for complex argument groups
- Document with
desc for better DX
Think of arguments like Ruby keyword arguments - they make your API self-documenting and easier to use.
Current limitations:
- Arguments don’t support directives yet
- Only one-dimensional arrays are supported
Equivalency
Arguments check equivalency for interface/object validation:
argument1 == argument2 # Name and type match
argument1 =~ argument2 # Type matches (name can differ)