Categories
GraphQL JavaScript

Introduction to GraphQL — Variables and Complex Operations

GraphQL is a query language for our API and a server-side runtime for running queries by using a type system for our data.

In this article, we’ll look at more complex GraphQL operations, including passing in variables, directives, mutations, and more.

Variables

In most apps, the arguments for queries need to be dynamic. It wouldn’t be a good idea to pass in dynamic arguments directly to the query string.

Fortunately, GraphQL lets us pass in variables into our operations request.

For example, we can write:

query PersonName($id: Int) {  
  person(id: $id) {  
    name  
  }  
}

to pass in an $id variable into our person query.

Then we can pass in variables via an object to make the request:

{  
  "id": 1000  
}

Then we get something like:

{  
  "data": {  
    "person": {  
      "name": "Jane"  
    }  
  }  
}

as the response.

Variable definitions

Variable definitions start with the variable name prefixed with a $, then the type of the variable.

In the example above, we have $id as the variable name and Int as the type.

All declared variables must be either a scalar, enums or other input object types.

If we want to pass a complex object into a field, we need to know the input type to match on the server.

Default variables

We can set default values for variables as follows:

query PersonName($id: Int = 1) {  
  person(id: $id) {  
    name  
  }  
}

In the code above, we added = 1 after $id: Int to set the default value for $id to 1.

Directives

We can use directives to dynamically change the structure and shape of our queries using variables.

For example, we can include directives as follows to make our query dynamic:

query Person($id: Int, $withFriends: Boolean!) {  
  person(id: $id) {  
    name  
    friends @include(if: $withFriends) {  
      name  
    }  
  }  
}

In the code above, we have the @include(if: $withFriends) directive to conditionally includes friends if $withFriends is true.

Therefore, if we make the following query with the following variables:

{  
  "id": 1000,  
  "withFriends": false  
}

Then we may get something like:

{  
  "data": {  
    "person": {  
      "name": "Jane"  
    }  
  }  
}

as a response.

A directive can be attached to a field or fragment inclusion and can affect the execution of the query in any way the server desires.

The core GraphQL specification includes 2 directives that must be supported by any spec-compliant GraphQL server implementation:

  • @include(if: Boolean) — only include this field if the argument if true.
  • @skip(if: Boolean) — skip this field if the argument is true.

It helps us get out of situations where we would have to do string manipulating to add and remove fields in our query.

Mutations

We can use mutations to send requests to change data on the server.

They’re similar to queries, except that it starts with the keyword mutation instead.

For example, we can define a mutation as follows:

mutation CreatePerson($firstName: String, $lastName: String) {  
  createPerson(firstName: $firstName, lastName: $lastName) {  
    firstName  
    lastName  
  }  
}

Then if we make the following query:

{  
  "firstName": "Joe",  
  "lastName": "Smith"  
}

we may get the following response:

{  
  "data": {  
    "createPerson": {  
      "firstName": "Joe",  
      "lastName": "Smith"  
    }  
  }  
}

In the response, we return the firstName and lastName fields as we specified in the request.

A mutation can contain multiple fields like a query. Mutations fields run in series.

This means that if we sent 2 createPerson mutations in one request. The first will finish before the second begins.

Inline Fragments

We can define interface and union types with GraphQL requests.

To do this, we use inline fragments to access data on the underlying concrete type.

For example, we can write the following query:

query Thing($id: Int!) {  
  person(id: $id) {  
    name  
    ... on Person{  
      gender  
    }  
    ... on Robot{  
      memory  
    }  
  }  
}

In the query above, the ...on operator indicates that we include an inline fragment within our query, where Person and Robot are our fragments.

Named fragments can also be inline fragments since they have a type attached.

Meta fields

We can request a __typename field to get the type of data returned in the response.

For example, if we have the following query:

{  
  search(text: "an") {  
    __typename  
    ... on Human {  
      name  
    }  
    ... on Robot {  
      name  
    }  
  }  
}

Then we may get the following response from the server:

{  
  "data": {  
    "search": [  
      {  
        "__typename": "Human",  
        "name": "Hans"  
      },  
      {  
        "__typename": "Robot",  
        "name": "Jane"  
      }  
    ]  
  }  
}

Conclusion

We can define mutations to make requests to change data.

To make requests dynamic, we can use variables to include variable data and directives for conditional inclusion of data in our response.

We can also use inline fragments for union types and include the type of data returned in the response by including the __typename property.

Categories
GraphQL JavaScript

