---
title: Using Custom Scalars
---

# Custom Scalars: When and How to Use Them

In GraphQL, scalar types represent primitive data like strings, numbers, and booleans. 
The GraphQL specification defines five built-in scalars: `Int`, `Float`, 
`String`, `Boolean`, and `ID`.

However, these default types don't cover all the formats or domain-specific values real-world
APIs often need. For example, you might want to represent a timestamp as an ISO 8601 string, or
ensure a user-submitted field is a valid email address. In these cases, you can define a custom
scalar type.

In GraphQL.js, custom scalars are created using the `GraphQLScalarType` class. This gives you
full control over how values are serialized, parsed, and validated.

Here’s a simple example of a custom scalar that handles date-time strings:

```js
import { GraphQLScalarType, Kind } from 'graphql';

const DateTime = new GraphQLScalarType({
  name: 'DateTime',
  description: 'An ISO-8601 encoded UTC date string.',
  serialize(value) {
    return value instanceof Date ? value.toISOString() : null;
  },
  parseValue(value) {
    return typeof value === 'string' ? new Date(value) : null;
  },
  parseLiteral(ast) {
    return ast.kind === Kind.STRING ? new Date(ast.value) : null;
  },
});
```
Custom scalars offer flexibility, but they also shift responsibility onto you. You're 
defining not just the format of a value, but also how it is validated and how it moves 
through your schema.

This guide covers when to use custom scalars and how to define them in GraphQL.js.

## When to use custom scalars

Define a custom scalar when you need to enforce a specific format, encapsulate domain-specific 
logic, or standardize a primitive value across your schema. For example:

- Validation: Ensure that inputs like email addresses, URLs, or date strings match a 
strict format.
- Serialization and parsing: Normalize how values are converted between internal and 
client-facing formats.
- Domain primitives: Represent domain-specific values that behave like scalars, such as 
UUIDs or currency codes.

Common examples of useful custom scalars include:

- `DateTime`: An ISO 8601 timestamp string
- `Email`: A syntactically valid email address
- `URL`: A well-formed web address
- `BigInt`: An integer that exceeds the range of GraphQL's built-in `Int`
- `UUID`: A string that follows a specific identifier format

## When not to use a custom scalar

Custom scalars are not a substitute for object types. Avoid using a custom scalar if:

- The value naturally contains multiple fields or nested data (even if serialized as a string).
- Validation depends on relationships between fields or requires complex cross-checks.
- You're tempted to bypass GraphQL’s type system using a catch-all scalar like `JSON` or `Any`.

Custom scalars reduce introspection and composability. Use them to extend GraphQL's scalar
system, not to replace structured types altogether.

## How to define a custom scalar in GraphQL.js

In GraphQL.js, a custom scalar is defined by creating an instance of `GraphQLScalarType`, 
providing a name, description, and three functions:

- `serialize`: How the server sends internal values to clients.
- `parseValue`: How the server parses incoming variable values.
- `parseLiteral`: How the server parses inline values in queries.
- `specifiedByURL` (optional): A URL specifying the behavior of your scalar;
  this can be used by clients and tooling to recognize and handle common scalars
  such as [date-time](https://scalars.graphql.org/andimarek/date-time.html)
  independent of their name.

The following example is a custom `DateTime` scalar that handles ISO-8601 encoded
date strings:

```js
import { GraphQLScalarType, Kind } from 'graphql';

const DateTime = new GraphQLScalarType({
  name: 'DateTime',
  description: 'An ISO-8601 encoded UTC date string.',
  specifiedByURL: 'https://scalars.graphql.org/andimarek/date-time.html',
  
  serialize(value) {
    if (!(value instanceof Date)) {
      throw new TypeError('DateTime can only serialize Date instances');
    }
    return value.toISOString();
  },

  parseValue(value) {
    const date = new Date(value);
    if (isNaN(date.getTime())) {
      throw new TypeError(`DateTime cannot represent an invalid date: ${value}`);
    }
    return date;
  },

  parseLiteral(ast) {
    if (ast.kind !== Kind.STRING) {
      throw new TypeError(`DateTime can only parse string values, but got: ${ast.kind}`);
    }
    const date = new Date(ast.value);
    if (isNaN(date.getTime())) {
      throw new TypeError(`DateTime cannot represent an invalid date: ${ast.value}`);
    }
    return date;
  },
});
```

These functions give you full control over validation and data flow.

## Learn more

- [Custom Scalars: Best Practices and Testing](./advanced-custom-scalars): Dive deeper into validation, testing, and building production-grade custom scalars.