website/pages/docs/custom-scalars.mdx
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:
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.
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:
Common examples of useful custom scalars include:
DateTime: An ISO 8601 timestamp stringEmail: A syntactically valid email addressURL: A well-formed web addressBigInt: An integer that exceeds the range of GraphQL's built-in IntUUID: A string that follows a specific identifier formatCustom scalars are not a substitute for object types. Avoid using a custom scalar if:
JSON or Any.Custom scalars reduce introspection and composability. Use them to extend GraphQL's scalar system, not to replace structured types altogether.
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
independent of their name.The following example is a custom DateTime scalar that handles ISO-8601 encoded
date strings:
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.