Apollo Server 2 is officially end-of-life as of 22 October 2023.

Learn more about upgrading.

Custom scalars


The GraphQL specification includes default scalar types Int, Float, String, Boolean, and ID. Although these scalars cover the majority of use cases, some applications need to support other atomic data types (such as Date) or add validation to an existing type. To enable this, you can define custom scalar types.

Defining a custom scalar

To define a custom scalar, add it to your schema like so:

GraphQL
1scalar MyCustomScalar

Object types in your schema can now contain fields of type MyCustomScalar. However, Apollo Server still needs to know how to interact with values of this new scalar type.

Defining custom scalar logic

After you define a custom scalar type, you need to define how Apollo Server interacts with it. In particular, you need to define:

  • How the scalar's value is represented in your backend

    • This is often the representation used by the driver for your backing data store.

  • How the value's back-end representation is serialized to a JSON-compatible type

  • How the JSON-compatible representation is deserialized to the back-end representation

You define these interactions in an instance of the GraphQLScalarType class.

For more information about the graphql library's type system, see the official documentation.

Example: The Date scalar

The following GraphQLScalarType object defines interactions for a custom scalar that represents a date (this is one of the most commonly implemented custom scalars). It assumes that our backend represents a date with the Date JavaScript object.

JavaScript
1const { GraphQLScalarType, Kind } = require('graphql');
2
3const dateScalar = new GraphQLScalarType({
4  name: 'Date',
5  description: 'Date custom scalar type',
6  serialize(value) {
7    return value.getTime(); // Convert outgoing Date to integer for JSON
8  },
9  parseValue(value) {
10    return new Date(value); // Convert incoming integer to Date
11  },
12  parseLiteral(ast) {
13    if (ast.kind === Kind.INT) {
14      return new Date(parseInt(ast.value, 10)); // Convert hard-coded AST string to integer and then to Date
15    }
16    return null; // Invalid hard-coded value (not an integer)
17  },
18});

This initialization defines the following methods:

  • serialize

  • parseValue

  • parseLiteral

Together, these methods describe how Apollo Server interacts with the scalar in every scenario.

serialize

The serialize method converts the scalar's back-end representation to a JSON-compatible format so Apollo Server can include it in an operation response.

In the example above, the Date scalar is represented on the backend by the Date JavaScript object. When we send a Date scalar in a GraphQL response, we serialize it as the integer value returned by the getTime function of a JavaScript Date object.

Note that Apollo Client cannot automatically interpret custom scalars (see issue), so your client must define custom logic to deserialize this value as needed.

parseValue

The parseValue method converts the scalar's serialized JSON value to its back-end representation before it's added to a resolver's args.

Apollo Server calls this method when the scalar is provided by a client as a GraphQL variable for an argument. (When a scalar is provided as a hard-coded argument in the operation string, parseLiteral is called instead.)

parseLiteral

When an incoming query string includes the scalar as a hard-coded argument value, that value is part of the query document's abstract syntax tree (AST). Apollo Server calls the parseLiteral method to convert the value's AST representation (which is always a string) to the scalar's back-end representation.

In the example above, parseLiteral converts the AST value from a string to an integer, and then converts from integer to Date to match the result of parseValue.

Providing custom scalars to Apollo Server

After you define your GraphQLScalarType instance, you include it in the same resolver map that contains resolvers for your schema's other types and fields:

JavaScript
1const { ApolloServer, gql } = require('apollo-server');
2const { GraphQLScalarType, Kind } = require('graphql');
3
4const typeDefs = gql`
5  scalar Date
6
7  type Event {
8    id: ID!
9    date: Date!
10  }
11
12  type Query {
13    events: [Event!]
14  }
15`;
16
17const dateScalar = new GraphQLScalarType({
18  // See definition above
19});
20
21const resolvers = {
22  Date: dateScalar
23  // ...other resolver definitions...
24};
25
26const server = new ApolloServer({
27  typeDefs,
28  resolvers
29});

Example: Restricting integers to odd values

In this example, we create a custom scalar called Odd that can only contain odd integers:

JavaScript
1const { ApolloServer, gql } = require('apollo-server');
2const { GraphQLScalarType, Kind } = require('graphql');
3
4// Basic schema
5const typeDefs = gql`
6  scalar Odd
7
8  type MyType {
9    oddValue: Odd
10  }
11`;
12
13// Validation function
14function oddValue(value) {
15  return value % 2 === 1 ? value : null;
16}
17
18const resolvers = {
19  Odd: new GraphQLScalarType({
20    name: 'Odd',
21    description: 'Odd custom scalar type',
22    parseValue: oddValue,
23    serialize: oddValue,
24    parseLiteral(ast) {
25      if (ast.kind === Kind.INT) {
26        return oddValue(parseInt(ast.value, 10));
27      }
28      return null;
29    },
30  }),
31};
32
33const server = new ApolloServer({ typeDefs, resolvers });
34
35server.listen().then(({ url }) => {
36  console.log(`🚀 Server ready at ${url}`)
37});

Importing a third-party custom scalar

If another library defines a custom scalar, you can import it and use it just like any other symbol.

For example, the graphql-type-json package defines the GraphQLJSON object, which is an instance of GraphQLScalarType. You can use this object to define a JSON scalar that accepts any value that is valid JSON.

First, install the library:

shell
1$ npm install graphql-type-json

Then require the GraphQLJSON object and add it to the resolver map as usual:

JavaScript
1const { ApolloServer, gql } = require('apollo-server');
2const GraphQLJSON = require('graphql-type-json');
3
4const typeDefs = gql`
5  scalar JSON
6
7  type MyObject {
8    myField: JSON
9  }
10
11  type Query {
12    objects: [MyObject]
13  }
14`;
15
16const resolvers = {
17  JSON: GraphQLJSON
18  // ...other resolvers...
19};
20
21const server = new ApolloServer({ typeDefs, resolvers });
22
23server.listen().then(({ url }) => {
24  console.log(`🚀 Server ready at ${url}`)
25});
Feedback

Edit on GitHub

Forums