doc/development/graphql_guide/array_argument_validation.md
All array arguments in GitLab GraphQL API should have size validation to prevent abuse and performance issues.
Use the validates option to explicitly declare array size limits:
argument :assignee_usernames, [GraphQL::Types::String],
required: false,
validates: { length: { maximum: Types::BaseArgument::MAX_ARRAY_SIZE } },
description: "Usernames of users assigned to the merge request " \
"(maximum is #{Types::BaseArgument::MAX_ARRAY_SIZE} usernames)."
Benefits:
During the transition period, array arguments without explicit validates: { length: { maximum: ... } } will automatically be limited to 100 items by BaseArgument.
# This will automatically be limited to 100 items
argument :items, [GraphQL::Types::String],
required: false,
description: 'List of items.'
The BaseArgument class implements smart detection:
# Explicit validation - uses 50 as the limit
argument :limited_items, [GraphQL::Types::String],
validates: { length: { maximum: 50 } },
description: 'Limited to 50 items.'
# No explicit validation - automatically limited to 100 items
argument :auto_limited_items, [GraphQL::Types::String],
description: 'Automatically limited to 100 items.'
All array arguments are automatically protected with a 100-item limit.
Gradually add explicit validates declarations to all array arguments:
# Before (relies on automatic validation)
argument :assignee_usernames, [GraphQL::Types::String],
required: false,
description: 'Usernames of users assigned to the merge request.'
# After (explicit validation)
argument :assignee_usernames, [GraphQL::Types::String],
required: false,
validates: { length: { maximum: Types::BaseArgument::MAX_ARRAY_SIZE } },
description: "Usernames of users assigned to the merge request " \
"(maximum is #{Types::BaseArgument::MAX_ARRAY_SIZE} usernames)."
Once all array arguments have explicit validation, we can remove the automatic validation from BaseArgument.
Types::BaseArgument::MAX_ARRAY_SIZEDefault maximum size for array arguments (currently 100).
Use this constant for consistency:
validates: { length: { maximum: Types::BaseArgument::MAX_ARRAY_SIZE } }
For specific use cases, you can use a different limit:
# Using a module constant
module WorkItems
module SharedFilterArguments
MAX_FIELD_LIMIT = 100
end
end
argument :ids, [::Types::GlobalIDType[::WorkItem]],
validates: { length: { maximum: WorkItems::SharedFilterArguments::MAX_FIELD_LIMIT } },
description: "Filter by global IDs (maximum is #{WorkItems::SharedFilterArguments::MAX_FIELD_LIMIT} IDs)."
When validation fails, users receive a clear error message:
"assigneeUsernames cannot accept more than 100 items"
# Example 1: Using the default constant
argument :user_ids, [GraphQL::Types::ID],
validates: { length: { maximum: Types::BaseArgument::MAX_ARRAY_SIZE } },
description: "User IDs (maximum is #{Types::BaseArgument::MAX_ARRAY_SIZE})."
# Example 2: Using a custom constant
argument :label_names, [GraphQL::Types::String],
validates: { length: { maximum: WorkItems::SharedFilterArguments::MAX_FIELD_LIMIT } },
description: "Label names (maximum is #{WorkItems::SharedFilterArguments::MAX_FIELD_LIMIT})."
# Example 3: Custom limit for specific use case
argument :vulnerability_ids, [GraphQL::Types::ID],
validates: { length: { minimum: 1, maximum: 50 } },
description: "Vulnerability IDs (minimum 1, maximum 50)."
# Bad: No validation and no description of limit
argument :items, [GraphQL::Types::String],
description: 'List of items.'
# Bad: Hardcoded number without constant
argument :items, [GraphQL::Types::String],
validates: { length: { maximum: 100 } },
description: 'List of items (maximum is 100).'
When testing resolvers or mutations with array arguments, test the validation:
RSpec.describe Resolvers::MyResolver do
it 'accepts arrays within the limit' do
items = Array.new(50, 'item')
expect { resolve(items: items) }.not_to raise_error
end
it 'rejects arrays exceeding the limit' do
items = Array.new(101, 'item')
expect { resolve(items: items) }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
end
end