Using GraphQL Paper with GraphQL

GraphQL Paper is built on the core graphql library and works well with its default resolvers. GraphQL Paper should also work well with other javascript systems that use resolver functions, too. The best integrations will still be with graphql-mocks and its packages which are designed and tested together.

If using GraphQL Paper with graphql-mocks check out the guide dedicated to setup, common patterns and techniques.

Example

Here is an example of GraphQL Paper working with the core graphql package

Schema

The full Querying and Mutating examples below make use of the following schema:

const graphqlSchema = `
schema {
query: Query
mutation: Mutation
}
type Query {
films: [Film!]!
film(filmId: ID!): Film
}
type Mutation {
addFilm(input: AddFilmInput!): Film!
}
type Film {
id: ID!
title: String!
year: String!
actors: [Actor!]!
}
type Actor {
id: ID!
name: String!
}
input AddFilmInput {
title: String!
year: String!
actors: [AddFilmActorInput!]!
}
input AddFilmActorInput {
name: String!
}
`;
// kick everything off
export default graphqlSchema;

Querying

To resolve queries in a GraphQL Resolver return data from the paper.data property on a Paper instance. The GraphQL Paper Querying Data documentation provides more information on how to retrieve data with GraphQL Paper.

GraphQL Paper also provides connections between different types and their corresponding documents out of the box which allows for the resolution of resolvers to work recursively. To kick off this behavior return the appropriate Document or list of Documents from the top-level Query resolvers.

Complete Query Example

In this example returning the resolvers for a list of [Film!] from the Query.films or a single Film by ID from Query.film will also satisify any queries that request a film's Actors by returning the actors connections on the Film document.

Using the graphql npm package with the above schema for an end-to-end example involving queries.

import schemaString from "./schema";
import { Paper } from "graphql-paper";
import { graphql, buildSchema } from "graphql";
async function run() {
const graphqlSchema = buildSchema(schemaString);
const paper = new Paper(graphqlSchema);
await paper.mutate(({ create }) => {
create("Film", {
id: "1",
title: "Jurassic Park",
year: "1993",
actors: [
{ id: "1", name: "Jeff Goldblum" },
{ id: "2", name: "Wayne Knight" },
],
});
create("Film", {
id: "2",
title: "Office Space",
year: "1999",
actors: [
{ id: "3", name: "Ron Livingston" },
{ id: "4", name: "Jennifer Aniston" },
],
});
});
const queryType = graphqlSchema.getQueryType();
const queryTypeFields = queryType.getFields();
queryTypeFields.films.resolve = function filmsResolver(
root,
args,
context,
info
) {
// return all `Film` Documents
return paper.data.Film;
};
queryTypeFields.film.resolve = function filmResolver(
root,
args,
context,
info
) {
return paper.data.Film.find((film) => film.id === args.filmId) ?? null;
};
const query = `
query {
film(filmId: "1") {
id
title
year
actors {
id
name
}
}
films {
id
title
year
# will return the connected Actor documents automatically
actors {
id
name
}
}
}
`;
const result = await graphql({
source: query,
schema: graphqlSchema,
});
console.log(result);
}
// kick everything off!
run();
Result:
{
  "data": {
    "film": {
      "actors": [
        {
          "id": "1",
          "name": "Jeff Goldblum"
        },
        {
          "id": "2",
          "name": "Wayne Knight"
        }
      ],
      "id": "1",
      "title": "Jurassic Park",
      "year": "1993"
    },
    "films": [
      {
        "id": "1",
        "title": "Jurassic Park",
        "year": "1993",
        "actors": [
          {
            "id": "1",
            "name": "Jeff Goldblum"
          },
          {
            "id": "2",
            "name": "Wayne Knight"
          }
        ]
      },
      {
        "id": "2",
        "title": "Office Space",
        "year": "1999",
        "actors": [
          {
            "id": "3",
            "name": "Ron Livingston"
          },
          {
            "id": "4",
            "name": "Jennifer Aniston"
          }
        ]
      }
    ]
  }
}

Mutating

Mutations also work with GraphQL Paper and graphql. The GraphQL Paper Mutating Data documentation provides more information on how mutating data with GraphQL Paper.

Returning from Mutations

If a document can satisfy a mutation's expected return type then return it from paper.mutate. In this example if the mutation field returns a GraphQL Type of SomeType then

async function createSomeTypeMutationResolver(root, args, context, info) {
// return the paper.mutate
return paper.mutate(({ create }) => {
// return the created SomeType Paper Document
return create('SomeType', { /* use data from args... */ });
});
}

If the result is not satisfied by the document the result can be awaited and modified as needed. Assuming in this example the mutation payload only required the payload with the ID

{ someTypeId: newId }

Then the mutation resolver could look like:

async function createSomeTypeMutationResolver(root, args, context, info) {
// return the paper.mutate
const someTypeDocument = await paper.mutate(({ create }) => {
// return the created SomeType Paper Document
return create('SomeType', { /* use data from args... */ });
});
// take the resulting document and returning the required shape
return {
someTypeId: someTypeDocument.id
};
}

Complete Mutation Example

Using the graphql npm package with the above schema for an end-to-end example using mutations.

import schemaString from "./schema";
import { Paper } from "graphql-paper";
import { graphql, buildSchema } from "graphql";
async function run() {
const graphqlSchema = buildSchema(schemaString);
const paper = new Paper(graphqlSchema);
const mutationType = graphqlSchema.getMutationType();
const mutationTypeFields = mutationType.getFields();
mutationTypeFields.addFilm.resolve = function filmsResolver(
root,
args,
context,
info
) {
return paper.mutate(({ create, getStore }) => {
const store = getStore();
const maxIdReducer = (previous, { id }) =>
Math.max(Number(previous), Number(id)).toString();
let lastFilmId = store.Film.reduce(maxIdReducer, "0");
let lastActorId = store.Actor.reduce(maxIdReducer, "0");
const newFilm = create("Film", {
id: ++lastFilmId,
title: args.input.title,
year: args.input.year,
});
newFilm.actors = args.input.actors.map((actor) => {
return create("Actor", {
id: ++lastActorId,
...actor,
});
});
return newFilm;
});
};
const mutation = `
mutation($addFilmInput: AddFilmInput!) {
addFilm(input: $addFilmInput) {
id
title
year
actors {
id
name
}
}
}
`;
const mutationInput = {
title: "My Girl",
year: "1991",
actors: [{ name: "Anna Chlumsky" }],
};
const result = await graphql({
source: mutation,
schema: graphqlSchema,
variableValues: { addFilmInput: mutationInput },
});
console.log(result);
}
// kick everything off!
run();
Result:
{
  "data": {
    "addFilm": {
      "actors": [
        {
          "id": "1",
          "name": "Anna Chlumsky"
        }
      ],
      "id": "1",
      "title": "My Girl",
      "year": "1991"
    }
  }
}