Integrating GraphQL Codegen + React Query + Clerk JWT Tokens
Published: 2024-04-03 02:22:34

## Use Case

So, you are using [GraphQL Codegen](https://the-guild.dev/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](https://the-guild.dev/graphql/codegen/docs/guides/react-vue) 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](https://clerk.com/docs/quickstarts/nextjs) 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.

```graphql
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.

```ts
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. 

```ts
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.

```ts
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](https://clerk.com/docs/quickstarts/nextjs). You will have all the code in the Repo so don't worry.

And also add the QueryClientProvider from React Query

```ts
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

```tsx
"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!


![Sending the JWT Token to the backend](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzbqup01d3or9s7dogei.png)


![GraphQL Query](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mjk535f7n274qztotdru.png)

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](https://github.com/robertoyamanaka/codegen-jwt).

See you next time!