Create and Use Data Types with Express GraphQL
Tags: webdev, programming, javascript, graphql
Published: 2021-01-25 01:40:35

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

We can create a simple GraphQL server with Express. To do this, we need the `express-graphql` and `graphql` packages.

In this article, we’ll look at how to create and use our own GraphQL data types.

Object Types
============

In many cases, we don’t want to accept and return a number or a string from the API. We can create our own data types to accept and return whatever we want from the API.

With the `express-graphql` package, we can define our data types in a string and then pass it into the `buildSchema` function.

For example, we can write the following code to define our types, build a schema, and add our resolvers to our code:

```
const express = require('express');  
const graphqlHTTP = require('express-graphql');  
const { buildSchema } = require('graphql');  
const schema = buildSchema(`  
  type RandomDie {  
    numSides: Int!  
    rollOnce: Int!  
    roll(numRolls: Int!): [Int]  
  } type Query {  
    getDie(numSides: Int): RandomDie  
  }  
`);

class RandomDie {  
  constructor(numSides) {  
    this.numSides = numSides;  
  } 

  rollOnce() {  
    return 1 + Math.floor(Math.random() * this.numSides);  
  } 

  roll({ numRolls }) {  
    const output = [];  
    for (let i = 0; i < numRolls; i++) {  
      output.push(this.rollOnce());  
    }  
    return output;  
  }  
}

const root = {  
  getDie: ({ numSides }) => {      
    return new RandomDie(numSides || 6);  
  },  
};

const app = express();app.use('/graphql', graphqlHTTP({  
  schema: schema,  
  rootValue: root,  
  graphiql: true,  
}));  
app.listen(3000, () => console.log('server started'));
```

In the code above, we defined our schema by writing:

```
const schema = buildSchema(`  
  type RandomDie {  
    numSides: Int!  
    rollOnce: Int!  
    roll(numRolls: Int!): [Int]  
  }  
  type Query {  
    getDie(numSides: Int): RandomDie  
  }  
`);
```

We defined the `RandomDie` type with the `numSides` field and `rollOnce` and `roll` methods.

Then we defined our `getDie` query to let access the members that we defined in the `RandomDie` type.

Then we defined our `RandomDie` class, which we’ll use in our `getDie` resolver which we’ll define later:

```
class RandomDie {  
  constructor(numSides) {  
    this.numSides = numSides;  
  }  
  rollOnce() {  
    return 1 + Math.floor(Math.random() * this.numSides);  
  }  
  roll({ numRolls }) {  
    const output = [];  
    for (let i = 0; i < numRolls; i++) {  
      output.push(this.rollOnce());  
    }  
    return output;  
  }  
}
```

In the class, we created the `rollOnce` and `roll` methods where we’ll return the results.

Then finally, we define our `getDie` resolver as follows:

```
const root = {  
  getDie: ({ numSides }) => {      
    return new RandomDie(numSides || 6);  
  },  
};
```

We get `numSides` from the parameter and then passed it into the `RandomDie` constructor when we instantiate it.

Then in the `/graphql` page, we can make the following query in the GraphiQL UI:

```
{  
  getDie(numSides: 6) {  
    rollOnce  
    roll(numRolls: 3)  
    numSides  
  }  
}
```

and we should get something like the following as a response:

```
{  
  "data": {  
    "getDie": {  
      "rollOnce": 3,  
      "roll": [  
        6,  
        4,  
        5  
      ],  
      "numSides": 6  
    }  
  }  
}
```

Note that we access fields and call methods with no arguments the same way, as we did with `rollOnce` and `numSides` .

This way of defining objects provides us with some advantages over traditional REST APIs. Instead of using an API request to get basic information about an object and multiple requests to find out more about the object, we can just make one query to get the things we need.

This saves bandwidth, increases performance, and simplifies client-side logic.


Conclusion
==========

We can create new types by putting it in the string with other parts of the schema. Then we can use the `buildSchema` function to build the schema.

Once we did that, we create a class to map the type fields into class members. Then we can instantiate that class in our resolver.

Then finally, we can make our request by sending the class name as the query name and then the member names with arguments if required inside the braces.