GraphQL

Leveraging GraphQL Scalars to Enhance Your Schema

William Mimura
4 min read
Leveraging GraphQL Scalars to Enhance Your Schema
This article was written over 18 months ago and may contain information that is out of date. Some content may still be relevant, but please refer to official documentation for the latest information.

Introduction

GraphQL has revolutionized the way developers approach application data and API layers, gaining well-deserved momentum in the tech world. Yet, for all its prowess, there's room for enhancement, especially when it comes to its scalar types. By default, GraphQL offers a limited set of these primitives — Int, Float, String, Boolean, and ID — that underpin every schema. While these types serve most use cases, there are scenarios where they fall short, leading developers to yearn for more specificity in their schemas.

Enter graphql-scalars, a library designed to bridge this gap. By supplementing GraphQL with a richer set of scalar types, this tool allows for greater precision and flexibility in data representation. In this post, we'll unpack the potential of enhanced Scalars, delve into the extended capabilities provided by graphql-scalars, and demonstrate its transformative power using an existing starter project. Prepare to redefine the boundaries of what your GraphQL schema can achieve.

Benefits of Using Scalars

GraphQL hinges on the concept of "types." Scalars, being the foundational units of GraphQL's type system, play a pivotal role. While the default Scalars — Int, Float, String, Boolean, and ID — serve many use cases, there's an evident need for more specialized types in intricate web development scenarios.

  1. Precision: Using default Scalars can sometimes lack specificity. Consider representing a date or time in your application with a String; this might lead to ambiguities in format interpretation and potential inconsistencies.
  2. Validation: Specialized scalar types introduce inherent validation. Instead of using a String for an email or a URL, for example, distinct types ensure the data meets expected formats at the query level itself.
  3. Expressiveness: Advanced Scalars provide clearer intentions. They eliminate ambiguity inherent in generic types, making the schema more transparent and self-explanatory.

Acknowledging the limitations of the default Scalars, tools like graphql-scalars have emerged. By broadening the range of available data types, graphql-scalars allows developers to describe their data with greater precision and nuance.

Demonstrating Scalars in Action with Our Starter Project

To truly grasp the transformative power of enhanced Scalars, seeing them in action is pivotal. For this, we'll leverage a popular starter kit: the Serverless framework with Apollo and Contentful. This kit elegantly blends the efficiency of serverless functions with the power of Apollo's GraphQL and Contentful's content management capabilities.

Setting Up the Starter:

  1. Initialize the Project:
npm create @this-dot/starter -- --kit serverless-framework-apollo-contentful
  1. When prompted, name your project enhance-with-graphql-scalars.
Welcome to starter.dev! (create-starter)
✔ What is the name of your project? … enhance-with-graphql-scalars
> Downloading starter kit...
✔ Done!

Next steps:
 cd enhance-with-graphql-scalars
 npm install (or pnpm install, yarn, etc)
  1. For a detailed setup, including integrating with Contentful and deploying your serverless functions, please follow the comprehensive guide provided in the starter kit here.
  2. And we add the graphql-scalars package
npm install graphql-scalars

Enhancing with graphql-scalars:

Dive into the technology.typedefs.ts file, which is the beating heart of our GraphQL type definitions for the project. Initially, these are the definitions we encounter:

export const technologyTypeDefs = gql`
	type Technology {
		id: ID!
		displayName: String!
		description: String
		url: URL
	}

	type Query {
		"Technology: GET"
		technology(id: ID!): Technology
		technologies(offset: Int, limit: Int): [Technology!]
	}

	type Mutation {
		"Technology: create, read and delete operations"
		createTechnology(displayName: String!, description: String, url: String): Technology
		updateTechnology(id: ID!, fields: TechnologyUpdateFields): Technology
		deleteTechnology(id: ID!): ID
	}

	input TechnologyUpdateFields {
		"Mutable fields of a technology entity"
		displayName: String
		description: String
		url: String
	}
`;

