# The store

The store is a document store. It can get and set documents (JavaScript objects) based on a type and unique key.

The store is formed of two parts:

  • a GraphQL schema: defines types and fields.
  • a mocks object: to generate mock values for every field.

This article assumes the following schema and mocks:

type Book {
  id: Int
  title: String
  author: String
}
const mocks = {
  String: () => faker.lorem.words(4),
  Int: () => faker.datatype.number({ min:1, max:999 }),
  Book: {
    author: () => `${faker.name.firstName()} ${faker.name.lastName()}`
  }
}

# Entities

The store has a concept of "entities": each type can have any number of entities. You can think of types as "tables" and entities as "rows". Entities are referenced by a unique field. By default the fields id or _id will be used as the unique field.

A very distinct feature of this store is that it will always return a value, even when it is empty:

store.get('Book', 1, 'title');

This returns the value of the field: title for entity: Book with id: 1.

The store will first check its cache for a value for Book with an id of 1. If this exists, that value is returned. If not, it will generate a value using the mocks: it will first try to use a mock for Book.id, if none is provided, it will use the mock for the type of the field declare in mocks above (Int).

# References

When using the get method without specifying a field the store will return a reference to the requested entity, not values:

store.get('Book', 1);

The above will return:

{
  $ref: {
    typeName: 'Book',
    key: 1
  }
}

To get values, we can also use a reference:

const $book = store.get('Book', 1);

// some time later
const title = store.get($book, 'title');

# Mutations

The store can also be modified using mutations. Values can be set for any entity, regardless of it already exists in the store.

store.set('Book', 1, 'author', 'John Doe');

This snippet sets the field: author to 'John Doe' for the entity: Book with id: 1.

# Default resolvers

All resolvers are auto-mocked by default and use the store to return values. Assuming the following schema and mocks:

type Query {
  books: [Book]
}

type Book {
  id: Int
  title: String
  author: String
}
const mocks = {
  String: () => faker.lorem.words(4),
  Int: () => faker.datatype.number({ min:1, max:999 }),
  Book: {
    author: () => `${faker.name.firstName()} ${faker.name.lastName()}`
  }
}

The default implementation for the books query will be:

const resolvers = {
  Query: {
    books: () => {
      return store.get('Query', 'ROOT', 'books');
    }
  }
}

It's important to note that the store considers Query the same as any other type (such as Book). The Query and Mutation types have one key difference: they have a single "entity" with the unique key "ROOT".

When the call store.get('Query', 'ROOT', 'books') happens, the store uses the schema to identify what to return: Query.books returns the type [Book] aka an array of Book. Note: The default for any auto-mocked "array of things" is to return two of those things.

The store generates two Book entities with only an id field as no other fields were specified on the request. Data for fields is only generated when requested (through queries). The call store.get('Query', 'ROOT', 'books') then returns two references.

The resolver return references to these entities in the store. The engine will then continue to execute the graph, querying the store for the specific fields for these entities.