Skip to content

How to create and use custom GraphQL Scalars

How to create and use custom GraphQL Scalars

How to create and use custom GraphQL Scalars

In the realm of GraphQL, scalars form the bedrock of the type system, representing the most fundamental data types like strings, numbers, and booleans. As explored in our previous post, "Leveraging GraphQL Scalars to Enhance Your Schema," scalars play a pivotal role in defining how data is structured and validated. But what happens when the default scalars aren't quite enough? What happens when your application demands a data type as unique as its requirements?

Enter the world of custom GraphQL scalars. These data types go beyond the conventional, offering the power and flexibility to tailor your schema to precisely match your application's unique needs. Whether handling complex data structures, enforcing specific data formats, or simply bringing clarity to your API, custom scalars open up a new realm of possibilities.

In this post, we'll explore how to understand, create, and effectively utilize custom scalars in GraphQL. From conceptualization to implementation, we'll cover the essentials of extending your GraphQL toolkit, empowering you to transform abstract ideas into concrete, practical solutions. So, let's embark together on the journey of understanding and utilizing custom GraphQL scalars, enhancing and expanding the capabilities of your GraphQL schema.

Understanding Custom Scalars

Custom scalars in GraphQL extend beyond basic types like String or Int, allowing data to be defined, validated, and processed more precisely. They're instrumental when default types don't quite capture the complexity or specificity of the data, such as with specialized date formats or unique identifiers.

The use of custom scalars brings several benefits:

  • Enhanced Clarity: They offer a clearer representation of what data looks like and how it behaves.
  • Built-in Validation: Data integrity is bolstered at the schema level.
  • Flexibility: They can be tailored to specific data handling needs, making your schema more adaptable and robust.

With this understanding, we'll explore creating and integrating custom scalars into a GraphQL schema, turning theory into practice.

Creating a Custom Scalar

Defining a Custom Scalar in TypeScript:

Creating a custom scalar in GraphQL with TypeScript involves defining its behavior through parsing, serialization, and validation functions.

  • Parsing: Transforms input data from the client into a server-understandable format.
  • Serializing: Converts server data back to a client-friendly format.
  • Validation: Ensures data adheres to the defined format or criteria.

Example: A 'Color' Scalar in TypeScript

The Color scalar will ensure that every color value adheres to a valid hexadecimal format, like #FFFFFF for white or #000000 for black:

// src/scalars/color.scalar.ts
import { GraphQLScalarType, Kind } from 'graphql';

const validateColor = (value: string): string => {
	const regex = /^#[0-9A-Fa-f]{6}$/;
	if (!regex.test(value)) {
		throw new Error(`Value is not a valid color: ${value}`);
	}
	return value;
};

export const colorScalar = new GraphQLScalarType({
	name: 'Color',
	description: 'Color custom scalar type for representing colors in hexadecimal',
	parseValue(value) {
		if (typeof value === 'string') {
			return validateColor(value);
		}
		throw new Error(`Value is not a valid string: ${value}`);
	},
	serialize(value) {
		if (typeof value === 'string') {
			return validateColor(value);
		}
		throw new Error(`Value is not a valid string: ${value}`);
	},
	parseLiteral(ast) {
		if (ast.kind === Kind.STRING) {
			return validateColor(ast.value);
		}
		return null;
	},
});

In this TypeScript implementation:

  • validateColors: a function that checks if the provided string matches the hexadecimal color format.
  • parseValue: a method function that converts the scalar’s value from the client into the server’s representation format - this method is called when a client provides the scalar as a variable. See parseValue docs for more information
  • serialize: a method function that converts the scalar’s server representation format to the client format, see serialize docs for more information
  • parseLiteral: similar to parseValue, this method function converts the scalar’s value from the client to the server’s representation format. Still, this method is called when the scalar is provided as a hard-coded argument (inline). See parseLiteral docs for more information

In the upcoming section, we'll explore how to incorporate and validate these custom scalars within your schema, ensuring they function seamlessly in real-world scenarios.

Integrating Custom Scalars into a Schema

Incorporating the 'Color' Scalar

After defining your custom Color scalar, the next crucial step is effectively integrating it into your GraphQL schema. This integration ensures that your GraphQL server recognizes and correctly utilizes the scalar.

Step-by-Step Integration

  1. Add the scalar to Type Definitions: Include the Color scalar in your GraphQL type definitions. This inclusion informs GraphQL about this new scalar type.
  2. Resolver Mapping: Map your custom scalar type to its resolver. This connection is key for GraphQL to understand how to process this type during queries and mutations.
// src/schema/index.ts
import { mergeResolvers, mergeTypeDefs } from '@graphql-tools/merge';
import { URLResolver, URLTypeDefinition } from 'graphql-scalars';
import gql from 'graphql-tag';
import { colorScalar } from '../scalars/color.scalar';
import { technologyResolvers, technologyTypeDefs } from './technology';

// Define the new custom scalar type
const colorTypeDef = gql`
	scalar Color
`;

const graphqlScalarTypeDefs = [URLTypeDefinition, colorTypeDef];
const graphqlScalarResolvers = {
	URL: URLResolver,
	Color: colorScalar, // add the new scalar to the resolver
};

export const typeDefs = mergeTypeDefs([...graphqlScalarTypeDefs, technologyTypeDefs]);

export const resolvers = mergeResolvers([{ ...graphqlScalarResolvers }, technologyResolvers]);

  1. Use the scalar: Update your type to use the new custom scalar
// src/chema/technology/technology.typedefs.ts
import gql from 'graphql-tag';

export const technologyTypeDefs = gql`
	type Technology {
		id: ID!
		displayName: String!
		description: String
		url: URL
		primaryColor: Color
	}
... // rest of the schema
`;

Testing the Integration

With your custom Color scalar integrated, conducting thorough testing is vital. Ensure that your GraphQL server correctly handles the Color scalar, particularly in terms of accepting valid color formats and rejecting invalid ones. For demonstration purposes, I've adapted a creation mutation to include the primaryColor field. To keep this post focused and concise, I won't detail all the code changes here, but the following screenshots illustrate the successful implementation and error handling.

Calling the mutation (createTechnology) successfully:

Showing the response from calling the mutation createTechnology successfully

Calling the mutation with forced fail (bad color hex):

Showing the response from calling the mutation createTechnology with fail

Conclusion

The journey into the realm of custom GraphQL scalars reveals a world where data types are no longer confined to the basics. By creating and integrating scalars like the Color type, we unlock precision and specificity in our GraphQL schemas, which significantly enhance our applications' data handling capabilities.

Custom scalars are more than just a technical addition; they testify to GraphQL's flexibility and power. They allow developers to express data meaningfully, ensuring that APIs are functional, intuitive, and robust.

As we've seen, defining, integrating, and testing these scalars requires a blend of creativity and technical acumen. It encourages a deeper understanding of how data flows through your application and offers a chance to tailor that experience to your project's unique needs.

So, as you embark on your GraphQL journey, consider the potential of custom scalars. Whether you're ensuring data integrity, enhancing API clarity, or simply making your schema a perfect fit for your application, the possibilities are as vast as they are exciting. Embrace the power of customization, and let your GraphQL schemas shine!