August 27, 2016

All you need to know about GraphQL.js 0.7

Jonas Helfer

Jonas Helfer

Yesterday, version 0.7 of GraphQL.js was released, and it contains a lot of exciting improvements, including new features for the schema language, an update to default resolve functions, and utilities for constructing and handling GraphQL documents. In this post I’ll cover:

  • New features — how, when and why to use them
  • Breaking changes — how and if they impact you
  • Fixes — what was fixed and why

Let’s start with the most exciting of the bunch!

New — Schema language improvements: descriptions + more

Adding descriptions to the schema language was one of the most requested features in GraphQL.js since support for the schema language itself was added in 0.5.0, and finally it’s out!

With 0.7, you can add descriptions directly as comments in the GraphQL schema language. Any comment block that comes before a field, type or directive definition will be used as a description. Here’s an example:

# A small domesticated carnivorous mammal with soft fur
# See [Wikipedia entry for cat](https://en.wikipedia.org/wiki/Cat)
type Cat extends Mammal {
  name: String
  meows: Boolean
  purrs: Boolean
  scratches: Boolean
}

Markdown syntax in descriptions is explicitly supported in the GraphQL spec, so tools like GraphiQL are built to work with it!

While the GraphQL schema language itself isn’t part of the of the official spec yet, it’s proving to be so useful that the plan is to include it there soon. There are already many libraries, such as graphql-tools, that use it for things like mocking and generating schemas.

Version 0.7 added other features to make using the schema language even more convenient:

  • Schemas returned by buildASTSchema are now executable. (Caveat: unions and interfaces are not supported out of the box, nor is parsing and serialization of scalar types, but GraphQL.js provides a useful error message in those cases.)
  • The new buildSchema utility function makes creating an executable schema object from the schema language a tiny bit more convenient.

Taken together, all these changes make a pretty compelling case for using the GraphQL schema language.

New — Convention for naming of root types

Until 0.7, there was no official recommendation for what to name your root types, so people chose different alternatives: Query, RootQuery, QueryRootType, etc. GraphQL.js 0.7 is more opinionated here; it prefers: QueryMutation and Subscription. This is not a breaking change, so you can still continue to name your root types differently, but if you use the Query, Mutation and Subscription, you will no longer need to specify the names of the root types when creating a shorthand schema:

type Query {
  aString: String
}# The following is now unnecessary if your root type is called Query
schema {
  query: Query
}

Even though it’s not required, I strongly recommend using the standard names for Query, Mutation and Subscription root types from now on.

Breaking — Default resolvers now get args and context

Until 0.7.0, the default resolve function in GraphQL did not pass along arguments and context, which meant that for all but the most trivial use-cases you had to define a resolve function.

With 0.7, you can now define more complex behavior directly on your JavaScript objects and execute GraphQL queries directly on a schema without having to define resolvers. For example you could do this:

const query = `query {
  addOrSubtractTwo(num: 10)
}`;const schema = buildSchema(`
  type Query {
    addOrSubtractTwo: Int
  }
`);const rootObject = {
  addOrSubtractTwo({ num }, context){
    return num + 2 * context.polarity;
  }
};const result = graphql(
  schema,
  query,
  rootObject,
  { polarity: -1 },
);// result is now { data: { addOrSubtractTwo: 8 } }

This is a breaking change because until now the default resolve function would not pass through arguments and context. However, unless you specifically programmed your function to behave differently when it gets arguments vs when it gets no arguments, you should be fine.

Breaking — Better GraphQLErrors

In 0.7 there are improvements to GraphQLError that get rid of unexpected behavior arising due to the special treatment of Error on many platforms. This is, strictly speaking, an improvement. The breaking change won’t affect you unless you called the GraphQLError constructor explicitly in your code, in which case you should check the diff and adjust the argument order to make sure your code still works. You may also have to update some of your test code if you relied on e.stack, because GraphQLError now provides stack traces in some cases where it didn’t before, and it no longer writes the message to the stack property when stack is missing.

A change that was already made in 0.6, but that has gotten relatively little notice so far, is that GraphQLError now includes a path property, which lets you correlate each error with the corresponding field in the response. That’s especially useful if you want your client to provide useful error messages to the user, or adjust its caching behavior based on whether a field errored or not. However, the default formatError function still filters it out, so if you want to make use of path on the client, you have to override the default function by passing the right formatError option to express-graphql or apollo-server:

formatError: e => ({ 
  message: e.message,
  locations: e.locations,
  path: e.path
}),

Fixes — Directives in extendSchema and error.message

Version 0.7 also has a fix for an issue with errors, and adds functionality that was missing in extendSchema:

  • GraphQLError no longer write messages to the stack property of the error, which preserves the original stack and improves compatibility with tools that expect the stack to have a specific format.
  • The extendSchema function now supports extending a schema with new directives.

New utility — separateOperations

Last but not least, the new separateOperations utility function gives us another glimpse of what Facebook’s internal GraphQL setup looks like.

separateOperations takes in a GraphQL document with (potentially) many operation definitions and fragments, and returns an array of documents, which each contain one operation definition and all the fragments used in that operation. Operations are things like queries, mutations and subscriptions.

How and why does Facebook use separateOperations? In their own words:

A typical task using GraphQL at Facebook looks something like:

1. Load and parse all .graphql files which may contain operations or fragments.

2. Use concatAST to produce one AST that contains all operations and fragments.

3. Separate this all-encompasing AST into individual ASTs that represent each operation which could be sent to the server in isolation.

separateOperations is for the third step, and it lets Facebook cache the many operations separately, instead of just caching one huge document that contains all of them.

Conclusion

This concludes our not-so-little tour of what’s changed in GraphQL.js 0.7. If you want to know the details, you can always check the excellent release notes. A huge thanks to Lee ByronKevin LackerRobert Zhu and Rylan Hawkins who all worked on this great release!

Personally, I’m really excited about all these changes and new features. The pace of development in the GraphQL ecosystem is accelerating really significantly. For an open-source project that’s barely a year old, GraphQL already has an incredible amount of traction and a remarkably vibrant community, and it’s only getting better!

Written by

Jonas Helfer

Jonas Helfer

Read more by Jonas Helfer