website/src/docs/hotchocolate/v16/building-a-schema/arguments.md
GraphQL arguments let clients pass values to individual fields. In Hot Chocolate, each parameter on a resolver method becomes a field argument in the schema, unless it is a recognized service type (like CancellationToken or a registered service).
GraphQL schema
type Query {
user(id: ID!): User
users(role: UserRole, limit: Int = 10): [User!]!
}
Client query
{
user(id: "UHJvZHVjdAppMQ==") {
name
}
}
Arguments are frequently provided through variables, which separate the static query structure from the dynamic runtime values:
query ($userId: ID!) {
user(id: $userId) {
name
}
}
Method parameters on a resolver become GraphQL arguments.
<ExampleTabs> <Implementation>// Types/UserQueries.cs
[QueryType]
public static partial class UserQueries
{
public static User? GetUser(string username, UserService users)
=> users.FindByName(username);
}
The username parameter becomes a username: String! argument. The UserService parameter is recognized as a service and is not exposed in the schema.
// Types/UserQueriesType.cs
public class UserQueriesType : ObjectType
{
protected override void Configure(IObjectTypeDescriptor descriptor)
{
descriptor.Name(OperationTypeNames.Query);
descriptor
.Field("user")
.Argument("username", a => a.Type<NonNullType<StringType>>())
.Resolve(context =>
{
var username = context.ArgumentValue<string>("username");
// ...
});
}
}
Use [GraphQLName] to change the argument name in the schema while keeping the C# parameter name unchanged.
// Types/UserQueries.cs
[QueryType]
public static partial class UserQueries
{
public static User? GetUser(
[GraphQLName("name")] string username,
UserService users)
=> users.FindByName(username);
}
This produces user(name: String!): User in the schema.
An argument is required when its C# type is non-nullable. Make an argument optional by using a nullable type.
// Types/ProductQueries.cs
[QueryType]
public static partial class ProductQueries
{
public static List<Product> GetProducts(string? category, int? limit)
{
// Both arguments are optional
// ...
}
}
This produces:
type Query {
products(category: String, limit: Int): [Product!]!
}
When using nullable reference types (recommended), string maps to String! and string? maps to String. See Non-Null for details.
Use [DefaultValue] to assign a default to an argument. The default appears in the schema and is used when the client omits the argument.
// Types/ProductQueries.cs
[QueryType]
public static partial class ProductQueries
{
public static List<Product> GetProducts(
[DefaultValue(10)] int limit)
{
// ...
}
}
This produces products(limit: Int! = 10): [Product!]!.
C# default parameter values also work:
public static List<Product> GetProducts(int limit = 10)
The [ID] attribute marks a parameter as a GraphQL ID scalar. When combined with global object identification, it also deserializes the opaque global ID back to the underlying value.
// Types/ProductQueries.cs
[QueryType]
public static partial class ProductQueries
{
public static Product? GetProduct([ID] int id, CatalogContext db)
=> db.Products.Find(id);
}
To restrict the ID to a specific type (ensuring only IDs serialized for Product are accepted):
public static Product? GetProduct(
[ID(nameof(Product))] int id,
CatalogContext db)
=> db.Products.Find(id);
In v16, you can also use the generic form [ID<Product>] which infers the type name automatically.
When an argument needs multiple fields, use an input object type instead of multiple scalar arguments.
// Types/BookFilterInput.cs
public record BookFilterInput(string? Title, string? Author, int? Year);
// Types/BookQueries.cs
[QueryType]
public static partial class BookQueries
{
public static List<Book> GetBooks(BookFilterInput filter, CatalogContext db)
{
// ...
}
}
This produces:
input BookFilterInput {
title: String
author: String
year: Int
}
type Query {
books(filter: BookFilterInput!): [Book!]!
}