DEV Community

John Au-Yeung
John Au-Yeung

Posted on • Originally published at thewebdev.info

Create and Use Data Types with Express GraphQL

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'));
Enter fullscreen mode Exit fullscreen mode

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  
  }  
`);
Enter fullscreen mode Exit fullscreen mode

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;  
  }  
}
Enter fullscreen mode Exit fullscreen mode

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);  
  },  
};
Enter fullscreen mode Exit fullscreen mode

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  
  }  
}
Enter fullscreen mode Exit fullscreen mode

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

{  
  "data": {  
    "getDie": {  
      "rollOnce": 3,  
      "roll": [  
        6,  
        4,  
        5  
      ],  
      "numSides": 6  
    }  
  }  
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)