Rapid GraphQL server setup for testing with Faker

Step by step guide on how to install, setup, generate fake data for JSON files, and run a GraphQL server (GraphiQL included) for testing or mocking in under 10 minutes.

June 16, 2020

What is covered

Code repository

Primers

  • json-graphql-server "Get a full fake GraphQL API with zero coding in less than 30 seconds"

  • faker.js "Generate massive amounts of fake data in the browser and Node.js"

Installing json-graphql-server and faker

Nothing special here npm i --save-dev json-graphql-server faker. We also need some other Node.js packages to set the whole thing up: express, ip, lodash.get, lodash.find, lodash.filter, lodash.findindex, lodash.startcase, random-string.

Generate data with Faker

Let’s take a look at /scripts/faker-data.js file. We first define some constants (lines 5-25) to calibrate Faker generated data, dataSet object we want populated with data, the helper function generateGroup and we make use of it to generate featuredPool. Between lines 62-105 we generate the data making use of the Faker functions lorem, random, company, name, finance. Finally, we write the JSON stringified dataSet to the dataSet.json file. We also create the activation.json file to authenticate the Apple TV devices based on their UUID.

const faker = require('faker');
const fs = require('fs');
const startCase = require('lodash.startcase');

const MAX = 100;
const FEATURED = 6;
const CATEGORIES = [
  'Action',
  'Adventure',
  'Comedy',
  'Crime',
  'Drama',
  'Epics',
  'Horror',
  'History',
  'Documentary',
  'Musical',
  'Sci-Fi',
  'War',
  'Western',
  'Thriller',
  'Biographical',
  'Animated',
  'Fantasy',
];

const dataSet = {
  categories: [],
  movies: [],
};

const generateGroup = (no, func, join = true) => {
  const group = [];

  while (group.length < no) {
    const item = func();
    if (group.indexOf(item) === -1) {
      group.push(item);
    }
  }

  if (join) {
    return group.join(', ');
  }

  return group;
};

const featuredPool = generateGroup(
  FEATURED,
  () => faker.random.number({ min: 1, max: MAX }),
  false,
);

CATEGORIES.forEach((categ, idx) => {
  dataSet.categories.push({
    id: idx + 1,
    title: categ,
  });
});

for (let i = 1; i <= MAX; i += 1) {
  dataSet.movies.push({
    id: i,
    title: startCase(faker.lorem.words()),
    released: faker.date.past(),
    genre: generateGroup(faker.random.number({ min: 1, max: 3 }), () =>
      faker.random.arrayElement(CATEGORIES),
    ),
    rated: faker.random.arrayElement([
      'G',
      'PG',
      'PG-13',
      'R',
      'NC-17',
      'NR',
      'UR',
    ]),
    country: generateGroup(faker.random.number({ min: 1, max: 3 }), () =>
      faker.random.arrayElement([
        'USA',
        'UK',
        'Hong Kong',
        'Canada',
        'Australia',
      ]),
    ),
    production: faker.company.companyName(),
    runtime: faker.random.number({ min: 60, max: 180 }),
    director: faker.name.findName(),
    writer: faker.name.findName(),
    actors: generateGroup(faker.random.number({ min: 4, max: 8 }), () =>
      faker.name.findName(),
    ),
    plot: faker.lorem.paragraph(),
    poster:
      'https://i.kinja-img.com/gawker-media/image/upload/b4xfydll6qktsk9dwxve.jpg',
    cover: 'https://cdn.hipwallpaper.com/i/69/13/V07gqa.jpg',
    rating: faker.finance.amount(4, 9, 1),
    boxOffice: `$${faker.finance.amount(600000, 1000000000, 0)}`,
    featured: featuredPool.indexOf(i) !== -1,
    trailer:
      'http://movietrailers.apple.com/movies/wb/wonderwoman/wonder-woman-trailer-5_h720p.mov',
  });
}

fs.writeFile(
  `${__dirname}/../backend/dataSet.json`,
  JSON.stringify(dataSet, null, 2),
  () => console.log('dataSet.json file generated'),
);

fs.writeFile(
  `${__dirname}/../backend/activation.json`,
  JSON.stringify({ devices: [] }, null, 2),
  () => console.log('activation.json file generated'),
);

Getting the Express backed server ready

/backend/server.js is an Express backed server using the jsonGraphqlExpress middleware. Those helpers imported at line 8 are used to read and write to the .json files. We also simulate a network delay of half a second at line 16. The first 3 routes /me, /activation-code, /logout are required by the authentication system of the Apple TV devices. The Express middleware between lines 68-78 is a firewall looking for uuid variable in the request headers or in the query params. Finally, we load the jsonGraphqlExpress middleware and start the GraphQL server.

const express = require('express');
const cors = require('cors');
const jsonGraphqlExpress = require('json-graphql-server').default;
const ip = require('ip');
const get = require('lodash.get');
const randomstring = require('random-string');

const { isActivated, insertActivation, logout, getMe } = require('./helpers');
const db = require('./db');

const PORT = 3100;
const IP = ip.address();
const app = express();

const DELAY = 500;
app.use((req, res, next) => setTimeout(next, DELAY));

app.use(cors());

app.get('/me', (req, res) => {
  const uuid = get(req, 'headers.uuid', null);
  if (!uuid) {
    res.sendStatus(400);
  } else {
    getMe(uuid, me => {
      if (!me) {
        res.sendStatus(401);
      } else {
        res.json(me);
      }
    });
  }
});

app.get('/activation-code', (req, res) => {
  const uuid = get(req, 'headers.uuid', null);
  if (!uuid) {
    res.sendStatus(400);
  } else {
    const activationCode = randomstring({ length: 6 }).toUpperCase();
    const newDevice = {
      activationCode,
      uuid,
      activated: false,
    };

    insertActivation(newDevice, () => {
      res.json(activationCode);
    });
  }
});

app.get('/logout', (req, res) => {
  const uuid = get(req, 'headers.uuid', null);
  if (!uuid) {
    res.sendStatus(400);
  } else {
    logout(uuid, result => {
      if (!result) {
        res.sendStatus(400);
      } else {
        res.sendStatus(204);
      }
    });
  }
});

app.use((req, res, next) => {
  const uuidHeaders = get(req, 'headers.uuid', null);
  const uuidParams = get(req, 'query.uuid', null);
  const uuid = uuidHeaders || uuidParams;

  if (uuid && isActivated(uuid)) {
    next();
  } else {
    res.sendStatus(401);
  }
});

app.use('/graphql', jsonGraphqlExpress(db));

app.listen(PORT, () => {
  console.log(`GraphQL server is running on http://${IP}:${PORT}`);
});

First you need an activated UUID to authenticate. Please add to the /backend/activation.json file one activated UUID like this:

{
  "devices": [
    {
      "id": 1,
      "activationCode": "BJDRIT",
      "uuid": "35814a01-f678-4ff0-8ccd-62263409138f",
      "activated": true
    }
  ]
}

Now you can play with the server using the GraphiQL interface using an URL like this http://192.168.0.2:3100/graphql?uuid=35814a01-f678-4ff0-8ccd-62263409138f where 192.168.0.2 might be your connection allocated IP address. Check your network settings to find what your real IP address is.