January 17, 2017

Webpack’ing your GraphQL Documents

Dhaivat Pandya

Dhaivat Pandya

When building a GraphQL application, there are benefits that come from placing your GraphQL query documents in their own files rather than inline with your view components. For example, you can get simpler editor and tooling support since the tools can look at static files instead of trying to parse application code. Also, it prevents you from accidentally manipulating queries at runtime. However, until now it has been difficult to do with our GraphQL webpack loader since there was no way to have different queries share GraphQL fragments.

This is a short post that describes one of the features we recently built into our Webpack loader for GraphQL documents — graphql-tag — that solves this problem.

Until now, the loader only gave you the ability to load a query from a single file, but with a recent update you can now import fragment definitions from other .graphql files. This makes it easy to place GraphQL documents outside of your view component definitions and share fragments across those documents. Now that the support for these files is fully featured, you can easily take advantage of built-in editor code highlighting for .graphql files, for example on GitHub:

That sweet, sweet highlighting.

Using it

Right now, most Apollo Client apps are written by putting queries inside JavaScript template literals with a gql tag:

export const COMMENT_QUERY = gql`
  query Comment($repoName: String!) {
    …
  }
}`;const withData = graphql(COMMENT_QUERY, {
 // options
});

This is a reasonable approach. But, if we don’t want to place our queries inline with our React components, we can do that with graphql-tag/loader.To start using it, we edit the Webpack config so that it looks like this:

module.exports = {
  …
  module: {
    loaders: [{
      test: /\.(graphql|gql)$/,
      exclude: /node_modules/,
      loader: ‘graphql-tag/loader’
    }]
   },
  …
}

Now, we can place our query document in a file called CommentQuery.graphql, and then import it within our Javascript code:

import COMMENT_QUERY from '../graphql/CommentQuery.graphql';

When the bundle is created, Webpack will load the .graphql file and use the GraphQL parser in order to set COMMENT_QUERY to a parsed version of the query contained within the .graphql file. Now, let’s say that this query used a fragment spread internally:

query Comment($repoName: String!) {
  …
  ...CommentsPageComment
  …
}fragment CommentsPageComment {
  …
} 

We could put both the query definition and the fragment definition in the same file. But, if the fragment is shared across multiple queries, we’d have to define it with every query that used. Instead, we can use graphql-tag/loader’s preprocessor #import statement. This is what our query file would look like:

#import "./CommentsPageComment.graphql"query Comment($repoName: String!) {
 …
 ...CommentsPageComment
 …
}

and the file CommentsPageComment.graphql would contain:

fragment CommentsPageComment on Comment {
  id
  postedBy {
    login
    html_url
  }
  createdAt
  content
}

This allows us to import COMMENT_QUERY as we did previously without worrying about having to import the fragment separately within our Javascript code.

That’s all you need to understand to start using graphql-tag/loader in your projects. If you’d like to see an example, you can check out how we use .graphql files and preprocessor #import statements within our example app, GitHunt-React.

How it works

The implementation of graphql-tag/loader is incredibly simple. Since it is a Webpack loader, it has to turn each .graphql into a bit of Javascript code that Webpack can place into the bundle it creates. Let’s consider what happens when we try to import this file:

#import "./CommentsPageComment.graphql"query Comment($repoName: String!) {
 …
 ...CommentsPageComment
 …
}

Notice that the first line is a GraphQL comment. This means that to an ordinary GraphQL parser, the import statement would be ignored entirely. But, graphql-tag/loader will process it and replace it with a require statement in the outputted code. It will then parse the whole document contained in this file and combine it with the definitions in the imported file. The Javascript code that graphql-tag/loader outputs should look like this:

var doc = // JSON object that represents the parsed GraphQL document
doc.definitions = doc.definitions
  .concat(
    require('CommentsPageComment.graphql').definitions
  );
module.exports = doc;

Notice that with the require statement, graphql-tag/loader will be executed again on CommentsPageComment.graphql by Webpack. This makes it possible to have #import statements within that file too. By generating this Javascript code, the loader makes it possible to import your .graphql files from your Javascript code.

Next steps

Importing files that contain static queries makes it possible to build all kinds of neat tools, including support for persisted queries. We’ve actually built support for persisted queries on top of this improved Webpack loader, and we’ll have a blog post explaining how to use them soon!

Written by

Dhaivat Pandya

Dhaivat Pandya

Read more by Dhaivat Pandya