Persisted queries are a powerful feature in GraphQL that can help improve performance and security by predefining and storing queries on the server. This approach can reduce the payload size, minimize the risk of malicious queries, and optimize the execution of frequently used queries.
What are Persisted Queries?
Persisted queries involve storing GraphQL queries on the server and referencing them by a unique identifier (ID) when making a request. Instead of sending the entire query string with each request, the client sends the ID of the persisted query, and the server retrieves and executes the corresponding query.
Benefits of Persisted Queries
- Reduced Payload Size: By sending only the query ID, the payload size is significantly reduced, which can improve network performance.
- Enhanced Security: Since the queries are predefined and stored on the server, it reduces the risk of executing malicious or overly complex queries.
- Improved Performance: Frequently used queries can be optimized and cached on the server, leading to faster execution times.
Setting Up Persisted Queries
Step 1: Define and Store Queries
First, define the queries you want to persist and store them on the server. This can be done using a simple key-value store where the key is the query ID and the value is the query string.
const persistedQueries = { "getUser": ` query getUser($id: ID!) { user(id: $id) { id name email } } `, "getPosts": ` query getPosts { posts { id title content } } ` };
Step 2: Implement a Middleware to Handle Persisted Queries
Create a middleware function to intercept incoming requests and replace the query ID with the actual query string.
const express = require('express'); const { graphqlHTTP } = require('express-graphql'); const { buildSchema } = require('graphql'); const app = express(); app.use('/graphql', (req, res, next) => { const queryId = req.body.queryId; if (queryId && persistedQueries[queryId]) { req.body.query = persistedQueries[queryId]; } next(); }); const schema = buildSchema(` type User { id: ID! name: String! email: String! } type Post { id: ID! title: String! content: String! } type Query { user(id: ID!): User posts: [Post] } `); const root = { user: ({ id }) => { // Fetch user by ID }, posts: () => { // Fetch all posts } }; app.use('/graphql', graphqlHTTP({ schema: schema, rootValue: root, graphiql: true, })); app.listen(4000, () => console.log('Server running on http://localhost:4000/graphql'));
Step 3: Client-Side Implementation
On the client side, instead of sending the entire query string, send the query ID and any necessary variables.
const fetchPersistedQuery = async (queryId, variables) => { const response = await fetch('http://localhost:4000/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ queryId: queryId, variables: variables }) }); const data = await response.json(); return data; }; // Example usage fetchPersistedQuery('getUser', { id: '1' }).then(data => console.log(data));
Practical Exercise
Exercise: Implement Persisted Queries
- Define a new persisted query: Add a new query to the
persistedQueries
object that fetches a list of comments. - Update the middleware: Ensure the middleware can handle the new query.
- Test the new query: Use the client-side implementation to fetch the list of comments using the query ID.
Solution
- Define a new persisted query:
const persistedQueries = { "getUser": ` query getUser($id: ID!) { user(id: $id) { id name email } } `, "getPosts": ` query getPosts { posts { id title content } } `, "getComments": ` query getComments { comments { id text author { id name } } } ` };
-
Update the middleware: The middleware already handles any query ID, so no changes are needed.
-
Test the new query:
Conclusion
Persisted queries are a valuable tool for optimizing GraphQL applications. By reducing payload sizes, enhancing security, and improving performance, they can significantly improve the efficiency and reliability of your GraphQL server. In this section, you learned how to set up and implement persisted queries, both on the server and client sides. In the next module, we will explore various tools and the GraphQL ecosystem to further enhance your development experience.