Categories
GraphQL

Authentication and Express Middleware with 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 use middleware with Express GraphQL.

Express Middleware

We can use Express middlewares as usual if we use express-graphql to build our GraphQL server with Express.

The request object is available as the second argument in any resolver.

For example, if we want to get the hostname of a request in our resolver, we can write:

const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');

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

const loggingMiddleware = (req, res, next) => {
  console.log(req.hostname);
  next();
}

const root = {
  hostname(args, request) {
    return request.hostname;
  }
};

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

In the code above, we created our schema as usual to get the hostname of the app.

Then we added our loggingMiddleware to log the hostname. It calls next so we can use our graphqlHTTP middleware.

Then in our root resolver, we added a hostname method, which takes the request parameter as the second argument, which has the Express request object.

This is where we can return the hostname property from request so that we can return it in the response.

This means that we can continue to use middleware to handle authentication as we did with REST APIs.

Popular authentication options for Express include Passport, express-jwt , and express-session .

Authentication

We can use jsonwebtoken in our Express GraphQL server as follows to add authentication via JSON web token.

To do this, we first install jsonwebtoken by running:

npm i `jsonwebtoken`

Then we can include it in our app and then add it to our Express GraphQL server as follows:

const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');
const jwt = require('jsonwebtoken');
const unless = require('express-unless');

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

const root = {
  hostname(args, request) {
    return request.hostname;
  }
};

const verifyToken = (req, res, next) => {
  jwt.verify(req.headers.authorization, 'secret', (err, decoded) => {
    if (err){
      return res.send(401);
    }
    next();
  });
}
verifyToken.unless = unless;

const app = express();
app.post('/auth', (req, res) => {
  const token = jwt.sign({ foo: 'bar' }, 'secret');
  res.send(token);
})

app.use(verifyToken.unless({ path: ['/auth'] }));
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(3000, () => console.log('server started'));

In the code above, we have the verifyToken middleware to verify the token that’s issued by our auth route.

In verifyToken , we called jwt.verify to verify the token that we passed into the Authorization header. If we get an error, then we send a 401 response.

Otherwise, we call next to proceed to the next middleware our route.

In the auth route, we issue our authentication token by calling jwt.sign with whatever content we want as the first argument and the token’s secret in the second argument.

Also, we excluded the verifyToken middleware from the auth route by using the express-unless middleware.

We did that by assigning unless to verifyToken.unless .

Then now when we want to make a request to our GraphQL server via the /graphql route, we have to pass in our auth token to the Authorization header.

This keeps our GraphQL requests more secure. However, we should have an encrypted secret if we’re going to JSON web tokens in the real world.

Conclusion

We can use Express middleware for logging, authentication, or whatever we need them for.

To include middleware, we just call the app.use method as usual.

We can exclude routes from using the middleware with the express-unless package.

To add authentication via JSON web tokens, we can use the jsonwebtoken package to add token issuing and verification capabilities to our Express GraphQL server.

Categories
GraphQL

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.

Categories
GraphQL

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

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

More 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 some GraphQL data types including lists, required fields, interfaces, union types, and input types.

Lists and Non-Null

A non-null type can be created by using the ! type modifier.

With that added, it’ll make sure that we have to include the field when we’re making a query to the server.

The non-null modifier can be used for defining arguments for a field, which causes the GraphQL server to return a validation error if a null value is passed as that argument.

Lists work in a similar way. We can use square brackets to mark something as a list.

For example, we can use them together as follows:

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

In the code above, we marked the name field as required and have the String type. Also, we have an addresses field which is a list of Address objects, and it’s also marked as required.

If we have something like:

myField: [String!]

This means that the list itself can be null, but myField can’t have any null members.

On the other hand, if we have:

myField: [String]!

then the list itself can’t be null, but it can contain null values.

We can nest any number of non-null and list modifiers according to our needs.

Interfaces

An interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.

For example, we can create an interface as follows:

interface Person {
  id: ID!
  name: String!
  friends: [Person]
}

This means that a type that implements Person must have the exact fields as we listed above.

For example, we can create a type that implements the interface as follows:

type Employee implements Person{
  id: ID!
  name: String!
  friends: [Person],
  employeeId: String!
}

In the code above, we have the Employee type that has all the fields above. We also have the employeeId string.

Therefore, we can use interfaces to ensure that a type has at least the fields and types that are listed in the interface.

To query something with various types that are implemented by an interface, we can use inline fragments as follows:

query Employee($id: ID!) {
  employee(id: $id) {
    name
    ... on Employee {
      employeeId
    }
  }
}

Union types

Unio types are similar to interfaces, but they don’t get to specify any common fields between types.

We can define a union type with the union keyword as follows:

union Worker = Person | Employee

We’ll need to use a conditional fragment to be able to query any fields:

{
  search(text: "an") {
    __typename
    ... on Person {
      name
    }
    ... on Employee{
      name
      employeeId
    }
  }
}

The __typename field is a String that indicates the type of the response object.

Input types

We can create input types to pass in whole objects into a request. For example, we can define an input type as follows:

input PersonInput {
  firstName: String!
  lastName: String!
}

Then we can pass it into a mutation as follows:

mutation CreatePerson($person: PersonInput!) {
  createReview(person: $person) {
    firstName
    lastName
  }
}

The fields on an input object can refer to input object types, but we can’t mix input and output types in our schema. Input object types also can’t have arguments in their fields.

Conclusion

To make sure a field isn’t null we can use the exclamation mark to mark it non-null.

We can also use square brackets to mark that something is a list.

To define a list of set fields for types, we can use interfaces to make sure all types that implement an interface have the fields listed.

Union types are useful for letting us combine common fields of types that implement the same interface and avoid repeating fields in queries.

We can use input types to pass in whole objects into a mutation.