Skip to content

GraphQL

Parris Khachi edited this page Sep 10, 2018 · 2 revisions

Quick Update

The old docs section of this guide is still valid, but needs an update. Namely to account for new features in postgraphile around extending the schema.

Extending the Schema (the older way)

As mentioned previously we rely on Postgraphile to handle much of our GraphQL integration. This means that as you create migrations your GraphQL schema is auto-generated. Another resource to look at is Apollo, which we have built many of our systems on top of as well. In this section, we plan to talk about how to extend graphql. Sometimes you may want to hide certain fields, add extra fields, or add queries/mutations that don't make sense to add into postgres directly.

One common case is when you don't want to fully expose all your database columns to some end users. In that case you can modify the graph/index.js file like so:

function removePrivateQueries(builder) {
  builder.hook('GraphQLObjectType:fields', (fields, _, { scope: { isRootQuery } }) => {
    if (!isRootQuery) { return fields; }
    delete fields.allPeople;

    return fields;
  });
}

function removePrivateMutations(builder) {
  builder.hook('GraphQLObjectType:fields', (spec) => {
    delete spec.createPerson;
    delete spec.deletePerson;
    delete spec.deletePersonById;

    return spec;
  });
}

export default () => ({
  graphiql: true,
  graphiqlRoute: '/admin/graphiql',
  pgDefaultRole: 'mysite_anonymous',
  jwtSecret: process.env.JWT_SECRET,
  jwtPgTypeIdentifier: 'public.jwt_token',
  appendPlugins: [removePrivateMutations, removePrivateQueries],
});

This ends up removing a few fields and the ability to manually add/remove people. It is, after-all, very likely this behavior doesn't make sense to provide CRUD for user. As a result of this, you may wonder... well how would it be possible to add a custom query or mutation. Here is an example:

function sendInvitation(builder) {
  builder.hook('GraphQLObjectType:fields',
  (
    fields,
    { extend, getTypeByName, newWithHooks, graphql },
    { scope: { isRootMutation } },
  ) => {
    if (!isRootMutation) {
      return fields;
    }

    const InvitationPayload = newWithHooks(
      graphql.GraphQLObjectType,
      {
        name: 'InvitationPayload',
        fields: () => ({
          success: {
            type: graphql.GraphQLBoolean,
            description: 'A description here',
          },
        }),
      },
      {},
    );

    return extend(fields, {
      sendInvitation: {
        type: GroupInvitationPayload,
        args: {
          groupId: {
            type: graphql.GraphQLInt,
            description: 'The id of the shared object.',
          },
          emailAddresses: {
            type: graphql.GraphQLString,
            description: 'A comma seperate list of email addresses to be sent the invites.',
          },
        },
        async resolve(_, { emailAddresses, groupId }) {});
          // do some stuff
          return { success: true };
        },
      },
    });
  });
}

The resolve function gets called when a valid mutation named sendInvitation is requested.

For now, we are unable to access the authJWT from here. Postgraphile is working on fixing this. So we recommend either passing authJWT directly into the mutation variables OR creating the function in a migration.

For some other simple examples of extending the graphql schema, check out our todomvc example