This is documentation for v16, which is currently in preview.
See the latest stable version instead.

Scalars

Scalars are the leaf types of a GraphQL schema — they represent concrete values like strings, numbers, and dates. Unlike object types, scalars cannot be decomposed further; they are where the query ends and actual data is returned.

Every scalar defines how values are converted between their GraphQL wire format (JSON) and .NET runtime representation. GraphQL includes five built-in scalars (String, Int, Float, Boolean, and ID), but you can also define custom scalars like DateTime, Uuid, or EmailAddress to add domain-specific validation and improve the clarity of your API. Hot Chocolate already comes with lots of additional scalars.

Built-in Scalars

The GraphQL specification defines five scalar types that every implementation must support.

String

SDL
type Product {
description: String;
}

This scalar represents a UTF-8 character sequence.

It is automatically inferred from the usage of the .NET string type.

Boolean

SDL
type Product {
purchasable: Boolean;
}

This scalar represents a Boolean value, which can be either true or false.

It is automatically inferred from the usage of the .NET bool type.

Int

SDL
type Product {
quantity: Int;
}

This scalar represents a signed 32-bit numeric non-fractional value.

It is automatically inferred from the usage of the .NET int type.

Float

SDL
type Product {
price: Float;
}

This scalar represents double-precision fractional values, as specified by IEEE 754.

It is automatically inferred from the usage of the .NET float or double type.

Note: We introduced a separate Decimal scalar to handle decimal values.

ID

SDL
type Product {
id: ID!;
}

This scalar is used to facilitate technology-specific Ids, like int, string or Guid.

It is not automatically inferred and the IdType needs to be explicitly specified.

ID values are always represented as a String in client-server communication, but can be coerced to their expected type on the server.

C#
public class Product
{
[GraphQLType<IdType>]
public int Id { get; set; }
}
public class Query
{
public Product GetProduct([GraphQLType<IdType>] int id)
{
// Omitted code for brevity
}
}
C#
public class Product
{
public int Id { get; set; }
}
public class ProductType : ObjectType<Product>
{
protected override void Configure(IObjectTypeDescriptor<Product> descriptor)
{
descriptor.Name("Product");
descriptor.Field(f => f.Id).Type<IdType>();
}
}
public class QueryType : ObjectType
{
protected override void Configure(IObjectTypeDescriptor descriptor)
{
descriptor.Name(OperationTypeNames.Query);
descriptor
.Field("product")
.Argument("id", a => a.Type<IdType>())
.Type<ProductType>()
.Resolve(context =>
{
var id = context.ArgumentValue<int>("id");
// Omitted code for brevity
});
}
}

Notice how our code uses int for the Id, but in a request / response it would be serialized as a string. This allows us to switch the CLR type of our Id, without affecting the schema and our clients.

.NET Scalars

In addition to the scalars defined by the specification, Hot Chocolate also supports the following set of scalar types:

TypeDescription
AnyThe Any scalar type represents any valid GraphQL value.
Base64StringThe Base64String scalar type represents an array of bytes encoded as a Base64 string.
ByteThe Byte scalar type represents a signed 8-bit integer.
DateThe Date scalar type represents a date in UTC.
DateTimeThe DateTime scalar type represents a date and time with time zone offset information.
DecimalThe Decimal scalar type represents a decimal floating-point number with high precision.
LocalDateThe LocalDate scalar type represents a date without time or time zone information.
LocalDateTimeThe LocalDateTime scalar type represents a date and time without time zone information.
LocalTimeThe LocalTime scalar type represents a time of day without date or time zone information.
LongThe Long scalar type represents a signed 64-bit integer.
ShortThe Short scalar type represents a signed 16-bit integer.
TimeSpanThe TimeSpan scalar type represents a duration of time.
UnsignedByteThe UnsignedByte scalar type represents an unsigned 8-bit integer.
UnsignedIntThe UnsignedInt scalar type represents an unsigned 32-bit integer.
UnsignedLongThe UnsignedLong scalar type represents an unsigned 64-bit integer.
UnsignedShortThe UnsignedShort scalar type represents an unsigned 16-bit integer.
URIThe URI scalar type represents a Uniform Resource Identifier (URI) as defined by RFC 3986.
URLThe URL scalar type represents a Uniform Resource Locator (URL) as defined by RFC 3986.
UUIDThe UUID scalar type represents a Universally Unique Identifier (UUID) as defined by RFC 9562.