Introduction to GraphQL Schemas and Types

GraphQL is a query language for our API and a server-side runtime for running queries by using a type system for our data.

In this article, we’ll look at the GraphQL type system and how it describes what data can be queried.

Schemas and Types

GraphQL requests are about selecting fields on objects.

In a query, we have a root object, then we select a field from that and fields below that level if it exists all the way to the bottom level.

For example, if we have the query:

{  
  person {  
    name  
  }  
}

Then we get something like:

{  
  "data": {  
    "person": {  
      "name": "Jane"  
    }  
  }  
}

as the response.

A GraphQL query closely matches the result so we can predict what the query will return without knowing that much about the serve.

But it’s still useful to have an exact description of data we can ask for.

This is where explicit types are useful.

Type language

GraphQL services can be written in any language. Therefore, types have to be defined in a language-agnostic way.

Object types and fields

We can define a GraphQL data type as follows:

type Person {  
  name: String!  
  addresses: [Address!]!  
}

In the code above, we created a new type with the type keyword. In the type, we have the name field, which is String , and addresses , which is an array of Address es, which is another type.

String is a built-in scalar type. The exclamation marks mean the field is not nullable. Therefore, String! is a non-nullable string.

[Address!]! is an array of Address objects. It’s also non-nullable, so we can always expect an array with zero or more items when we query for addresses . Since Address! is also non-nullable, we can always expect every item of the array to be an Address object.

Arguments

Every field of a GraphQL object type can have zero or more arguments.

For example, we can write the following:

type Person {  
  name: String!  
  height(unit: HeightUnit = METER): Float  
}

In the code above, we have the height field in the Person that takes a unit as the argument. unit is of type HeightUnit which is set to METER by default.

height returns a Float.

The Query and Mutation types

There’re 2 types that are special within a schema. They’re Query and Mutation .

Every GraphQL service has a query type and may or may not have an mutation type.

These types are the same as any other object type, but they’re special because they define the entry point of every GraphQL query.

If we have a query like the following:

query {  
  person {  
    name  
  }  
  robot(id: "2000") {  
    name  
  }  
}

Then the GraphQL service needs to have the query type with the person and robot fields:

type Query {  
  person(name: String): Person  
  robot(id: ID!): Robot  
}

We define mutation types the same way. We define fields on the Mutation type and those are available as the root mutation fields we can call in our query.

Scalar types

Scalar fields are what all object types contain in the end. They’re the most basic types of fields.

GraphQL comes with the following scalar types out of the box:

  • Int — a signed 32-bit integer
  • Float — a signed double-precision floating-point value
  • String — a UTF-8 character sequence
  • Booleantrue or false
  • ID — a unique identifier that used for fetching an object or as the key for a cache. It’s serialized the same way as a String but it’s not intended to be human-readable

Most GraphQL service implementations also let us specify custom scalar types.

We can define one as follows:

scalar Date

Then it’s up to use to decide how to serialize, deserialize and validate them.

Enumeration types

Enum types are special types of scalar that are restricted to a particular set of allowed values.

It lets us validate that any argument of the type is one of the allowed values.

Also, it communicates through the type system that a field will always be one of a finite set of values.

We can define an enum as follows:

enum Fruit {  
  APPLE  
  ORANGE  
  GRAPE  
}

Then if we have Fruit , we expect it to be either APPLE, ORANGE or GRAPE .

Conclusion

We can define types so that we can validate the data that’s submitted to the GraphQL server.

To do this, we create a type with the type keyword if it’s an object type.

We can also create scalar types which are the most basic types that can be included in other types or referenced directly. We can define them with the scalar keyword.

To define a type with fixed values, we can define an enum type with a few possible constant values.

The exclamation mark indicates that a type is not nullable.

Categories
GraphQL JavaScript

Creating a Basic GraphQL Server with Express

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 a simple GraphQL server with express and make a simple schema for it.

Create an Express GraphQL Server

First, we have to install the packages by running:

npm install express express-graphql graphql --save

Then we can create a server.js file in our project directory and add the following code:

const express = require('express');  
const graphqlHTTP = require('express-graphql');  
const { buildSchema } = require('graphql');
const schema = buildSchema(`  
  type Query {  
    hello: String  
  }  
`);

const root = {  
  hello: () => {  
    return 'Hello world!';  
  },  
};

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 created our express instance, which then we added the schema to it, which we defined by writing:

const schema = buildSchema(`  
  type Query {  
    hello: String  
  }  
`);