Our enhancement strategy is straightforward:

  • Convert the url field from a String to the URL scalar type, bolstering field validation to adhere strictly to the URL format.

Post-integration of graphql-scalars, and with our adjustments, the revised type definition emerges as:

export const technologyTypeDefs = gql`
	type Technology {
		id: ID!
		displayName: String!
		description: String
		url: URL
	}

	type Query {
		"Technology: GET"
		technology(id: ID!): Technology
		technologies(offset: Int, limit: Int): [Technology!]
	}

	type Mutation {
		"Technology: create, read and delete operations"
		createTechnology(displayName: String!, description: String, url: URL): Technology
		updateTechnology(id: ID!, fields: TechnologyUpdateFields): Technology
		deleteTechnology(id: ID!): ID
	}

	input TechnologyUpdateFields {
		"Mutable fields of a technology entity"
		displayName: String
		description: String
		url: URL
	}
`;

To cap it off, we integrate the URL type definition along with its resolvers (sourced from graphql-scalars) in the schema/index.ts file:

import { mergeResolvers, mergeTypeDefs } from '@graphql-tools/merge';
import { technologyResolvers, technologyTypeDefs } from './technology';
import { URLResolver, URLTypeDefinition } from 'graphql-scalars';

const graphqlScalars = [URLTypeDefinition];

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

export const resolvers = mergeResolvers([{ URL: URLResolver }, technologyResolvers]);

This facelift doesn't just refine our GraphQL schema but infuses it with innate validation, acting as a beacon for consistent and accurate data.

Testing in the GraphQL Sandbox

Time to witness our changes in action within the GraphQL sandbox. Ensure your local server is humming along nicely.

Kick off with verifying the list query:

query {
  technologies {
    id
    displayName
    url
  },
}

Output:

{
  "data": {
    "technologies": [
      {
        "id": "4UXuIqJt75kcaB6idLMz3f",
        "displayName": "GraphQL",
        "url": "https://graphql.framework.dev/"
      },
      {
        "id": "5nOshyir74EmqY4Jtuqk2L",
        "displayName": "Node.js",
        "url": "https://nodejs.framework.dev/"
      },
      {
        "id": "5obCOaxbJql6YBeXmnlb5n",
        "displayName": "Express",
        "url": "https://www.npmjs.com/package/express"
      }
    ]
  }
}

Success! Each url in our dataset adheres to the pristine URL format. Any deviation would've slapped us with a format error.

Now, let's court danger. Attempt to update the url field with a wonky format:

mutation {
  updateTechnology(id: "4UXuIqJt75kcaB6idLMz3f", fields: { url: "aFakeURLThatShouldThrowError" }) {
    id
    displayName
    url
  }
}

As anticipated, the API throws up a validation roadblock:

{
  "data": {},
  "errors": [
    {
      "message": "Expected value of type \"URL\", found \"aFakeURLThatShouldThrowError\"; Invalid URL",
      "locations": [
        {
          "line": 18,
          "column": 65
        }
      ],
      "extensions": {
        "code": "GRAPHQL_VALIDATION_FAILED",
        "stacktrace": [
          "TypeError [ERR_INVALID_URL]: Invalid URL",
          "    at new NodeError (node:internal/errors:399:5)",
          "    at new URL (node:internal/url:560:13)",
          ...
        ]
      }
    }
  ]
}

For the final act, re-run the initial query to reassure ourselves that the original dataset remains untarnished.

Conclusion

Enhancing your GraphQL schemas with custom scalars not only amplifies the robustness of your data structures but also streamlines validation and transformation processes. By setting foundational standards at the schema level, we ensure error-free, consistent, and meaningful data exchanges right from the start.

The graphql-scalars library offers an array of scalars that address common challenges developers face. Beyond the URL scalar we explored, consider diving into other commonly used scalars such as:

  • DateTime: Represents date and time in the ISO 8601 format.
  • Email: Validates strings as email addresses.
  • PositiveInt: Ensures integer values are positive.
  • NonNegativeFloat: Guarantees float values are non-negative.