DateTime Type

Any Type

The Any scalar is a special type that can be compared to object in C#. Any allows us to specify any literal or return any output type.

Consider the following type:

SDL
type Query {
foo(bar: Any): String
}

Since our field foo specifies an argument bar of type Any all of the following queries would be valid:

GraphQL
{
a: foo(bar: 1)
b: foo(bar: [1, 2, 3, 4, 5])
a: foo(bar: "abcdef")
a: foo(bar: true)
a: foo(bar: { a: "foo", b: { c: 1 } })
a: foo(bar: [{ a: "foo", b: { c: 1 } }, { a: "foo", b: { c: 1 } }])
}

The same goes for the output side. Any can return a structure of data although it is a scalar type.

If we want to access the data we can either fetch data as an object or you can ask the context to provide it as a specific object.

C#
object foo = context.ArgumentValue<object>("bar");
Foo foo = context.ArgumentValue<Foo>("bar");

We can also ask the context which kind the current argument is:

C#
ValueKind kind = context.ArgumentKind("bar");

The value kind will tell us by which kind of literal the argument is represented.

An integer literal can still contain a long value and a float literal could be a decimal but it also could just be a float.

C#
public enum ValueKind
{
String,
Integer,
Float,
Boolean,
Enum,
Object,
Null
}

If we want to access an object dynamically without serializing it to a strongly typed model we can get it as IReadOnlyDictionary<string, object> or as ObjectValueNode.

Lists can be accessed generically by getting them as IReadOnlyList<object> or as ListValueNode.

Uuid Type

The Uuid scalar supports the following serialization formats.

SpecifierFormat
N00000000000000000000000000000000
D (default)00000000-0000-0000-0000-000000000000
B{00000000-0000-0000-0000-000000000000}
P(00000000-0000-0000-0000-000000000000)
X{0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}

The UuidType will always return the value in the specified format. In case it is used as an input type, it will first try to parse the result in the specified format. If the parsing does not succeed, it will try to parse the value in other formats.

To change the default format we have to register the UuidType with the specifier on the schema:

C#
builder.Services
.AddGraphQLServer()
.AddType(new UuidType('D'));

Additional Scalars

We also offer a separate package with scalars for more specific use cases.

To use these scalars we have to add the HotChocolate.Types.Scalars package.

Bash
dotnet add package HotChocolate.Types.Scalars
Warning
All HotChocolate.* packages need to have the same version.

Available Scalars:

TypeDescription
EmailAddressEmail address, represented as UTF-8 character sequences, as defined in RFC5322
HexColorHEX color code
HslCSS HSL color as defined here
HslaCSS HSLA color as defined here
IPv4IPv4 address as defined here
IPv6IPv6 address as defined in RFC8064
IsbnISBN-10 or ISBN-13 number as defined here
LatitudeDecimal degrees latitude number
LongitudeDecimal degrees longitude number
MacAddressIEEE 802 48-bit (MAC-48/EUI-48) and 64-bit (EUI-64) Mac addresses, represented as UTF-8 character sequences, as defined in RFC7042 and RFC7043
PhoneNumberA value that conforms to the standard E.164 format as defined here
RgbCSS RGB color as defined here
RgbaCSS RGBA color as defined here
UtcOffsetA value of format ±hh:mm

Most of these scalars are built on top of native .NET types. An Email Address for example is represented as a string, but just returning a string from our resolver would result in Hot Chocolate interpreting it as a StringType. We need to explicitly specify that the returned type (string) should be treated as an EmailAddressType.

C#
[GraphQLType(typeof(EmailAddressType))]
public string GetEmail() => "[email protected]";

Learn more about explicitly specifying GraphQL types

NodaTime

We also offer a package specifically for NodaTime.

It can be installed like the following.

Bash
dotnet add package HotChocolate.Types.NodaTime
Warning
All HotChocolate.* packages need to have the same version.

Available Scalars:

TypeDescriptionExample
DateTimeZoneA NodaTime DateTimeZone"Europe/Rome"
DurationA NodaTime Duration"-123:07:53:10.019"
InstantA NodaTime Instant"2020-02-20T17:42:59Z"
IsoDayOfWeekA NodaTime IsoDayOfWeek7
LocalDateA NodaTime LocalDate"2020-12-25"
LocalDateTimeA NodaTime LocalDateTime"2020-12-25T13:46:78"
LocalTimeA NodaTime LocalTime"12:42:13.03101"
OffsetDateTimeA NodaTime OffsetDateTime"2020-12-25T13:46:78+02:35"
OffsetDateA NodaTime OffsetDate"2020-12-25+02:35"
OffsetTimeA NodaTime OffsetTime"13:46:78+02:35"
OffsetA NodeTime Offset"+02:35"
PeriodA NodeTime Period"P-3W3DT139t"
ZonedDateTimeA NodaTime ZonedDateTime"2020-12-31T19:40:13 Asia/Kathmandu +05:45"

When returning a NodaTime type from one of our resolvers, for example a NodaTime.Duration, we also need to explicitly register the corresponding scalar type. In the case of a NodaTime.Duration this would be the DurationType scalar.

C#
public class Query
{
public Duration GetDuration() => Duration.FromMinutes(3);
}
C#
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddType<DurationType>();

This package was originally developed by @shoooe.

Binding behavior

Hot Chocolate binds most of the native .NET types automatically. A System.String is for example automatically mapped to a StringType in the schema.

We can override these mappings by explicitly specifying type bindings.

C#
builder.Services
.AddGraphQLServer()
.BindRuntimeType<string, StringType>();

Furthermore, we can also bind scalars to arrays or type structures:

C#
builder.Services
.AddGraphQLServer()
.BindRuntimeType<byte[], Base64StringType>();

Hot Chocolate only exposes the used scalars in the generated schema, keeping it simple and clean.

Custom Converters

We can reuse existing scalar types and bind them to different runtime types by specifying converters.

We could for example register converters between NodaTime's OffsetDateTime and .NET's DateTimeOffset to reuse the existing DateTimeType.

C#
public class Query
{
public OffsetDateTime GetDateTime(OffsetDateTime offsetDateTime)
{
return offsetDateTime;
}
}
C#
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.BindRuntimeType<OffsetDateTime, DateTimeType>()
.AddTypeConverter<OffsetDateTime, DateTimeOffset>(
x => x.ToDateTimeOffset())
.AddTypeConverter<DateTimeOffset, OffsetDateTime>(
x => OffsetDateTime.FromDateTimeOffset(x));

Scalar Options

Some scalars like TimeSpan or Uuid have options like their serialization format.

We can specify these options by registering the scalar explicitly.

C#
builder.Services
.AddGraphQLServer()
.AddType(new UuidType('D'));

Custom Scalars

A scalar type converts values between their GraphQL wire format and their .NET runtime representation. Each custom scalar must handle four conversion scenarios:

MethodDirectionPurpose
OnCoerceInputLiteralGraphQL literal → .NETParses values embedded directly in a query, e.g. { field(arg: "value") }
OnCoerceInputValueJSON → .NETParses values provided as variables in the request
OnCoerceOutputValue.NET → JSONWrites resolver results to the response
OnValueToLiteral.NET → GraphQL literalConverts default values for schema introspection