With this schema, we’re allowed to submit the hello query to our GraphQL server.

Then we have our root object, which is our resolver for resolving the hello query:

const root = {  
  hello: () => {  
    return 'Hello world!';  
  },  
};

We just return the 'Hello world!' string to the user who queries our server.

The code graphiql: true means that enabled the GraphiQL tool so that we can go to localhost:3000/graphql to see the GraphiQL tool. This is where we can type in our query.

Note that we don’t need a special client to make requests to our client. We can make a POST request to the /graphql endpoint without our GraphQL request payload to send the request to our server.

For example, if we use Postman by typing in our /graphql endpoint and then pass in {“query”: “{ hello }”} as the request payload string. In the header, we set Content/Type to application/json , then we get:

{  
    "data": {  
        "hello": "Hello world!"  
    }  
}

as the response.

We can also make the request in JavaScript with the Fetch API as follows:

(async () => {  
  const response = await fetch(  
    "/graphql",  
    {  
      method: "POST",  
      headers: {  
        "Content-Type": "application/json",  
        Accept: "application/json"  
      },  
      body: JSON.stringify({ query: "{ hello }" })  
    }  
  );  
  const data = await response.json();  
  console.log(data);  
})();

Then we get:

{  
  "data": {  
    "hello": "Hello world!"  
  }  
}

In many cases, we would need to accept arguments in our resolver and query.

To do this, we can write the following:

const express = require('express');  
const graphqlHTTP = require('express-graphql');  
const { buildSchema } = require('graphql');  
const schema = buildSchema(`  
  type Query {  
    rollDice(numSides: Int): Int  
  }  
`);

const root = {  
  rollDice: (params) => {  
    const { numSides } = params;  
    return Math.ceil(Math.random() * numSides);  
  },  
};

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 have the root object with the rollDice resolver function which as the params parameter to get the arguments that are passed into GraphQL request.

Then we return a random number from it and return it. The number will be returned within the response.

To make our rollDice resolver accept the request, we included:

type Query {  
  rollDice(numSides: Int): Int  
}

in our schema so that it’ll take in the numSides argument.

If we go to the /graphq page in our browser, and then type in:

{  
  rollDice(numSides: 10)  
}

in the left pane, we’ll get a number between 0 and 10.

To make a GraphQL POST request that takes in arguments, we can write the following:

(async () => {  
  const numSides = 6;  
  const query = `query RollDice($numSides: Int) {  
   rollDice(numSides: $numSides)  
  }`; 

  const response = await fetch(  
    "/graphql",  
    {  
      method: "POST",  
      headers: {  
        "Content-Type": "application/json",  
        Accept: "application/json"  
      },  
      body: JSON.stringify({  
        query,  
        variables: { numSides },  
      })  
    });  
  const data = await response.json();  
  console.log(data);  
})();

In the code above, we have the query , which is:

query RollDice($numSides: Int) {  
  rollDice(numSides: $numSides)  
}

and variables , which is:

variables: { numSides }

The JSON body that we sent with the request is:

{  
  "query": "query RollDice($numSides: Int) {  
     rollDice(numSides: $numSides)  
  }",  
  "variables": {  
    "numSides": 6  
  }  
}

This is the same what we ran in the GraphiQL UI, but needed the query RollDice to wrap our query so that we can pass in the numSides variable.

Conclusion

We can easily create a GraphQL Server with Express by using the express-graphql package.

After that, we have to create our resolver function and schema to let us accept GraphQL requests.

Resolvers can also accept variables. In there, we return the result, which will then be returned in the response to the user.

To test our API, we can use the GraphiQL GUI to enter queries and test them.

Categories
GraphQL JavaScript

Create and Use Data Types with Express GraphQL

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.

Categories
GraphQL JavaScript

Adding Mutations with Express GraphQL

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 mutations and input types with Express and GraphQL.

Mutations and Input Types

To create mutations, we create a schema that has the Mutation type rather than a Query .

Then it’s a simple as making the API endpoint part of the top-level Mutation type instead of a Query type.

Both mutations and queries can be handled by root resolvers.

We can then create a GraphQL server that takes both queries and mutations as follows:

const express = require('express');  
const graphqlHTTP = require('express-graphql');  
const { buildSchema } = require('graphql');  
const crypto = require('crypto');  
const schema = buildSchema(`  
  input TodoInput {  
    text: String      
  } 

  type Todo {  
    id: ID!  
    text: String      
  } 

  type Query {  
    getTodo(id: ID!): Todo  
  } 

  type Mutation {  
    createTodo(input: TodoInput): Todo  
    updateTodo(id: ID!, input: TodoInput): Todo  
  }  
`);

