Integrating GraphQL Codegen + React Query + Clerk JWT Tokens
Roberto Yamanaka
Posted on April 3, 2024
Use Case
So, you are using GraphQL Codegen to automatically generate your GraphQL schema (and types) based on your GraphQL schema in the backend.
Using Codegen gives you a better experience, since it automatically generates you the types and custom code/hooks you want. You can even automatically generate your React Query Hooks to fetch the GraphQL data. Life is great.
However... there is one issue. How do these automatically generated hooks and functions handle the authentication?
Let's dive into how we can achieve this: authentication + generated code
Basic setup
We'll start by creating a new NextJS project (vanilla React works just fine).
I'll name the project codegen-jwt
and use the default configuration from Next
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias (@/*)? No / Yes
What import alias would you like configured? @/*
Next, lets install GraphQL, Codegen with some useful plugins and Tanstack React Query to test our generated query hooks.
npm i graphql axios @tanstack/react-query
npm i -D typescript ts-node @graphql-codegen/cli @graphql-codegen/client-preset @parcel/watcher @graphql-codegen/typescript-react-query
Now lets add our Auth provider Clerk. You can follow these instructions for a quick setup.
npm install @clerk/nextjs
Prepare the GraphQL Queries and source
For this tutorial we will use a public GraphQL schema for our Codegen
https://graphql.anilist.co
Now let's create some GraphQL queries that Codegen will take to generate the types for us.
Create a new file called codegen/queries.graphql
and add this query.
query findAnimeById($id: Int) {
Media (id: $id, type: ANIME) {
id
title {
romaji
english
native
}
}
}
Codegen will use this to generate the types and the react-query hooks.
Building our Code Generator
Ok! Now that we have the setup ready. Let's write our codegen/codegen.ts
file. This serves as the Instructions for Codegen.
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
overwrite: true,
schema: "https://graphql.anilist.co",
documents: 'codegen/**/*.graphql',
generates: {
"graphql/__generated__/graphql.ts": {
plugins: [
"typescript",
"typescript-operations",
"typescript-react-query",
],
config: {
reactQueryVersion: "auto",
exposeQueryKeys: true,
exposeFetcher: true,
fetcher: {
func: "@/codegen/fetcher#useFetchGraphQLData",
isReactHook: true,
},
},
},
},
};
export default config;
The most important part of this is the fetcher config. This is where we define all the configuration for the fetcher Codegen will use.
Our fetcher.tsx
has a custom hook that will handle the calls to the backend. Here we will pass our Clerk JWT Token to the Headers to authenticate using the useAuth
hook.
If your auth provider doesn't use hooks for obtaining the JWT Token, you can create a simple Ts function to obtain it and then add it to your headers. Just set isReactHook: false
Here is that codegen/fetcher.tsx
file.
import { useAuth } from "@clerk/nextjs";
import axios, { AxiosError, isAxiosError } from "axios";
interface GraphQLResponse<T> {
data: T;
errors?: { message: string }[];
}
export const useFetchGraphQLData = <TData, TVariables>(
query: string
): ((variables?: TVariables) => Promise<TData>) => {
const { getToken } = useAuth();
const url =
"https://dummyjson.com"; // Replace this with your GraphQL API URL
return async (variables?: TVariables) => {
const token = await getToken();
try {
const response = await axios.post<GraphQLResponse<TData>>(
url,
{
query,
variables,
},
{
headers: {
"Content-Type": "application/json",
Authorization: token ? `Bearer ${token}` : "",
},
}
);
if (response.data.errors) {
throw new Error(response.data.errors[0].message);
}
return response.data.data;
} catch (error) {
if (isAxiosError(error)) {
const serverError = error as AxiosError<GraphQLResponse<unknown>>;
if (serverError && serverError.response) {
const errorMessage =
serverError.response.data.errors?.[0]?.message ||
"Error in GraphQL request";
throw new Error(errorMessage);
}
}
throw error;
}
};
};
And just like that you can add JWT tokens to your graphQL queries!
Running Codegen
Now that we have everything in place, lets generate the codegen with this command.
npx graphql-codegen --config codegen/codegen.ts
If we go to the generated file graphql/__generated__/graphql.ts
we can see that it automatically created the React Query Hook.
export const useFindAnimeByIdQuery = <
TData = FindAnimeByIdQuery,
TError = unknown
>(
variables?: FindAnimeByIdQueryVariables,
options?: Omit<UseQueryOptions<FindAnimeByIdQuery, TError, TData>, 'queryKey'> & { queryKey?: UseQueryOptions<FindAnimeByIdQuery, TError, TData>['queryKey'] }
) => {
return useQuery<FindAnimeByIdQuery, TError, TData>(
{
queryKey: variables === undefined ? ['findAnimeById'] : ['findAnimeById', variables],
queryFn: useFetchGraphQLData<FindAnimeByIdQuery, FindAnimeByIdQueryVariables>(FindAnimeByIdDocument).bind(null, variables),
...options
}
)};
Testing it out
Let's do a quick setup for Clerk following the Quickstart Guide. You will have all the code in the Repo so don't worry.
And also add the QueryClientProvider from React Query
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const queryClient = new QueryClient()
return (
<ClerkProvider>
<QueryClientProvider client={queryClient}>
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
</QueryClientProvider>
</ClerkProvider>
);
}
Now we can create a dummy page app/test-codegen/page.tsx
to test our generated hook
"use client"
import { useFindAnimeByIdQuery } from "@/graphql/__generated__/graphql";
export default function TestCodegen() {
const { data, isLoading } = useFindAnimeByIdQuery({ id: 15125 });
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div className="h-screen flex items-center justify-center">
<p>Check your network</p>
</div>
);
}
Once we load the page we get our JWT Token sent as the Bearer token!
There we have it! Now you can send your JWT Token or customize your generated requests to your GraphQL API URL.
Here is the link to the repo.
See you next time!
Posted on April 3, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.