As a potential next step, consider crafting your own custom scalars tailored to your project's specific requirements. Building a custom scalar not only offers unparalleled flexibility but also provides deeper insights into GraphQL's inner workings and its extensibility.

Remember, while GraphQL is inherently powerful, the granular enhancements like scalars truly elevate the data-fetching experience for developers and users alike. Always evaluate your project's needs and lean into enhancements that bring the most value.

To richer and more intuitive GraphQL schemas!

About the author

William Mimura

William Mimura

Senior Software Engineer

Keep reading

View all posts →

How to Leverage Apollo Client Fetch Policies Like the Pros

Apollo Client provides a rich ecosystem and cache for interfacing with your GraphQL APIs. You write your query and leverage the `useQuery` hook to fetch your data....

Dustin Goodman7 mins
GraphQL

How to create and use custom GraphQL Scalars

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. ...

William Mimura5 mins
GraphQLJavaScript

Efficiently Extract Object References in Shopify Storefront GraphQL API

Dive into the intricacies of Shopify's Storefront API with our blog post, 'Efficiently Extract Object References in Shopify Storefront GraphQL API'. Discover a hands-on guide to extracting complex data types, like images, from metafields and metaobjects....

William Mimura4 mins
GraphQLShopify

Introducing the New Serverless, GraphQL, Apollo Server, and Contentful Starter kit

