Create custom types with GraphQLScalarType

GraphQL: custom types

GraphQL makes the object available GraphQLScalarType which allows you to create more complex and personalized types than those proposed by default. At first we will set up a server GraphQL with Express, then detail the use of GraphQLScalarType.
GraphQL proposes to control the data via different objects each representing a type of data.
graphqlhub-logo
Here is a non-exhaustive list of these:

GraphQL also provides the object GraphQLScalarType which will allow us to create our own types! It is this object that will interest us here.
But before creating our custom type with GraphQLScalarType, we will first set up a server GraphQL with Express to test our custom type.
A bit of code:

// server.js
const dates = require('./datas.json')
const Express     = require('Express')
const graphQLHTTP = require('express-graphql')
const {
  GraphQLObjectType
} = require('graphql')
const app  = Express()
const PORT = 8083
app.listen(PORT, _ => consul.log(`server running on port %s`, PORT))

We imported express, express-graphql et graphql then set up our server listening on the port 8083.

const {
  GraphQLObjectType,
  GraphQLInt,
  GraphQLString
} = require('graphql')
const UserType = new GraphQLObjectType({ name: 'User', description: 'Define to user',
  fields: () => ({
    'id': {
      'type': GraphQLInt,
      'Description': 'User ID'
    },
    'first_name': {
      'type': GraphQLString,
      'Description': 'User first name'
    },
    'last_name': {
      'type': GraphQLString,
      'Description': 'User last name'
    },
    'Email': {
      'type': GraphQLString,
      'Description': 'user email'
    },
    'gender': {
      'type': GraphQLString,
      'Description': 'User gender'
    } }) })

We have created a type User with the class GraphQLObjectType based on data from our datas.json which we will use later. Each field here is typed via type classes GraphQL. We used GraphQLIntet GraphQLString without forgetting of course to import them.

const {
  /* ... */
  GraphQLSchema } = require('graphql')
/* ... */
const Query = new GraphQLObjectType({ name: 'Queries', description: 'Define queries',
  fields: () => ({ getUserByMail: { type: UserType, args: { E-mail: { type: GraphQLString } },
      solves: (_, { E-mail }) => dates.find(user => Email === user.Email) } }) })
const schedule = new GraphQLSchema({ query })
const app  = Express()
const PORT = 8083
app.use('/', graphQLHTTP({ diagram: diagram, graphiql: true, pretty: true,
  formatError: error => ({ message: error.message, rentals: error.rentals, stack: error.stack
  }) }))

The variable query and more specifically the method fields who composed it contains the list of queries that we make available.
She is composed :

  • of a name getUserByMail,
  • of the argument to be used email,
  • of the type associated with this argument, here it is a type String so GraphQLString,
  • of a resolver, the function resolve which will retrieve our user's data.

The resolver getUserByMail is now in place and allows to request the data by providing an email as argument. We also used the module graphql-js and the function graphQLHTTP as middleware. Here the root url "/" is now supported by our middleware and schema GraphQLSchema.
To start the server just run the following command:

 node server.js #or `yarn watch` if you cloned the repository (and have yarn globally)

The graphQL GUI graphiql is accessible via the url http://localhost:8083
We can test that our resolver works correctly with this query (to be used in the interface graphiql):

{ user: getUserByMail(Email: "ehuntern@huffingtonpost.com") { id first_name last_name } }

user at the start of the request is an alias of our resolver getUserByMail`
Now that our server is working properly, let's use the object GraphQLScalarType to create a custom type for emails. One EmailType.

const {
  /* ... */
  GraphQLScalarType, GraphQLError, Kind, GraphQLSchema } = require('graphql')
/ ** ...**/
const EmailType = new GraphQLScalarType({ name: 'Email',
  serialize: value => value,
  parseValue: value => value,
  parseLiteral(branch) {
    return branch.value
  } })

So we created ane instance of the object EmailType through the constructor GraphQLScalarType giving it as a parameter:

  • Attribute name to name our new data type.
  • Attributes serialize et parseValue are two “serialization” functions that ensure the validity of the input value.
  • parseLiteral is a function that will return an object containing the node information AST* of our input value.

Check AST ou Abstract Syntax Tree or in French Abstract Syntax Tree
Here is an example of this object:

{ kind: 'StringValue', value: 'ehuntern@huffingtonpost.com', loc: { start: 31, end: 60
  }}

See his AST
It is therefore in this function parseLiteral that we are going to set up the controls thanks to this object branch.
We have access to the node type AST with the attribute kind and its value with the attribute value.

parseLiteral(branch) {
  if (branch.child ! == Kind.STRING) {
    throw new GraphQLError('Query error: Email must be a string ' + branch.child, [ast]); }
  if (!branch.value.match(/@/)) {
    throw new GraphQLError('Query error: Not a valid Email', [ast]); }
  return branch.value
}

Here we start by checking that our value is of type String, via the enum GraphQL Kind, which provides us with the list of the different types of node AST.
Then we do a summary check on the value to check and then, if the two conditions are met, we return our value!
EmailType is finished and now we can use it in the description of the query:

const Query = new GraphQLObjectType({ name: 'Queries', description: 'Define queries',
  fields: () => ({ getUserByMail: { type: UserType, args: { E-mail: { type: EmailType // Here 
        } },
      solves: (_, { E-mail }) => dates.find(user => Email === user.Email) } }) })

The email argument must now match its own type EmailType !
To conclude, we can say that GraphQLScalarType is very practical, easy to use and allows us to create our own types quickly.
This object allows us control inputs upstream and thus avoid the usual verification methods like isEmail(), isPhone(), isBlabla() etc ...
Thomas Rimbault, Developer at JS Republic
The github containing the source

  • yarn install
  • yarn watch
[actionbox color=”default” title=”” description=”JS-REPUBLIC is a service company specializing in JavaScript development. We are an approved training center. Find all our technical training on our partner site dedicated to Training” btn_label=”Our training” btn_link=”http://training.ux-republic.com” btn_color=”primary” btn_size=”big” btn_icon=”star” btn_external =”1″]