# Scalars - Hot Chocolate

> Reference for GraphQL scalars in Hot Chocolate: built-in types like ID and DateTime, the NodaTime package, binding behavior, and custom scalar definitions.

Canonical source: https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars

Scalars are the leaf types in 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 convert between the GraphQL wire format (JSON) and the .NET runtime representation. The GraphQL specification specifies five core scalars (`String`, `Int`, `Float`, `Boolean`, and `ID`), which form the foundation of every GraphQL server.

Hot Chocolate comes with many more scalars than the GraphQL core scalars, mapping to common .NET primitive types and structs.

| .NET Type         | GraphQL Scalar | Binding  | Notes                                                     | Spec                                                                 |
| ----------------- | -------------- | -------- | --------------------------------------------------------- | -------------------------------------------------------------------- |
| string            | String         | Implicit | UTF-8 character sequence                                  | [Spec](https://spec.graphql.org/draft/#sec-String)                   |
| bool              | Boolean        | Implicit | true or false                                             | [Spec](https://spec.graphql.org/draft/#sec-Boolean)                  |
| int               | Int            | Implicit | Signed 32-bit integer                                     | [Spec](https://spec.graphql.org/draft/#sec-Int)                      |
| float, double     | Float          | Implicit | IEEE 754 double-precision                                 | [Spec](https://spec.graphql.org/draft/#sec-Float)                    |
| string, int, Guid | ID             | Explicit | Unique identifier, always serialized as string            | [Spec](https://spec.graphql.org/draft/#sec-ID)                       |
| decimal           | Decimal        | Implicit | High-precision decimal (separate from Float)              | [Spec](https://scalars.graphql.org/chillicream/decimal.html)         |
| long              | Long           | Implicit | Signed 64-bit integer                                     | [Spec](https://scalars.graphql.org/chillicream/long.html)            |
| short             | Short          | Implicit | Signed 16-bit integer                                     | [Spec](https://scalars.graphql.org/chillicream/short.html)           |
| DateTime          | DateTime       | Implicit | Date and time with time zone offset                       | [Spec](https://scalars.graphql.org/chillicream/date-time.html)       |
| DateTimeOffset    | DateTime       | Implicit | Date and time with time zone offset                       | [Spec](https://scalars.graphql.org/chillicream/date-time.html)       |
| DateOnly          | LocalDate      | Implicit | Date without time or time zone                            | [Spec](https://scalars.graphql.org/chillicream/local-date.html)      |
| DateOnly          | Date           | Explicit | Date in UTC                                               | [Spec](https://scalars.graphql.org/chillicream/date.html)            |
| DateTime          | LocalDateTime  | Explicit | Date and time without time zone                           | [Spec](https://scalars.graphql.org/chillicream/local-date-time.html) |
| TimeOnly          | LocalTime      | Implicit | Time of day without date or time zone                     | [Spec](https://scalars.graphql.org/chillicream/local-time.html)      |
| TimeSpan          | Duration       | Implicit | Duration of time                                          | [Spec](https://scalars.graphql.org/chillicream/duration.html)        |
| Guid              | UUID           | Implicit | Universally unique identifier (RFC 9562)                  | [Spec](https://scalars.graphql.org/chillicream/uuid.html)            |
| Uri               | URI            | Implicit | Uniform resource identifier (replaces URL for System.Uri) | [Spec](https://scalars.graphql.org/chillicream/uri.html)             |
| Uri               | URL            | Explicit | Deprecated, use URI instead                               | [Spec](https://scalars.graphql.org/chillicream/url.html)             |
| byte\[\]          | Base64String   | Implicit | Base64-encoded byte array (replaces deprecated ByteArray) | [Spec](https://scalars.graphql.org/chillicream/base64-string.html)   |
| byte              | UnsignedByte   | Implicit | Unsigned 8-bit integer                                    | [Spec](https://scalars.graphql.org/chillicream/unsigned-byte.html)   |
| sbyte             | Byte           | Implicit | Signed 8-bit integer                                      | [Spec](https://scalars.graphql.org/chillicream/byte.html)            |
| ushort            | UnsignedShort  | Implicit | Unsigned 16-bit integer                                   | [Spec](https://scalars.graphql.org/chillicream/unsigned-short.html)  |
| uint              | UnsignedInt    | Implicit | Unsigned 32-bit integer                                   | [Spec](https://scalars.graphql.org/chillicream/unsigned-int.html)    |
| ulong             | UnsignedLong   | Implicit | Unsigned 64-bit integer                                   | [Spec](https://scalars.graphql.org/chillicream/unsigned-long.html)   |
| JsonElement       | Any            | Implicit | Any valid GraphQL value                                   | [Spec](https://scalars.graphql.org/chillicream/any.html)             |

Note

Hot Chocolate only exposes scalars that your schema uses. Unused scalars do not appear in the generated schema.

### ID

The GraphQL `ID` scalar is not automatically mapped to a .NET type because it is a semantic type representing a unique identifier. You must annotate fields explicitly to use `ID`.

`ID` values are always serialized as strings in responses, but clients can provide `int` or `string` values as variables or GraphQL literals. On the server side, you can use `string`, `int`, or `Guid` as the runtime type for `ID` fields.

C#

```
public sealed class Product
{
    [ID]
    public int Id { get; set; }
}

[QueryType]
public static partial class ProductQueries
{
    public static Product GetProduct([ID] int id)
    {
        // Omitted code for brevity
    }
}
```

### DateTime Scalars

You can use `HotChocolate.Types.DateTimeOptions` to configure the built-in BCL-backed `DateTime`, `LocalDateTime`, and `LocalTime` scalars. With these options, you can:

- Set how many fractional second digits are accepted during parsing (`InputPrecision`, up to 9)
- Control how many fractional second digits are written during serialization (`OutputPrecision`, up to 7)
- Require input to match the expected scalar format before parsing (`ValidateInputFormat`)
- Always emit fractional seconds in serialized output (when `OutputPrecision > 0`), padded with trailing zeros up to `OutputPrecision` (`AlwaysOutputFractionalSeconds`)

Although the built-in scalars can parse up to 9 fractional second digits, the underlying BCL types only preserve up to 7 digits (100-nanosecond precision), so additional digits are rounded during parsing.

By default, trailing zeros are stripped from the fractional component and the fractional component is omitted entirely when zero (for example, `2023-12-24T15:30:00.5000000Z` is emitted as `2023-12-24T15:30:00.5Z`). Set `AlwaysOutputFractionalSeconds = true` to keep a fixed-width representation regardless of value. The option has no effect when `OutputPrecision` is `0`, since there are no fractional second digits to emit.

To customize the built-in scalars, register configured scalar instances explicitly:

C#

```
builder
    .AddGraphQL()
    .AddType(new DateTimeType(new DateTimeOptions
    {
        OutputPrecision = 3,
        AlwaysOutputFractionalSeconds = true
    }))
    .AddType(new LocalDateTimeType(new DateTimeOptions
    {
        OutputPrecision = 3,
        AlwaysOutputFractionalSeconds = true
    }))
    .AddType(new LocalTimeType(new DateTimeOptions
    {
        OutputPrecision = 3,
        AlwaysOutputFractionalSeconds = true
    }));
```

### UUID Format

The `UUID` scalar supports multiple serialization formats:

| Specifier   | Format                                                               |
| ----------- | -------------------------------------------------------------------- |
| N           | 00000000000000000000000000000000                                     |
| 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` always returns values in the specified format. When parsing input, it tries the specified format first, then falls back to other formats.

To change the default format:

C#

```
builder
    .AddGraphQL()
    .AddType(new UuidType('N'));
```

### Any Scalar

The `Any` scalar is comparable to `object` in C#. It accepts any literal and can return any output type.

SDL

```
type Query {
  metadata(filter: Any): Any
}
```

All of the following queries are valid against an `Any` argument:

GraphQL

```
{
  a: metadata(filter: 1)
  b: metadata(filter: [1, 2, 3])
  c: metadata(filter: "text")
  d: metadata(filter: true)
  e: metadata(filter: { key: "value", nested: { count: 1 } })
}
```

#### Runtime type

The `Any` scalar uses `System.Text.Json.JsonElement` as its .NET runtime type. Fields annotated with `Any` expect resolvers to return a `JsonElement`.

To access an argument dynamically:

C#

```
JsonElement value = context.ArgumentValue<JsonElement>("filter");

if (value.ValueKind == JsonValueKind.Object)
{
    string? name = value.GetProperty("name").GetString();
}
```

To deserialize into a strongly typed model:

C#

```
MyFilter filter = context.ArgumentValue<MyFilter>("filter");
```

You can also inspect the value kind to determine how the argument was provided:

C#

```
ValueKind kind = context.ArgumentKind("filter");
```

The `ValueKind` enum tells you which kind of literal represents the argument:

C#

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

> An integer literal can contain a long value, and a float literal can be a decimal or a float.

#### Returning dictionaries and arbitrary .NET types

By default, `Any` expects a `JsonElement`. To return common .NET types such as `Dictionary<string, object>` or `ExpandoObject`, register the JSON type converter:

C#

```
builder
    .AddGraphQL()
    .AddJsonTypeConverter();
```

With the converter registered, resolvers can return dictionaries or any JSON-serializable object:

C#

```
[GraphQLType<AnyType>]
public object GetData() => new Dictionary<string, object>
{
    { "name", "John" },
    { "age", 30 }
};
```

#### Custom type serialization

For custom reference types, register a dedicated converter to control serialization. For example, to serialize `TimeZoneInfo` as its string ID instead of a full JSON object:

C#

```
builder
    .AddGraphQL()
    .AddTypeConverter<TimeZoneInfo, JsonElement>(
        value => JsonSerializer.SerializeToElement(value.Id));
```

The resolver can then return the type directly:

C#

```
[GraphQLType<AnyType>]
public TimeZoneInfo GetTimezone() => TimeZoneInfo.Utc; // serializes as "UTC"
```

## Additional Scalars Package

For more specific use cases, install the `HotChocolate.Types.Scalars` package:

Bash

```
dotnet add package HotChocolate.Types.Scalars
```

Warning

All `HotChocolate.*` packages need to have the same version.

| Type         | Description                                                                                                                                                                                 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| EmailAddress | Email address as defined in [RFC 5322](https://tools.ietf.org/html/rfc5322)                                                                                                                 |
| HexColor     | HEX color code                                                                                                                                                                              |
| Hsl          | CSS HSL color as defined [here](https://developer.mozilla.org/docs/Web/CSS/color%5Fvalue#hsl%5Fcolors)                                                                                      |
| Hsla         | CSS HSLA color as defined [here](https://developer.mozilla.org/docs/Web/CSS/color%5Fvalue#hsl%5Fcolors)                                                                                     |
| IPv4         | IPv4 address as defined [here](https://en.wikipedia.org/wiki/IPv4)                                                                                                                          |
| IPv6         | IPv6 address as defined in [RFC 8064](https://tools.ietf.org/html/rfc8064)                                                                                                                  |
| Isbn         | ISBN-10 or ISBN-13 number as defined [here](https://en.wikipedia.org/wiki/International%5FStandard%5FBook%5FNumber)                                                                         |
| Latitude     | Decimal degrees latitude number                                                                                                                                                             |
| Longitude    | Decimal degrees longitude number                                                                                                                                                            |
| MacAddress   | IEEE 802 48-bit (MAC-48/EUI-48) and 64-bit (EUI-64) Mac addresses as defined in [RFC 7042](https://tools.ietf.org/html/rfc7042#page-19) and [RFC 7043](https://tools.ietf.org/html/rfc7043) |
| PhoneNumber  | E.164 format phone number as defined [here](https://en.wikipedia.org/wiki/E.164)                                                                                                            |
| Rgb          | CSS RGB color as defined [here](https://developer.mozilla.org/docs/Web/CSS/color%5Fvalue#rgb%5Fcolors)                                                                                      |
| Rgba         | CSS RGBA color as defined [here](https://developer.mozilla.org/docs/Web/CSS/color%5Fvalue#rgb%5Fcolors)                                                                                     |
| UtcOffset    | A value of format ±hh:mm                                                                                                                                                                    |

Many of these scalars are built on native .NET types. An email address, for example, is represented as a `string`, but returning a `string` from your resolver causes Hot Chocolate to interpret it as a `StringType`. You need to specify the scalar type explicitly:

C#

```
[GraphQLType<EmailAddressType>]
public string GetEmail() => "test@example.com";
```

[Learn more about explicit types](https://chillicream.com/docs/hotchocolate/defining-a-schema/object-types#explicit-types)

## NodaTime Scalars

For [NodaTime](https://github.com/nodatime/nodatime) types, install the dedicated package:

Bash

```
dotnet add package HotChocolate.Types.NodaTime
```

Warning

All `HotChocolate.*` packages need to have the same version.

`HotChocolate.Types.NodaTime` provides alternative implementations of the same five built-in date and time scalars defined by the specifications on [scalars.graphql.org](https://scalars.graphql.org/):

| GraphQL Scalar                                                                | NodaTime Runtime Type                                                         | Replaces Built-in Mapping     |
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------- |
| [DateTime](https://scalars.graphql.org/chillicream/date-time.html)            | [OffsetDateTime](https://nodatime.org/3.2.x/api/NodaTime.OffsetDateTime.html) | DateTimeOffset                |
| [Duration](https://scalars.graphql.org/chillicream/duration.html)             | [Duration](https://nodatime.org/3.2.x/api/NodaTime.Duration.html)             | (see note below for TimeSpan) |
| [LocalDate](https://scalars.graphql.org/chillicream/local-date.html)          | [LocalDate](https://nodatime.org/3.2.x/api/NodaTime.LocalDate.html)           | DateOnly                      |
| [LocalDateTime](https://scalars.graphql.org/chillicream/local-date-time.html) | [LocalDateTime](https://nodatime.org/3.2.x/api/NodaTime.LocalDateTime.html)   | DateTime                      |
| [LocalTime](https://scalars.graphql.org/chillicream/local-time.html)          | [LocalTime](https://nodatime.org/3.2.x/api/NodaTime.LocalTime.html)           | TimeOnly                      |

Note

The `Duration` scalar uses `NodaTime.Duration` as its runtime type. Calling `AddNodaTime()` does **not** automatically bind `System.TimeSpan` to `DurationType` or register `TimeSpan`↔`NodaTime.Duration` converters, as the runtime types are not compatible.

These NodaTime scalars expose the same `@specifiedBy` URLs and implement the same GraphQL scalar specifications as the built-in versions, but they use NodaTime runtime types and may differ subtly in behavior. For example, the NodaTime implementations support up to 9 fractional second digits (nanosecond precision), whereas the equivalent BCL types only support up to 7 fractional second digits (100-nanosecond precision).

Register them with `AddNodaTime()`:

C#

```
builder
    .AddGraphQL()
    .AddNodaTime();
```

`AddNodaTime()` registers the five scalar types above and configures the related CLR bindings and converters automatically.

If you prefer, you can still register individual scalar types explicitly. For example:

C#

```
using NodaTimeDurationType = HotChocolate.Types.NodaTime.DurationType;

builder
    .AddGraphQL()
    .AddType<NodaTimeDurationType>();
```

### NodaTime scalar options

`HotChocolate.Types.NodaTime.DateTimeOptions` configures the NodaTime-backed `DateTime`, `LocalDateTime`, and `LocalTime` scalars:

- `InputPrecision` controls how many fractional second digits are accepted during parsing, up to `9`.
- `OutputPrecision` controls how many fractional second digits are written during serialization, up to `9`.
- `AlwaysOutputFractionalSeconds` always emits fractional seconds in serialized output when `OutputPrecision > 0`, padded with trailing zeros up to `OutputPrecision`. By default, trailing zeros are stripped and the fractional component is omitted entirely when zero. The option has no effect when `OutputPrecision` is `0`.

Unlike the built-in BCL-backed scalars, the NodaTime implementations preserve up to 9 fractional second digits (nanosecond precision).

If you need non-default NodaTime precision settings, register those scalar types individually instead of using `AddNodaTime()`:

C#

```
using NodaTimeDateTimeOptions = HotChocolate.Types.NodaTime.DateTimeOptions;
using NodaTimeDateTimeType = HotChocolate.Types.NodaTime.DateTimeType;
using NodaTimeLocalDateTimeType = HotChocolate.Types.NodaTime.LocalDateTimeType;
using NodaTimeLocalTimeType = HotChocolate.Types.NodaTime.LocalTimeType;

builder
    .AddGraphQL()
    .AddType(new NodaTimeDateTimeType(new NodaTimeDateTimeOptions
    {
        OutputPrecision = 3,
        AlwaysOutputFractionalSeconds = true
    }))
    .AddType(new NodaTimeLocalDateTimeType(new NodaTimeDateTimeOptions
    {
        OutputPrecision = 3,
        AlwaysOutputFractionalSeconds = true
    }))
    .AddType(new NodaTimeLocalTimeType(new NodaTimeDateTimeOptions
    {
        OutputPrecision = 3,
        AlwaysOutputFractionalSeconds = true
    }));
```

## Binding Behavior

You can override the default .NET-to-scalar mappings by specifying type bindings explicitly:

C#

```
builder
    .AddGraphQL()
    .BindRuntimeType<string, StringType>();
```

You can also bind scalars to arrays or complex types:

C#

```
builder
    .AddGraphQL()
    .BindRuntimeType<byte[], Base64StringType>();
```

## Custom Converters

You can reuse existing scalar types with different runtime types by registering converters. For example, to map NodaTime's `OffsetDateTime` to the existing `DateTimeType`:

C#

```
public sealed class ScheduleQueries
{
    public OffsetDateTime GetDateTime(OffsetDateTime offsetDateTime)
    {
        return offsetDateTime;
    }
}
```

C#

```
builder
    .AddGraphQL()
    .AddQueryType<ScheduleQueries>()
    .BindRuntimeType<OffsetDateTime, DateTimeType>()
    .AddTypeConverter<OffsetDateTime, DateTimeOffset>(
        x => x.ToDateTimeOffset())
    .AddTypeConverter<DateTimeOffset, OffsetDateTime>(
        x => OffsetDateTime.FromDateTimeOffset(x));
```

## Custom Scalars

A custom scalar converts values between the GraphQL wire format and a .NET runtime type. Each custom scalar handles four conversion scenarios:

| Method               | Direction               | Purpose                                                         |
| -------------------- | ----------------------- | --------------------------------------------------------------- |
| OnCoerceInputLiteral | GraphQL literal to .NET | Parses values embedded in a query, e.g. { field(arg: "value") } |
| OnCoerceInputValue   | JSON to .NET            | Parses values provided as variables in the request              |
| OnCoerceOutputValue  | .NET to JSON            | Writes resolver results to the response                         |
| OnValueToLiteral     | .NET to GraphQL literal | Converts default values for schema introspection                |

Extend `ScalarType<TRuntimeType, TLiteral>` to create a custom scalar:

C#

```
public sealed class CreditCardNumberType : ScalarType<string, StringValueNode>
{
    private readonly ICreditCardValidator _validator;

    // You can inject services 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 value)
    {
        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.

#### Integer scalars

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

C#

```
public sealed 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();

    protected override void OnCoerceOutputValue(int runtimeValue, ResultElement resultValue)
        => resultValue.SetNumberValue(runtimeValue);

    protected override IValueNode OnValueToLiteral(int runtimeValue)
        => new IntValueNode(runtimeValue);
}
```

`IntegerTypeBase` validates that values fall within the specified range and throws a `LeafCoercionException` if they do not. To customize the error message, override `FormatError`:

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 `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 works well for formats like phone numbers, postal codes, or identifiers.

C#

```
public sealed 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
    .AddGraphQL()
    .AddType(new RegexType(
        "PostalCode",
        @"^\d{5}(-\d{4})?$",
        "US postal code in format 12345 or 12345-6789"));
```

To customize the error message for pattern validation failures, override `FormatException`:

C#

```
protected override LeafCoercionException FormatException(string runtimeValue)
    => new LeafCoercionException(
        $"'{runtimeValue}' is not a valid hex color. Expected format: #RGB or #RRGGBB.",
        this);
```

## Next Steps

- **Need to define object types?** See [Object Types](https://chillicream.com/docs/hotchocolate/defining-a-schema/object-types).
- **Need to accept complex inputs?** See [Input Object Types](https://chillicream.com/docs/hotchocolate/defining-a-schema/input-object-types).
- **Need to define enums?** See [Enums](https://chillicream.com/docs/hotchocolate/defining-a-schema/enums).
- **Need to add custom validation logic?** See [Directives](https://chillicream.com/docs/hotchocolate/defining-a-schema/directives).
[Edit this page on GitHub](https://github.com/ChilliCream/graphql-platform/edit/main/website/content/docs/hotchocolate/defining-a-schema/scalars.md)

Last updated on **June 30, 2026** by **Tobias Tengler**
