GraphQL, created by Facebook, refines API data fetching by enabling clients to request specific data, differing from traditional REST. Testing GraphQL involves understanding queries for data retrieval and mutations for modifications. A comprehensive testing approach covers CRUD operations and advanced features like filtering, pagination, and ordering. Various tools are available to facilitate effective GraphQL API testing.
An Introduction to GraphQL Testing
article
In the world of microservice architectures and modern web development, APIs are the key to enabling communication between different software systems. The HTTP-based REST APIs have been widespread since the early 2000s, thanks to their simplicity and ability to work well with the existing infrastructure of the Internet.
However, Facebook introduced GraphQL in 2012 to optimize its data fetching in mobile applications. The REST API often returned more data than necessary, which resulted in slower app performance and higher data usage, annoying users on slower networks and less powerful devices. The goal with this new query language was to request only the data needed, thus solving many of the performance problems with REST.
Facebook open-sourced GraphQL in 2015, allowing other developers to benefit from its capabilities. It is now maintained by the GraphQL Foundation, which claims to be “a neutral foundation founded by global technology and application development companies”, established in 2018 under the Linux Foundation. Today, GraphQL is widely adopted by companies like GitHub, Shopify, Twitter, and many others, thanks to its efficiency, precise, client-driven data queries, and developer-friendly features.
While a REST API typically has multiple endpoints, each corresponding to a different operation or resource with matching HTTP requests like GET, POST, PUT, DELETE, etc., GraphQL has a single endpoint, providing services via complex queries.
To effectively test GraphQL APIs, it’s important to understand their core concepts. A query in GraphQL is analogous to a GET request in REST. It is used to retrieve data from the server, but with some differences. For example, in the case below the client specifies exactly what data it needs and in what structure.
The following is an example of querying the list of Star Wars movies from this public, free online sandbox:
queryQuery{
allFilms{
films{
title
director
}
}
}
The result will contain a list of movies this endpoint provides, including the data the client asks for—their title and director, in this case.
{
"data":{
"allFilms":{
"films": [
{
"title":"A New Hope",
"director":"George Lucas"
},
{
"title":"The Empire Strikes Back",
"director":"Irvin Kershner"
},
{
"title":"Return of the Jedi",
"director":"Richard Marquand"
},
{
"title":"The Phantom Menace",
"director":"George Lucas"
},
{
"title":"Attack of the Clones",
"director":"George Lucas"
},
{
"title":"Revenge of the Sith",
"director":"George Lucas"
}
]
}
}
}
Mutations are used for operations modifying data, such as creating, updating, or deleting records. Mutations are the equivalent of POST, PUT, PATCH, and DELETE operations in REST APIs. They are structured similarly to queries but indicate that a change will occur on the server.
Consider the below example, keeping in mind the online tools are usually not configured to allow modification, so this one will also throw a “Schema is not configured for mutations” error.
mutationchangeTitle{
updateAllFilms(
id:"ZmlsbXM6MQ=="
input:{
title:"Die Hard"
}
)
{
id
title
director
}
}
}
The keyword mutation shows that a data change is expected. The name changeTitle of the mutation is optional—it is only required for the testing tools to identify the query. The id specifies the data object the client wants to change, and the input provides the new parameter values. The last part of the mutation lists the data fields the client would like to see in the response, as a confirmation of the data change.
Having the basic queries and mutations, how should one start testing any given GraphQL API? Here’s one possible approach: getting your hands on the schema describing the structure of the API, you can start with the CRUD operations.
- Create: A mutation allowing the client to specify exactly what data to create and what data to return after the operation is completed. The response eliminates the need for a separate query to verify the result of the creation.
- Read: A query analogous to GET requests in RESTful APIs. The client can specify the exact fields of data needed, avoiding the common problems of over-fetching or under-fetching.
- Read many: Fetching multiple records is also handled through queries. Unlike REST, where multiple GET requests may be needed to retrieve various resources or lists, GraphQL allows clients to request many records in a single query.
- Update: Modifying objects is also done using mutations. GraphQL allows the client to specify which fields to update and what data to return after the update is performed. This operation is analogous to PUT or PATCH requests in REST, but with the added flexibility of determining the response data.
- Delete: The inverse operation of the create mutation. The client can specify what resource to delete and what data to return as confirmation of the deletion.
Having a set of simple CRUD operations, you can also test the built-in support for some advanced features, ensuring precise data retrieval in case of large data sets.
- Filtering in GraphQL allows clients to retrieve data that meets specific criteria, much like adding parameters to a SQL query or URL query string in REST APIs. Clients can request only the records that match certain conditions, such as retrieving users over a certain age, products within a price range, or posts created after a particular date. Filters can handle both simple conditions (like equality) and more complex operators (like `AND`, `OR`, and ranges).
- GraphQL also supports pagination, which allows clients to retrieve data in smaller chunks, known as pages. This is particularly useful in applications where users need to browse through large lists. GraphQL supports different pagination styles, but the most common are cursor-based and offset-based.
- Ordering allows clients to request data sorted in a specific order, such as by date, name, or price. This feature is crucial for applications where data needs to be presented in a particular sequence, like sorting products by price in an online store, or displaying posts by their creation date in a blog.
Having an approach to testing GraphQL APIs, you can choose from several tools available, offering a wide range of features, from interactive query builders to automated testing suites, that can be integrated into your development and CI/CD workflows. A non-exhaustive list of these tools include:
- GraphiQL: an in-browser IDE developed by the GraphQL Foundation.
- Apollo Studio: a cloud-based platform for working with GraphQL APIs, part of the Apollo ecosystem.
- Postman: traditionally known for REST API testing, also includes support for GraphQL, providing a familiar interface for those already accustomed to it.
- Insomnia: another popular API testing tool, with a user-friendly interface.
- Jest with Supertest or Apollo Client: being a widely used JavaScript testing framework, and by pairing it with these tools, you can automate your GraphQL tests as part of your CI/CD pipeline.
- Ariadne is a Python library designed to simplify the development of GraphQL APIs by providing a schema-first approach. With Ariadne, developers define the GraphQL schema using SDL (Schema Definition Language) and then map resolvers to each part of the schema, ensuring a clear separation between the schema and the underlying Python logic.
Having an approach like this, you can thoroughly test every aspect of your GraphQL API to ensure it performs well, behaves as expected, and remains secure. Performance testing, particularly for large datasets, and security testing to protect against vulnerabilities like over-fetching and injection attacks, are vital for the long-term success of any GraphQL API.
Lets Hang!