Custom Scalars
GraphQL comes with five built-in scalar types (String, Int, Float, Boolean, ID), but sometimes you need more specific types. Custom scalars let you define your own primitive types.
Common use cases include DateTime, Email, URL, JSON, and other specialized types.
Using Custom Scalars
First, you declare the custom scalar in your schema. Then you need to implement serialization and parsing logic.
const { GraphQLScalarType } = require('graphql');
const typeDefs = `
scalar DateTime
scalar Email
scalar JSON
type Post {
id: ID!
title: String!
content: String!
createdAt: DateTime!
updatedAt: DateTime!
}
type User {
id: ID!
name: String!
email: Email!
metadata: JSON
}
`;
The schema declares DateTime, Email, and JSON as scalar types. Now we need to implement how they work.
Implementing DateTime
Here's how to implement a DateTime scalar that handles ISO date strings:
const DateTimeScalar = new GraphQLScalarType({
name: 'DateTime',
description: 'ISO 8601 date-time string',
serialize(value) {
// Convert from internal value to JSON
if (value instanceof Date) {
return value.toISOString();
}
throw new Error('DateTime must be a Date object');
},
parseValue(value) {
// Convert from JSON to internal value
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new Error('Invalid DateTime format');
}
return date;
},
parseLiteral(ast) {
// Convert from query string to internal value
if (ast.kind === 'StringValue') {
const date = new Date(ast.value);
if (isNaN(date.getTime())) {
throw new Error('Invalid DateTime format');
}
return date;
}
throw new Error('DateTime must be a string');
},
});
The scalar has three methods: serialize (server to client), parseValue (client to server), and parseLiteral (query string to server).
Using graphql-scalars Library
You don't always need to implement scalars from scratch. The graphql-scalars library provides many common types:
const {
DateTimeResolver,
EmailAddressResolver,
URLResolver,
JSONResolver,
} = require('graphql-scalars');
const resolvers = {
DateTime: DateTimeResolver,
Email: EmailAddressResolver,
URL: URLResolver,
JSON: JSONResolver,
// ... other resolvers
};
The library handles all the serialization and parsing logic for you. Just import and use.
When to Use Custom Scalars
Use custom scalars when you need:
Date/Time: ISO 8601 formatted dates (DateTime scalar)
Email: Validated email addresses (EmailAddress scalar)
URL: Validated URLs (URL scalar)
JSON: Flexible JSON data (JSON scalar)
PositiveInt: Numbers greater than zero
type Mutation {
createUser(input: CreateUserInput!): User!
}
input CreateUserInput {
name: String!
email: EmailAddress!
birthday: DateTime
website: URL
preferences: JSON
}
Custom scalars add validation and clarity to your API. Clients know exactly what format to expect.