Skip to content

GraphQL Custom Scalar Types — Extending Primitives for Domain-Specific Values

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Graphql Custom Scalar Types. We cover key concepts, practical examples, and best practices to help you master this topic.

GraphQL custom scalars extend the built-in types (String, Int, Float, Boolean, ID) to represent domain-specific values like dates, URLs, email addresses, and JSON objects with custom validation.

What You'll Learn

  • Defining custom scalar types
  • Implementing parseValue, serialize, and parseLiteral
  • Using community scalar libraries

Why It Matters

Built-in scalars lack validation and semantic meaning. Custom scalars enforce data integrity and make schemas self-documenting.

Code Examples

# Custom scalar definitions
scalar DateTime
scalar URL
scalar Email
scalar JSON
scalar PositiveInt
scalar PhoneNumber

type User {
  id: ID!
  email: Email!
  website: URL
  createdAt: DateTime!
  metadata: JSON
  age: PositiveInt
  phone: PhoneNumber
}
// Custom scalar implementation
const { GraphQLScalarType, Kind } = require('graphql');

const DateTimeScalar = new GraphQLScalarType({
  name: 'DateTime',
  description: 'ISO 8601 date-time string',
  serialize(value) {
    return value instanceof Date ? value.toISOString() : value;
  },
  parseValue(value) {
    const date = new Date(value);
    if (isNaN(date.getTime())) {
      throw new TypeError('Invalid date');
    }
    return date;
  },
  parseLiteral(ast) {
    if (ast.kind === Kind.STRING) {
      const date = new Date(ast.value);
      if (isNaN(date.getTime())) {
        throw new TypeError('Invalid date');
      }
      return date;
    }
    throw new TypeError('DateTime must be a string');
  }
});

const EmailScalar = new GraphQLScalarType({
  name: 'Email',
  description: 'Valid email address',
  serialize(value) { return value; },
  parseValue(value) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(value)) {
      throw new TypeError('Invalid email address');
    }
    return value;
  },
  parseLiteral(ast) {
    if (ast.kind === Kind.STRING) {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(ast.value)) {
        throw new TypeError('Invalid email address');
      }
      return ast.value;
    }
    throw new TypeError('Email must be a string');
  }
});
// Using graphql-scalars library
const { DateTimeResolver, URLResolver, JSONResolver } = require('graphql-scalars');

const typeDefs = `
  scalar DateTime
  scalar URL
  scalar JSON
`;

const resolvers = {
  DateTime: DateTimeResolver,
  URL: URLResolver,
  JSON: JSONResolver
};

Common Mistakes

1. Reimplementing Standard Scalars

Use community libraries for common scalars like DateTime and URL.

2. Not Validating Input in parseValue

Custom scalars should validate both input and output to catch errors early.

3. Using String Instead of Custom Scalars

Semantic types improve API documentation and client code clarity.

4. Ignoring parseLiteral for AST Values

Variables use parseValue, but inline literals use parseLiteral. Implement both.

5. Creating Too Many Custom Scalars

Only create custom scalars for values with specific validation or formatting needs.

Practice Questions

  1. How do you define a custom scalar in SDL?
  2. What three methods must a custom scalar implement?
  3. Why use custom scalars instead of String?
  4. What is the difference between parseValue and parseLiteral?
  5. What library provides common custom scalars?

Answers:

  1. Use the scalar keyword: scalar DateTime.
  2. serialize, parseValue, and parseLiteral.
  3. For validation, semantic meaning, and self-documenting schemas.
  4. parseValue handles variable input; parseLiteral handles inline literal values in queries.
  5. graphql-scalars provides DateTime, URL, JSON, Email, and many more.

Challenge: Create custom scalars for a financial application: Currency (validated format), Percentage (0-100 range), UUID, and UnixTimestamp. Implement all three methods and add comprehensive validation.

FAQ

Can custom scalars return complex objects?

No. Scalars must serialize to a single value (string, number, boolean). For complex structures, use object types.

How do custom scalars affect performance?

The overhead is minimal. Each scalar value goes through serialize/parseValue which are simple function calls.

Can I use custom scalars as mutation arguments?

Yes. Custom scalars work for both query fields and mutation arguments.

How do I document custom scalars?

Add a description to the scalar definition. Include format examples in the description.

Are custom scalars supported by all GraphQL clients?

Yes. Custom scalars are transparent to clients. The client sends/receives the serialized value.

Mini Project

Build a library of custom scalars for a healthcare application: DateOfBirth (validates age > 0), BloodPressure (formatted string), SSN (masked), and ZipCode (validated format). Implement all three methods and write tests for validation.

What's Next

Explore enum types for fixed value sets, then learn about interface types for shared field definitions.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro