Mocking your GraphQL API

Mocking your GraphQL API is a common practice when developing and testing your application. It allows you to simulate the behavior of your API without making real network requests.

Installing

Start by installing the @graphql-mesh/plugin-mock package:

npm i @graphql-mesh/plugin-mock

How to use?

Add it to your plugins:

import { defineConfig } from "@graphql-hive/gateway";
import { useMock } from "@graphql-mesh/plugin-mock";

export const gatewayConfig = defineConfig({
  plugins: [
    useMock({
      mocks: [
        {
          apply: "User.firstName",
          faker: "{{name.firstName}}",
        },
      ],
    }),
  ],
});

The example above will replace the resolver of User.firstName with a mock that uses faker.js to generate a random name.

Custom mock functions for fields

You can also provide a custom function to generate the mock value for a field:

import { defineConfig } from "@graphql-hive/gateway";
import { useMock } from "@graphql-mesh/plugin-mock";
import { fullName } from "./user-mocks.js";

export const gatewayConfig = defineConfig({
  plugins: (pluginCtx) => [
    useMock({
      mocks: [
        {
          apply: "User.fullName",
          custom: fullName,
        },
      ],
    }),
  ],
});

Custom mock functions for types

You can mock types with custom mock functions like below;

import { defineConfig } from "@graphql-hive/gateway";
import { useMock } from "@graphql-mesh/plugin-mock";
import { user } from "./user-mocks.js";

export const gatewayConfig = defineConfig({
  plugins: (pluginCtx) => [
    useMock({
      mocks: [
        {
          apply: "User",
          custom: user,
        },
      ],
    }),
  ],
});
export const mockFullName = () => {
  return `John Doe`;
};

When defined manually, properties can return values either directly or through a method. This is useful when defining static mocks because a mock property will be called as many times as there are items in an array. Here’s an example on how this could be achieved:

function* generateNames() {
  while (true) {
    yield "John Doe";
    yield "John Snow";
  }
}

const fullNames = generateNames();

export const fullName = () => fullNames.next().value;

Mocking the lists

Hive Gateway generates two mocked items by default if the return type is a list. But this can be configured, as shown below:

type Query {
  users: [User]
}
type User {
  id: ID
  fullName: String
}
import { defineConfig } from "@graphql-hive/gateway";
import { useMock } from "@graphql-mesh/plugin-mock";

export const gatewayConfig = defineConfig({
  plugins: (pluginCtx) => [
    useMock({
      mocks: [
        {
          apply: "User.fullName",
          faker: "{{name.fullName}}",
        },
        {
          apply: "Query.users",
          length: 3,
        },
      ],
    }),
  ],
});

Now query { users { id fullName } }{:graphql} query will return 3 of User item;

{
  "users": [
    { "id": "SOME_RANDOM_ID", "fullName": "John Doe" },
    { "id": "SOME_RANDOM_ID", "fullName": "Jane Doe" },
    { "id": "SOME_RANDOM_ID", "fullName": "The Other Doe" }
  ]
}

Stateful mocking

Hive Gateway supports GraphQL Tools' Stateful Mocking feature. So you can have stateful mocking by using the store provided in the context context.mockStore;

Initialize store

When having a schema that returns a list, in this case, a list of users:

import type { IMockStore } from "@graphql-mesh/plugin-mock";

export function initializeStore(mockStore: IMockStore) {
  const users = [{ id: "uuid", name: "John Snow" }];
  // Set individual users' data in the store so that they can be queried as individuals later on
  users.forEach((user) => {
    mockStore.set("User", user.id, user);
  });

  // Populate the `users` query on the root with data
  mockStore.set("Query", "ROOT", "users", users);
}

Get from the store

You can implement the mock query field *ById declaratively like below:

type Query {
  user(id: ID): User
  users: [User]
}
import { defineConfig } from "@graphql-hive/gateway";
import { useMock } from "@graphql-mesh/plugin-mock";
import { initializeStore } from "./init-store.js";

export const gatewayConfig = defineConfig({
  plugins: (pluginCtx) => [
    useMock({
      initializeStore,
      mocks: [
        {
          apply: "Query.user",
          custom: (_, args, context) => context.mockStore.get("User", args.id),
        },
      ],
    }),
  ],
});

Mutate data in the store

type User {
  id: ID
  name: String
}
type Query {
  me: User
  user(id: ID): User
  users: [User]
}
type Mutation {
  changeMyName(newName: String): User
  updateUser(id: ID, name: String): User
}
import { defineConfig } from "@graphql-hive/gateway";
import { useMock } from "@graphql-mesh/plugin-mock";
import { initializeStore } from "./init-store.js";

interface User {
  id: string;
  name: string;
}

export const gatewayConfig = defineConfig({
  plugins: (pluginCtx) => [
    useMock({
      initializeStore,
      mocks: [
        {
          apply: "Query.me",
          custom: (_, args, context) => context.mockStore.get("User", "uuid"),
        },
        {
          apply: "Mutation.changeMyName",
          custom: (_, args, context) => {
            const user = context.mockStore.get("User", "uuid") as User;
            user.name = args.newName;
            context.mockStore.set("User", "uuid", user);
            return user;
          },
        },
        {
          apply: "Mutation.updateUser",
          custom: (_, args, context) => {
            const user = context.mockStore.get("User", args.id) as User;
            user.name = args.name;
            context.mockStore.set("User", args.id, user);
            return user;
          },
        },
      ],
    }),
  ],
});