Redux Toolkit and the included RTK Query are awesome. Not only are both packages incredibly well designed, easy to use and production-ready, but they also provide tons of documentation.
However, I had trouble finding code examples for certain GraphQL use-cases. In the end I pieced things together, so I decided to document my results here.
This uses @reduxjs-toolkit: 1.8.1
and @rtk-query/graphql-request-base-query: 2.2.0
. Consult the docs to see if this information is now outdated.
Using graphqlRequestBaseQuery
If you want to use GraphQL the official documentation quickly points you to a custom build graphqlBaseQuery. But RTK Query
actually provides a more sophisticated GraphQL query: graphqlRequestBaseQuery
. That one is used in some of the sandbox code examples, too.
import { createApi } from '@reduxjs/toolkit/query/react';
import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query';
export const api = createApi({
reducerPath: 'api',
baseQuery: graphqlRequestBaseQuery({
url: 'https://api.acme.com/graphql/',
}),
endpoints: () => ({})
});
If your api requires a token to access, you can pass the token via the prepareHeaders
param.
import { createApi } from '@reduxjs/toolkit/query/react';
import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query';
export const api = createApi({
reducerPath: 'api',
baseQuery: graphqlRequestBaseQuery({
url: 'https://api.acme.com/graphql/',
prepareHeaders: (headers, { getState }) => {
// Retrieve token from redux store
const token = getState().auth?.token;
if (token) {
headers.set('authorization', `Bearer ${token}`)
} else {
// use refresh token or navigate to login
}
return headers
},
}),
endpoints: () => ({})
});
I store that token in another api-splice (e.g. auth-slice
). RTK Query gives you an easy way to access other slices from with-in the prepareHeaders
function.
Dynamically changing the API url
A React Native app that I was working on required me to dynamically change between our production and staging API (for testing).
Luckily, RTK Query supports this use-case and even provides an example for a fetch
based API. The changes are trivial. It's almost plug and play to get it to work with GraphQL.
import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query';
import type { BaseQueryFn } from '@reduxjs/toolkit/query/react';
import { DocumentNode } from 'graphql';
import { ClientError } from 'graphql-request';
const dynamicGraphqlBaseQuery: BaseQueryFn<{
document: string | DocumentNode;
variables?: any;
}, unknown, unknown, Partial<Pick<ClientError, "request" | "response">>, {}>
= async (args, api, extraOptions) => {
const baseUrl = api.getState().config.env.url;
const rawBaseQuery = graphqlRequestBaseQuery<Partial<ClientError>>({
url: `${baseUrl}/graphql`,
// can also drop prepareHeaders here
});
return rawBaseQuery(args, api, extraOptions);
};
export const api = createApi({
reducerPath: 'api',
baseQuery: dynamicGraphqlBaseQuery,
endpoints: () => ({})
});
Like with our token earlier, the necessary url is stored in a config-slice
. And just like with prepareHeaders
, we can use the second parameter to access getState
(and retrieve our url from the redux state).