Introducing the new Serverless, GraphQL, Apollo Server, and Contentful Starter kit The team at This Dot Labs has released a brand new starter kit which includes the Serverless Framework, GraphQL, Apollo Server and Contentful configured and ready to go. This article will walk through how to set up the new kit, the key technologies used, and reasons why you would consider using this kit. Table of Contents How to get started setting up the kit Generate the project Setup Contentful access Setting up Docker Starting the local server How to Create the Technology Model in Contentful How to seed the database with demo data How to work with the migration scripts Technologies included in this starter kit Why use GraphQL? Why use Contentful? Why use Amazon Simple Queue Service (SQS)? Why use Apollo Server? Why use the Serverless Framework? Why use Redis? Why use the Jest testing framework? Project structure How to deploy your application What can this starter kit be used for? Conclusion How to get started setting up the kit Generate the project In the command line, you will need to start the starter.dev CLI by running the npx @this dot/create starter command. You can then select the Serverless Framework, Apollo Server, and Contentful CMS kit and name your new project. Then you will need to cd into your new project directory and install the dependencies using the tool of your choice (npm, yarn, or pnpm). Next, you will need to Run cp .env.example .env to copy the contents of the .env.example file into the .env file. Setup Contentful access You will first need to create an account on Contentful, if you don't have one already. Once you are logged in, you will need to create a new space. From there, go to Settings API keys and click on the Content Management Tokens tab. Next, click on the Generate personal token button and give your token a name. Copy your new Personal Access Token, and add it to the CONTENTFUL CONTENT MANAGEMENT API TOKEN variable. Then, go to Settings General settings to get the CONTENTFUL SPACE ID. The last step is to add those CONTENTFUL CONTENT MANAGEMENT API TOKEN and CONTENTFUL SPACE ID values to your .env file. Setting up Docker You will first need to install Docker Desktop if you don't have it installed already. Once installed, you can start up the Docker container with the npm run infrastructure:up command. Starting the local server While the Docker container is running, open up a new tab in the terminal and run npm run dev to start the development server. Open your browser to http://localhost:3000/dev/graphql to open up Apollo server. How to Create the Technology Model in Contentful To get started with the example model, you will first need to create the model in Contentful. 1. Log into your Contentful account 2. Click on the Content Model tab 3. Click on the Design your Content Modal button if this is your first modal 4. Create a new model called Technology 5. Add three new text fields called displayName, description and url 6. Save your new model How to seed the database with demo data This starter kit comes with a seeding script that pre populates data for the Technology Content type. In the command line, run npm run db:seed which will add three new data entries into Contentful. If you want to see the results from seeding the database, you can execute a small GraphQL query using Apollo server. First, make sure Docker, and the local server(npm run dev) are running, and then navigate to http://localhost:3000/dev/graphql. Add the following query: When you run the query, you should see the following output. How to work with the migration scripts Migrations are a way to make changes to your content models and entries. This starter kit comes with a couple of migration scripts that you can study and run to make changes to the demo Technology model. These migration scripts are located in the scripts/migration directory. To get started, you will need to first install the contentful cli. You can then login to Contentful using the contentful cli. You will then need to choose the Contentful space where the Technology model is located. If you want to modify the existing demo content type, you can run the second migration script from the starter kit. If you want to build out more content models using the CLI, you can study the example code in the /scripts/migrations/01 create technology contentType.js file. From there, you can create a new migration file, and run the above contentful space migration command. If you want to learn more about migration in Contentful, then please check out the documentation. Technologies included in this starter kit Why use GraphQL? GraphQL is a query language for your API and it makes it easy to query all of the data you need in a single request. This starter kit uses GraphQL to query the data from our Contentful space. Why use Contentful? Contentful is a headless CMS that makes it easy to create and manage structured data. We have integrated Contentful into this starter kit to make it easy for you to create new entries in the database. Why use Amazon Simple Queue Service (SQS)? Amazon Simple Queue Service (SQS) is a queuing service that allows you to decouple your components and process and store messages in a scalable way. In this starter kit, an SQS message is sent by the APIGatewayProxyHandler using the sendMessage function, which is then stored in a queue called DemoJobQueue. The SQS handler sqs handler polls this queue, and processes any message received. Why use Apollo Server? Apollo Server is a production ready GraphQL server that works with any GraphQL client, and data source. When you run npm run dev and open the browser to http://localhost:3000/dev/graphql, you will be able to start querying your Contentful data in no time. Why use the Serverless Framework? The Serverless Framework is used to help auto scale your application by using AWS Lambda functions. In the starter kit, you will find a serverless.yml file, which acts as a configuration for the CLI and allows you to deploy your code to your chosen provider. This starter kit also includes the following plugins: serverless offline allows us to deploy our application locally to speed up development cycles. serverless plugin typescript allows the use of TypeScript with zero config. serverless dotenv plugin preloads function environment variables into the Serverless Framework. Why use Redis? Redis is an open source in memory data store that stores data in the server memory. This starter kit uses Redis to cache the data to reduce the API response times and rate limiting. When you make a new request, those new requests will be retrieved from the Redis cache. Why use the Jest testing framework? Jest is a popular testing framework that works well for creating unit tests. You can see some example test files under the src/schema/technology directory. You can use the npm run test command to run all of the tests. Project structure Inside the src directory, you will find the following structure: This given structure makes it easy to find all of the code and tests related to that specific component. This structure also follows the single responsibility principle which means that each file has a single purpose. How to deploy your application The Serverless Framework needs access to your cloud provider account so that it can create and manage resources on your behalf. You can follow the guide to get started. Steps to get started: 1. Sign up for an AWS account 2. Create an IAM User and Access Key 3. Export your AWS ACCESS KEY ID & AWS SECRET ACCESS KEY credentials. 4. Deploy your application on AWS Lambda: 5. To deploy a single function, run: To stop your Serverless application, run: For more information on Serverless deployment, check out this article. What can this starter kit be used for? This starter kit is very versatile, and can be used with a front end application for a variety of situations. Here are some examples: personal developer blog small e commerce application Conclusion In this article, we looked at how we can get started using the Serverless, GraphQL, Apollo Server, and Contentful Starter kit. We also looked at the different technologies used in the kit, and why they were chosen. Lastly, we looked at how to deploy our application using AWS. I hope you enjoy working with our new starter kit!...

Jessica Wilkins7 mins
GraphQL