How to Add Custom GraphQL Query to Strapi V4
Have you ever had difficulties with setting up a new query, defining new types, or re-using already existing types from other content-types in Strapi V4? Did you feel confused about the available customizations when using GraphQL? Let us help you and show how we solved this in one of our projects!
While Strapi's own documentation is good for adding GraphQL support, it also confusing what customizations are available when somebody wants to use GraphQL. As our project is using Strapi v4, we had to learn how to create such queries, compared to v3.
The first example under the official GraphQL documentation's Customization section is a showcase of the capabilities without providing much context. That example - next to some ShadowCRUD settings - creates two object types (Book and Article), a custom Nexus plugin, overrides a query resolver for an existing content type (address) and disables authentication for the same content type (Query.address).
For me, it was hard to see how to set up a new query, define new types and how to re-use already existing types from other content-types. As our project is using Strapi v4, we had to learn how to create such queries, compared to v3.
Eventually, we could explore the documentation, the Strapi codebase and the examples of official plugins (like users and permissions plugin) and conclude a way to effectively add GraphQL customizations.
This post is showing the results of our findings and aims to help the developers who need to achieve similar goals with Strapi v4.
The domain for the example
Let's assume that we are developing an e-commerce site where we sell products. One day we would like to add a feature to track popularity of each product using a third party solution. To keep the API keys secret at the server side, we are planning to define a new GraphQL query to the existing GraphQL schema provided by Strapi.
This query will handle the integration to the 3rd party API and optionally return the product data if requested.
Initialize the project
For the sake of simplicity, we are setting up a new Strapi project for this example using the official guide, thus using npx from command line:
After creating the initial administration account, we can use another terminal window to proceed.
Define the Product content type
In order to add a
Product content type with an API and a single attribute called
name, you can use the command line interface or the started admin web interface.
I will use the CLI so I can include its output.
Add GraphQL plugin
The Strapi quick start project does not contain the GraphQL plugin, so we have to install it. You can follow the plugin's documentation, however, it is just as easy that running this command:
When your Strapi application restarts, you can access the GraphQL playground at http://localhost:1337/graphql
Let's check the Product query which was created for the content type with the following query:
For the first try it fails because there is no permission set up for unauthenticated users.
After adding the
findOne permissions of
Product content type to the
Public role in the admin
Users & Permissions Plugin /
Public location in the admin web interface, the above query should return empty list.
If you would like to see some results, then add a new product manually or (after providing the
create permission for the
Public role) with this GraphQL mutation:
Prepare to add custom GraphQL query
We can add customizations to the GraphQL plugin in Strapi's
register lifecycle hook, which is in
In a reasonably large project this file can contain lots of code, so to make it obvious where GraphQL related code is defined, we will create another file at
./src/graphql.js. This will be responsible to collect the custom GraphQL queries and mutations and add them to Strapi.
After our changes the files look like this.
Later we will replace the noop extension with the
popularity extension, which will also be similar to
noop, ie: it will be a function taking
strapi as parameter, returns another function with a parameter which has a
nexus field and returns the extension definition.
This complicated function is helping in the long run, as
strapi is provided by our code, but
nexus is provided by the GraphQL plugin's
extension service. Both can be useful in our extension code to reach other Strapi services, plugins and use nexus api.
Use Nexus API
It is time to define our
popularity extension. I will copy the
noop extension into the
.src/api/popularity/graphql.js file and include this non-working definition to the extensions.
As you can see I have chosen to import the new module and add it to the extensions list. The drawback is that whenever we add another GraphQL query, we have to manually add it to the extension list, otherwise it won't be recognized.
Let's continue with the actual extension, fill in the
popularity GraphQL definition. Our first approach is to use nexus api.
I have included a mocked response for the 3rd party call, and let the reader elaborate on how to continue the implementation regarding the integration with any 3rd parties or custom logic.
Now you can check the results in the GraphQL Playground using the below query.
This gives the result:
So our implementation works as we expect.
For us the hardest problem was to link the custom response type (
PopularityResponse) and the Product content type's resolvers. The final trick was to provide a resolver for the immediate relations (
product field in the example), and return the result in a format what Strapi's graphql plugin accepts (an object with
value field). This information came from the graphql plugin source code, which implies that using this trick is considered a dependency on graphql plugin internals and can break during upgrades.
Use GraphQL Schema
Since Strapi v4.0.8, the
ProductResponseEntity can be referenced from GraphQL SDL type definitions in Strapi. If you are interested, here is the same definition, but using GraphQL schema and avoiding nexus API.
Personally, I prefer the GraphQL SDL variant as it is more compact, and it is familiar to most developers. In case we make a mistake in the SDL definition, it is failing at starting up Strapi, which is usually the same when we use Nexus API.
For both cases the
resolversConfig can add configuration for authorization, middlewares and policies. While it is possible to override existing queries and mutation's
resolversConfig, you should keep in mind that the active
resolversConfig would be result of merging Strapi's default resolvers configuration and your settings. This makes the resolvers' configuration overrides dependent on the original settings and the merging process (which both can change with Strapi upgrades).
As we can see, it is not that hard to add customized queries to Strapi. However, there are major differences between Strapi v3 and v4 regarding GraphQL.
I hope the above example saves you the time we have spent to find out the ways to implement GraphQL queries in Strapi v4.