The easiest way to create a custom scalar is to extend ScalarType<TRuntimeType, TLiteral>, which provides the basic scaffolding.

C#
public sealed class CreditCardNumberType : ScalarType<string, StringValueNode>
{
private readonly ICreditCardValidator _validator;
// we can inject services that have been registered
// with the DI container
public CreditCardNumberType(ICreditCardValidator validator)
: base("CreditCardNumber")
{
_validator = validator;
Description = "Represents a credit card number";
}
protected override string OnCoerceInputLiteral(StringValueNode valueLiteral)
{
AssertCreditCardNumberFormat(valueLiteral.Value);
return valueLiteral.Value;
}
protected override string OnCoerceInputValue(
JsonElement inputValue,
IFeatureProvider context)
{
var value = inputValue.GetString()!;
AssertCreditCardNumberFormat(value);
return value;
}
protected override void OnCoerceOutputValue(
string runtimeValue,
ResultElement resultValue)
{
AssertCreditCardNumberFormat(runtimeValue);
resultValue.SetStringValue(runtimeValue);
}
protected override StringValueNode OnValueToLiteral(string runtimeValue)
{
AssertCreditCardNumberFormat(runtimeValue);
return new StringValueNode(runtimeValue);
}
private void AssertCreditCardNumberFormat(string number)
{
if (!_validator.ValidateCreditCard(value))
{
throw new LeafCoercionException(
"The specified value is not a valid credit card number.",
this);
}
}
}

Specialized Base Classes

Hot Chocolate provides specialized base classes for common scalar patterns that handle much of the boilerplate for you.

Integer Scalars

Use IntegerTypeBase<T> for scalars that represent numeric values with min/max constraints. The base class handles parsing, validation, and range checking automatically.

C#
public class TcpPortType : IntegerTypeBase<int>
{
public TcpPortType()
: base("TcpPort", min: 1, max: 65535)
{
Description = "A valid TCP port number (1-65535)";
}
protected override int OnCoerceInputLiteral(IntValueNode valueLiteral)
=> valueLiteral.ToInt32();
protected override int OnCoerceInputValue(JsonElement inputValue)
=> inputValue.GetInt32();
public override void OnCoerceOutputValue(int runtimeValue, ResultElement resultValue)
=> resultValue.SetNumberValue(runtimeValue);
public override IValueNode OnValueToLiteral(int runtimeValue)
=> new IntValueNode(runtimeValue);
}

The IntegerTypeBase automatically validates that values fall within the specified range and throws a LeafCoercionException if they don't. To customize the error message, override the FormatError method:

C#
protected override LeafCoercionException FormatError(int runtimeValue)
=> new LeafCoercionException(
$"The value {runtimeValue} is not a valid TCP port. Must be between 1 and 65535.",
this);

Hot Chocolate also provides a FloatTypeBase<T> for floating-point scalars (float, double, decimal) that need min/max range validation.

Regex-Based Scalars

Use RegexType for string scalars that must match a specific pattern. This is ideal for formats like phone numbers, postal codes, or identifiers.

C#
public class HexColorType : RegexType
{
public HexColorType()
: base(
"HexColor",
@"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
"A hex color code, e.g. #FF5733 or #F53")
{
}
}

You can also instantiate RegexType directly when registering scalars:

C#
builder.Services
.AddGraphQLServer()
.AddType(new RegexType(
"PostalCode",
@"^\d{5}(-\d{4})?$",
"US postal code in format 12345 or 12345-6789"));

Like IntegerTypeBase and FloatTypeBase, RegexType automatically validates values and throws a LeafCoercionException if they don't match the pattern. To customize the error message, override the FormatException method:

C#
protected override LeafCoercionException FormatException(string runtimeValue)
=> new LeafCoercionException(
$"'{runtimeValue}' is not a valid hex color. Expected format: #RGB or #RRGGBB.",
this);
Last updated on February 17, 2026 by Michael Staib