class Todo {  
  constructor(id, { text }) {  
    this.id = id;  
    this.text = text;  
  }  
}

let todos = {};
const root = {  
  getTodo: ({ id }) => {  
    if (!todos[id]) {  
      throw new Error('Todo not found.');  
    }  
    return new Todo(id, todos[id]);  
  },  
  createTodo: ({ input }) => {  
    const id = crypto.randomBytes(10).toString('hex');  
    todos[id] = input;  
    return new Todo(id, input);  
  },  
  updateTodo: ({ id, input }) => {  
    if (!todos[id]) {  
      throw new Error('Todo not found');  
    }  
    todos[id] = input;  
    return new Todo(id, input);  
  },  
};

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 types by writing:

const schema = buildSchema(`  
  input TodoInput {  
    text: String      
  } 

  type Todo {  
    id: ID!  
    text: String      
  } 

  type Query {  
    getTodo(id: ID!): Todo  
  } 

  type Mutation {  
    createTodo(input: TodoInput): Todo  
    updateTodo(id: ID!, input: TodoInput): Todo  
  }  
`);

We created the input type TodoInput and the Todo type. Then we created the Query type with the getTodo member so that we can get our todo items.

Then in our Mutation , we added the createTodo and updateTodo members so that we can add and update todos.

Then we create our Todo class so that we can store the todo data:

class Todo {  
  constructor(id, { text }) {  
    this.id = id;  
    this.text = text;  
  }  
}

Next, we have our root resolver:

const root = {  
  getTodo: ({ id }) => {  
    if (!todos[id]) {  
      throw new Error('Todo not found.');  
    }  
    return new Todo(id, todos[id]);  
  },  
  createTodo: ({ input }) => {  
    const id = crypto.randomBytes(10).toString('hex');  
    todos[id] = input;  
    return new Todo(id, input);  
  },  
  updateTodo: ({ id, input }) => {  
    if (!todos[id]) {  
      throw new Error('Todo not found');  
    }  
    todos[id] = input;  
    return new Todo(id, input);  
  },  
};

It adds the functions with the same as we specified in our schema so that we can do something when we make some queries.

In this example, getTodo , we’ll return the todo with the given id .The todo that’s found will be returned. Otherwise, we throw an error.

In createTodo , we get the input from the query and then add the todo entry to our todos object, which is our fake database to store the todos. The todo that’s saved will be returned.

Then we have the updateTodo function to update the todo by id . Whatever has the given id will be replaced with the content of input . The todo that’s saved will be returned. We throw an error if a todo with the given id isn’t found.

Then when we go to the /graphql page, we can type in the following to the GraphiQL window:

mutation {  
  createTodo(input: {text: "eat"}) {  
    id  
    text  
  }  
}

Then we get something like:

{  
  "data": {  
    "createTodo": {  
      "id": "c141d1fda69e8d9084bd",  
      "text": "eat"  
    }  
  }  
}

as the response.

If we make a query for updating todo as follows:

mutation {  
  updateTodo(id: "e99ce10750c93793a23d", input: {text: "eat"}) {  
    id  
    text  
  }  
}

We get something like:

{  
  "data": {  
    "updateTodo": {  
      "id": "e99ce10750c93793a23d",  
      "text": "eat"  
    }  
  }  
}

back as the response.

If a todo isn’t found, then we get:

{  
  "errors": [  
    {  
      "message": "Todo not found",  
      "locations": [  
        {  
          "line": 9,  
          "column": 3  
        }  
      ],  
      "path": [  
        "updateTodo"  
      ]  
    }  
  ],  
  "data": {  
    "updateTodo": null  
  }  
}

as the response.

We can make the getTodo query as follows:

query {  
  getTodo(id: "e99ce10750c93793a23d"){  
    id  
    text  
  }  
}

Then we get:

{  
  "data": {  
    "getTodo": {  
      "id": "e99ce10750c93793a23d",  
      "text": "eat"  
    }  
  }  
}

as the response.

Conclusion

We can create mutations as we do with queries.

To accept mutation operations in our GraphQL server, we make our types for storing our data, then we create our mutations by populating the Mutation type with our members.

We can then use the buildSchema function to build the schema we just specified.

Then in our root reducer, we make the functions that with the names that we specified in the type definitions.

Finally, we can make queries to our server to run the mutations.