website/src/docs/hotchocolate/v16/building-a-schema/documentation.md
GraphQL descriptions enrich your schema with information that consumers see in developer tools, IDE autocompletion, and introspection results. Every type, field, argument, and enum value can carry a description string.
"Represents a registered user."
type User {
"The unique username."
username: String!
}
Hot Chocolate provides two ways to add descriptions: the [GraphQLDescription] attribute and XML documentation comments.
The [GraphQLDescription] attribute sets a description on any schema element.
// Types/User.cs
[GraphQLDescription("Represents a registered user.")]
public class User
{
[GraphQLDescription("The unique username.")]
public string Username { get; set; }
}
// Types/UserRole.cs
[GraphQLDescription("Available user roles.")]
public enum UserRole
{
[GraphQLDescription("Full system access.")]
Administrator,
[GraphQLDescription("Content moderation access.")]
Moderator
}
// Types/UserQueries.cs
[QueryType]
public static partial class UserQueries
{
[GraphQLDescription("Finds a user by username.")]
public static User? GetUser(
[GraphQLDescription("The username to search for.")] string username,
UserService users)
=> users.FindByName(username);
}
// Types/UserType.cs
public class UserType : ObjectType<User>
{
protected override void Configure(IObjectTypeDescriptor<User> descriptor)
{
descriptor.Description("Represents a registered user.");
descriptor
.Field(f => f.Username)
.Description("The unique username.");
}
}
// Types/UserRoleType.cs
public class UserRoleType : EnumType<UserRole>
{
protected override void Configure(IEnumTypeDescriptor<UserRole> descriptor)
{
descriptor.Description("Available user roles.");
descriptor
.Value(UserRole.Administrator)
.Description("Full system access.");
}
}
In code-first, the Description() method takes precedence over all other forms of documentation. This applies even if the provided value is null or empty.
Hot Chocolate can generate descriptions from standard C# XML documentation comments. This lets you maintain a single source of documentation for both your C# code and GraphQL schema.
// Types/User.cs
/// <summary>
/// Represents a registered user.
/// </summary>
public class User
{
/// <summary>
/// The unique username.
/// </summary>
public string Username { get; set; }
}
// Types/UserRole.cs
/// <summary>
/// Available user roles.
/// </summary>
public enum UserRole
{
/// <summary>
/// Full system access.
/// </summary>
Administrator,
/// <summary>
/// Content moderation access.
/// </summary>
Moderator
}
// Types/UserQueries.cs
[QueryType]
public static partial class UserQueries
{
/// <summary>
/// Finds a user by username.
/// </summary>
/// <param name="username">The username to search for.</param>
public static User? GetUser(string username, UserService users)
=> users.FindByName(username);
}
To make XML docs available at runtime, enable GenerateDocumentationFile in your .csproj:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
The <NoWarn> element is optional. It suppresses compiler warnings for types without documentation comments.
If you do not want XML comments to appear in the schema:
// Program.cs
builder
.AddGraphQL()
.ModifyOptions(opt => opt.UseXmlDocumentation = false);
When both [GraphQLDescription] and XML documentation are present, they follow this priority:
[GraphQLDescription] attribute (implementation-first): Used if the value is non-null and non-empty. If null or empty, XML documentation is used as a fallback.Description() method (code-first): Always takes precedence, even if null or empty.If you use a custom naming convention and XML documentation, pass an XmlDocumentationProvider to the convention so descriptions are preserved:
// Types/CustomNamingConventions.cs
public class CustomNamingConventions : DefaultNamingConventions
{
public CustomNamingConventions(
IDocumentationProvider documentationProvider)
: base(documentationProvider) { }
}
// Program.cs
IReadOnlySchemaOptions capturedSchemaOptions;
builder
.AddGraphQL()
.ModifyOptions(opt => capturedSchemaOptions = opt)
.AddConvention<INamingConventions>(sp =>
new CustomNamingConventions(
new XmlDocumentationProvider(
new XmlDocumentationFileResolver(
capturedSchemaOptions.ResolveXmlDocumentationFileName),
sp.GetApplicationService<ObjectPool<StringBuilder>>()
?? new NoOpStringBuilderPool())));