5 benefits of static GraphQL queries
Sashko Stubailo
GraphQL has only been a publicly-available technology for a little over a year, so there aren’t yet clear best practices. But we’re constantly learning, and Facebook’s own use of GraphQL for over 4 years has been a treasure of experience. There’s one thing in particular that is starting to emerge as a de-facto best practice in the GraphQL community:
It’s best to write your GraphQL queries as static strings using the GraphQL query language.
This means:
- It’s best to avoid generating queries dynamically at runtime
- It’s good to use the GraphQL query language directly, rather than a language-specific syntax or query builder
You have two approaches: write the query strings inside your code, using something like a tagged template literal in JavaScript, or alongside your code in .graphql files, which is the preferred approach for native mobile clients.
The most common objection is that if you use strings for queries, you won’t get features like editor integration and code highlighting, but in fact it’s just the opposite: all of the best GraphQL tools and integrations rely on the static query language.
1. Editor tooling
Below is an animation taken from the GitHub page of Jim Kynde Meyer’s GraphQL IntelliJ plugin:
As you can see, with this plugin your query strings become much more than just strings — they give you validation errors, autocompletion, highlighting, and more. Because this tool just works with the GraphQL query language, it also works with all popular GraphQL clients, including Apollo and Relay.
2. Code generation for static typing
Internally at Facebook, GraphQL queries are used to generate native code that does the fetch and returns typed results. You can read more about this in our previous post summarizing a talk by Hyo Jeong from Facebook.
But it’s not just a Facebook thing anymore; the community is starting to catch up! For example, take Apollo iOS, a code-generation GraphQL client for Swift. This tool has some great features:
- Detects all .graphql files in the project
- Validates queries against your schema
- Rebuilds your code when you change the queries
- Gives you typed result objects based on queries and fragments
It integrates deeply with the Swift type system, making sure that fields are declared as optional when they are nullable in the GraphQL schema, for example.
And that’s just the start — people in the Apollo community are working on TypeScript and Flow code generation as well, so that you can get the same experience in your JS app! Because GraphQL is so structured, you can make sure you get only the data you need, and your tools can check it for you.
Update after publication: Check out the detailed post below about TypeScript code generation:An enhanced GraphQL developer experience with TypeScriptDevelop faster and better with the GraphQL type systemdev-blog.apollodata.com
3. Server-side logging
Have you even wondered what GraphQL operation names are for? For example, in the following query:
query HeroNameAndFriends($episode: Episode) { hero(episode: $episode) { name friends { name } } }
The HeroNameAndFriends is the operation name of the query. Fragments have names, too. In REST, we had endpoint URLs to track what data was being accessed. Operation names are the equivalent for GraphQL.
These can be extremely useful tools to use on the server side to analyze the performance and frequency of your GraphQL queries. Facebook uses them heavily to track changes in their native iOS and Android apps over time, which use their closed-source internal GraphQL clients.
Unfortunately, most open-source GraphQL clients available today, including Relay 1 and Lokka, generate dynamic queries on the fly, making the operation name and fragment names no longer meaningful (for example, a common fragment name you’ll see on your server if you’re using Relay 1 is RQL_12or similar).
That’s why Apollo Client only uses static queries with no dynamic generation at all, keeping your operation and fragment names intact. Relay 2 is being re-architected to have much more predictable queries as well.
If you want to take advantage of this on your server, check out our server-side logging product, Apollo Optics!
4. Persisted queries
Note: Since the time of writing, we launched persisted queries for Apollo Client! Read about it in the post below:Persisted GraphQL Queries with Apollo ClientIf you’re a regular at a diner, you know that saying the full name of a dish is a rookie move. If you refer to the…dev-blog.apollodata.com
In addition to queries being analyzable, trackable, and identifiable, there’s one more important benefit to static queries. It’s a concept Facebook uses heavily and has been calling persisted queries.
This question has been asked in many ways before:
“If my GraphQL server accepts and executes any query sent to it, how can I prevent malicious clients from querying too much data, or sending really expensive queries?”
Well, one answer is to introduce a complexity measurement for queries, or to limit the length of time they can spend executing. But here’s how Facebook does it:
- In development mode, the server accepts any query you throw at it.
- When you deploy your code, all of the queries are saved to a database.
- In production, the server only supports the queries that have been previously stored.
As an added bonus, this enables Facebook to save bandwidth by not sending the entire query string (the mobile news feed query was many kilobytes in size). Since all of the queries are already stored on the server, they just need to send a query ID and the pre-validated query is looked up directly.
This means you no longer have to worry about arbitrary queries hitting your server in production, and it’s very easy to identify which query is causing a performance issue since you can just look up the ID.
Currently none of the open source GraphQL clients include tools to enable this, but the next version of Apollo Client will have a tool to pull queries out of your code and save them. The ability to use persisted queries is also one of the reasons Relay 2 will have a much more static architecture, which is critical for production use at Facebook.
5. A common language for data fetching
There’s one more important reason why you should write GraphQL queries directly in your code rather than using a dynamic query builder: It’s amazing to be able to use a GraphiQL to explore the data available from your API, then paste that query into your UI code and be good to go. Having a shared language to talk about data fetching is extremely useful to be able to share tools and vocabulary across different apps.
The API developers can easily read any query across any client codebase and see what the data requirements are, and the UI developers can look into the server logs to identify why their queries are taking longer than expected. This kind of communication is facilitated by the static and language-agnostic nature of GraphQL.
After talking with Facebook, building our own tools, and writing several production apps in GraphQL, we’re pretty convinced that using static GraphQL queries in UI code is the right approach. You can expect to see our open source tools, like the Apollo JS Client, Apollo iOS, and the Apollo GraphQL server, gaining features to better enable this approach soon.
If this is something you’re interested in, let’s work together! Getting complete code generation for a variety of typed languages and building tools to collect queries from the UI and persist them to the server is going to require a lot of collaboration across the whole GraphQL ecosystem.
Today, we’ve only scratched the surface of the tooling that could be built around GraphQL clients and servers. There are so many more benefits to be gained.