Mock your GraphQL server realistically with faker.js

🦁 Yvonnick FRIN - Aug 7 '19 - - Dev Community

Sometimes your GraphQL server need to use an API that isn't implemented yet or that is unavailable temporarly. Mocking seems the right thing to do in this case but it's hard to maintain good mock data so we end up with "Lorem ipsum" everywhere in our apps.

faker.js

faker.js is a library that let you generate fake data in node or in the browser. It embarks a lot of methods to generate data for common use cases like:

  • names
  • addresses
  • phones
  • images
  • companies
  • ...

Even for... Lorem ipsum!

Other tools

We will use Koa to create the server. It is "a new web framework designed by the team behind Express". In practice, it has a different middleware implementation and embarks less things than express. If you want more information a comparaison is available on Koa's repository.

For GraphQL implementation we will use Apollo Server. It seems to be the most popular server implementation of GraphQL and it fits well with Koa since an apollo-server-koa package exists and have been implemented by the Apollo Server team.

Finally we will use the well-known cross-env package to set environnement variables regardless of the platform you are using.

Setting up the server

First of all, we create a folder with the following structure:

.
├── package.json
└── src
    └── index.js
Enter fullscreen mode Exit fullscreen mode

For this article we will be using yarn but you can use npm as well. Init package.json with the init command:

yarn init
Enter fullscreen mode Exit fullscreen mode

Install the following dependencies:

yarn add apollo-server-koa cross-env faker graphql koa
Enter fullscreen mode Exit fullscreen mode

Add a start script that execute our src/index.js file:

{
  "scripts": {
    "start": "node src/index.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

In the src/index.js file we instantiate a new Koa application:

const Koa = require('koa');

const app = new Koa();

app.listen({port: 4000}, () =>
  console.log(`🚀 Server ready at http://localhost:4000`),
);
Enter fullscreen mode Exit fullscreen mode

Run your start script. You should see the console.log message present in the above example in the console output.

Create the GraphQL endpoint

It is time to implement our GraphQL endpoint. Let's say we are in the situation where an API isn't implemented yet. This API is supposed to expose a list of persons with their firstname and lastname. We will define a schema and make it accessible on a /graphql endpoint with apollo-server-koa.

Import ApolloServer and gql from apollo-server-koa:

const {ApolloServer, gql} = require('apollo-server-koa');
Enter fullscreen mode Exit fullscreen mode

We define a query that returns a list of person and the type Person itself:

const typeDefs = gql`
  type Person {
    lastname: String
    firstname: String
  }

  type Query {
    persons: [Person]
  }
`;
Enter fullscreen mode Exit fullscreen mode

Since the API doesn't exist yet we throw an error to inform user that he can't use this query:

const resolvers = {
  Query: {
    persons: () => {
      throw Error('Not yet implemented');
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

We instantiate an Apollo server with the our type definitions and the resolver for our query:

const server = new ApolloServer({
  typeDefs,
  resolvers,
});
Enter fullscreen mode Exit fullscreen mode

Finally, apply the GraphQL middleware previously created to the Koa application:

server.applyMiddleware({app});
Enter fullscreen mode Exit fullscreen mode

If you start your server right now with the command yarn start and open the url http://localhost:4000/graphql in your browser. You should see the nice interface of GraphQL IDE.

GraphQL IDE

If you type a query to retrieve the list of persons with their data:

{
  persons {
    firstname,
    lastname
  }
}
Enter fullscreen mode Exit fullscreen mode

It should result in getting the following error:

{
  "errors": [
        {
            "message": "Not yet implemented",
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Mock it with faker.js

This error isn't the expected result. We want random realistic data when our server is mocked. To achieve it we need to override the default resolver that throw an error by another that generate fake data.

For this purpose we will set the NODE_ENV environnement variable to mock to determine which behavior our Apollo server should follow. We will achieve that by adding a mock script in our package.json that set the NODE_ENV variable using cross-env and calling the start script:

{
  "scripts": {
    "start": "node src/index.js",
    "mock": "cross-env-shell NODE_ENV=mock yarn start"
  }
}
Enter fullscreen mode Exit fullscreen mode

Apollo server has a mocks property in its options that take either a Boolean or a object with mocked resolvers. To begin we will set it to true if NODE_ENV is equal to mock:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  mocks: process.env.NODE_ENV === 'mock' ? true : false,
});
Enter fullscreen mode Exit fullscreen mode

At this step if you re-execute your query in the GraphQL IDE you end up with a first result:

{
  "data": {
    "persons": [
      {
        "firstname": "Hello World",
        "lastname": "Hello World"
      },
      {
        "firstname": "Hello World",
        "lastname": "Hello World"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

That's nice but it isn't really realistic. To add faker we need to implement our own mocked resolver and pass it to Apollo server. In mocked resolvers the Query property has to be a function that return an object with the resolvers definition. In our resolver for persons query we return an array of two person.

faker.js methods are organized in namespace. We will use the name namespace that contains methods such as:

  • firstName
  • lastName
  • jobTitle
  • title
  • ...

You guess it, we will use firstName and lastName methods to generate random data for our two persons:

const mockResolvers = {
  Query: () => ({
    persons: () => [
      {
        firstname: name.firstName(),
        lastname: name.lastName(),
      },
      {
        firstname: name.firstName(),
        lastname: name.lastName(),
      },
    ],
  }),
};

// ...

const server = new ApolloServer({
  typeDefs,
  resolvers,
  mocks: process.env.NODE_ENV === 'mock' ? mockResolvers : false,
});
Enter fullscreen mode Exit fullscreen mode

Execute your query in the GraphQL IDE you now have nice datas like:

{
  "data": {
    "persons": [
      {
        "firstname": "Mélissa",
        "lastname": "Mathieu"
      },
      {
        "firstname": "Camille",
        "lastname": "Richard"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Bonus

Here is a method I use to generate random sized arrays for my mocked resolvers:

const randomArray = (min, max, callback) => {
  const size = random.number({min, max});
  return Array.from({length: size}, callback);
};
Enter fullscreen mode Exit fullscreen mode

We could refactor the previous resolver using this method like this:

const mockResolvers = {
  Query: () => ({
    persons: () =>
      randomArray(2, 6, () => ({
        firstname: name.firstName(),
        lastname: name.lastName(),
      })),
  }),
};
Enter fullscreen mode Exit fullscreen mode

I made a repository with all the sources presented in this article. Feedback is appreciated 🙏 Please tweet me if you have any questions @YvonnickFrin!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .