Fields

All GraphGen needs to create data is a schema

In this section, we'll see how to use GraphQL types to generate simple data.

Basic Types

Without providing any information than just a type's declaration, Graphgen will introspect its fields an creates a record that will satisfy their constraints. So, for example, we can take the following declaration for a Person type:

type Person {
  name: String!
  age: Int!
}

And graphgen will create a record with a name that is a string, and an age which is an integer. We can see this in action if we executed the following.

graphgen.create("Person");
// {
//   name: "Person.name 5057341"
//   age: 302
// }

Custom Generators

While technically "Person.name 5057341" is a string, and 302 is an integer, as actual values for a name and an age they are terrible! Luckily graphgen has a mechanism to make this data much more realistic. To use it we have to do two things: first, attach a custom directive to the field saying what generation method we want this field to use, and second, we have to define that generation method when we create our graphgen. Let's see what the directives look like:

type Person {
  name: String! @gen(with: "human.fullName")
  age: String! @gen(with: "human.age")
}

We're now telling graphen not to use just any old method to generate the Person fields, but instead to use the specific human.fullName method to generate the name, and human.age method to generate the age.

In order to specify their implementations we have to pass a "generate" function to graphgen that will match generation methods and return appropriate data for them. The goal here is not to re-invent the wheel, and GraphGen is not prescriptive about how you generate your data. In this example, we'll use FakerJS to generate our field data.

import { faker } from "@faker-js/faker";

const graphgen = createGraphGen({
  source,
  sourcename: "world.graphql",
  generate(info) {
    switch (info.method) {
      case "human.fullName":
        return faker.name.fullName();
      case "human.age":
        return faker.datatype.number({ max: 100, min: 0 });
      default:
        // fallback to the next generation functions
        return info.next();
    }
  },
});

We can now see how much better the data is with our new generation methods:

graphgen.create("Person");
//{
//  name: "Ms. Teri Rutherford",
//  age: 61
//}

💡In practice, you will rarely specify generators manually and attach them to fields. Instead, you will import them wholesale and match them against field names using patterns. To learn more see the section on Generation Methods

Optional Fields

Not everybody has a car. Not everyone has a nickname, and so when generating data, you want to express the variation of data presence by marking that it is optional. To do this, we use the natural syntax of GraphQL which uses ! to indicate that a field is required vs nothing to indicate that a field is optional. We can see in the following example how because not everybody has a bank account number, we can specify it as an optional Int

type Person {
  bankAccountNumber: Int
}

Notice the lack of ! on the field definition. Now, graphgen will assign a person a bank account number half the time, but the other half it won't.

Accounting for chance

But what if 90% of people have a bank account? Then it doesn't make any sense to generate a bank account number half the time. Instead we need to instruct graphgen that it should be doing it nine times out of ten. We can do this by attaching a @has(chance) directive to the field. To take the preceding example:

type Person {
  bankAccountNumber: Int @has(chance: 0.9)
}

This tells GraphGen to generate a bank acount number for 90% of people.