Use GraphQL without writing GraphQL
Tags: graphql, react, webdev, typescript
Published: 2021-12-19 12:49:45

👍 Follow me on Twitter [@andycoupedev](https://twitter.com/andycoupedev)

In this walkthrough, we are going to create a full stack application with full type safety using GraphQL without writing any actual GraphQL with the star of the show being GenQL. Below is a list of tools we will be using.

- [TypeScript](https://www.typescriptlang.org/) - typed JavaScript from the future.
- [Hasura](hasura.io) - instant GraphQL and REST APIs on new or existing data sources.
- [React Query](https://react-query.tanstack.com/overview) - manage fetching, caching and server state easily.
- [GenQL](https://genql.vercel.app/) - Generate a type safe GraphQL client for our GraphQL API.
- [NextJS](https://nextjs.org/) - Arguably the best React framework.


## Create our frontend
To generate our frontend let's create our NextJS TypeScript project with the following command from a directory of your choice.

`npx create-next-app@latest your-app-name --ts`

## Create our GraphQL API
For our GraphQL API, let's head over to [Hasura](hasura.io) and create a project - you'll need to create an account. Once you've done that, select the create project option and select all of the free tier options. Click "Launch Console" and you should be presented with the Hasura console. 

We have rapidly generated the frontend and API layers of our application, leaving just the DB layer left. Thankfully, Hasura has our back. 

Click on the "Data" tab from the Hasura console and you should see a button to connect a database. From here, there should be a "Create Heroku database" option. Follow these steps (you may have to sign up to Heroku if you're not already signed up) and we'll have a Postgres database managed by Heroku, connected to our GraphQL API.

![app diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rskzyyop9660a6y1b8an.png)

## Create our database

Now, let's create a table. For this application I'm going with a football (soccer) theme so let's name our table `teams`.

![creating a table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xtfosbwybp77b4l6lixm.png)

The frequently used columns button is useful and lets us quickly add columns `id`, `created_at` and `updated_at`. Add a column of type `Text` named `name` to store our team names as well.
 
![adding a new table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7d1i01canyokv05l5i5r.png)

Click "Add table" to create the table.

After creating the table, the insert row tab will allow us to manually create a row in the table, let's do that and hit "Save".

![Creating a row](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r86muczguhmtz0r4lz4g.png)

 
Head over to the "API" tab and you will now be able to query the data from our database using Hasura's playground 😎.


![querying data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k38tityinso6vv239m46.png)

## Back to the frontend

We have our backend setup. To interact with our GraphQL API from our frontend we are going to generate a GraphQL client using [GenQL](https://genql.vercel.app/) so we need to install some dependencies in our NextJS application.

```
npm i -D @genql/cli # cli to generate the client code
npm i @genql/runtime graphql # runtime dependencies
```

@genql/cli is a dev dependency because it is only required to generate the client, @genql/runtime instead is a direct dependency of the generated code.

To generate our client we can use the following command.

```
 genql --endpoint <your graphql endpoint from hasura console> --output ./genql-generated -H 'x-hasura-admin-secret: <your admin secret from hasura console>'
```
The generated files expose a function `createclient`. This creates a client you can use to send requests.

Let's create a file at the root of our project named `genql-client.ts` with the following contents to create our client.

```javascript
import { createClient } from "./genql-generated";

const client = createClient({
    url: <your graphql endpoint from the hasura console>,
    headers: {
        'x-hasura-admin-secret': <your hasura admin secret from hasura console>,
    },
})
```

With our GraphQL client in our holster, we're ready to start firing requests. 

We are going to use React Query to manage fetching and server state.

```
npm i react-query
```

For the purpose of this walkthrough we will just make the request in the default index page provided by NextJS. So head to `pages/index.tsx` and import our client below the rest of the existing imports. 

I like to work inside the `src` directory so your imports may be a level higher than mine. NextJS supports moving the `pages` directory into a `src` directory out of the box.

```javascript
// ...existing imports
import { client } from '../../genql-client'
```

Let's create a function to fetch the teams in our database. Don't just copy and past the code below. Type it out and appreciate the autocompletion using CMD or CTRL + SPACE depending on your OS 😎

```javascript
const fetchTeams = () => {
  return client.query({
    teams: [{}, { id: true, name: true, created_at: true }],
  });
};
```
Consult the GenQL docs on the syntax but you can get the general idea of how to build a query. Once again, autocompletion will guide you like a good friend. 

Our generated files also export an object called `everything` which allows us to query all fields in a type instead of providing a boolean to every type, like so.

```javascript
// ...existing imports
import { everything } from "../../genql-generated";

const fetchTeams = () => {
  return client.query({ teams: [{}, everything] });
};
```
Now let's bring in `useQuery` from React Query and wire it up to our fetchTeams function.

```javascript
// ...existing imports
import { client } from "../../genql-client";
import { everything } from "../../genql-generated";
import { useQuery } from "react-query";
```
Invoke the `useQuery` hook inside the `Home` page component and provide it with your query key and query function as the second and third arguments respectively.

```javascript
  const { data } = useQuery("teams", fetchTeams);
```
Almost there! We need to wrap our app in a `<QueryClientProvider />` component provided to us by React Query. This will have to be added further up the tree in the `_app.tsx` file. Update `_app.tsx` with the following code.

```javascript
import type { AppProps } from "next/app";
import { QueryClientProvider, QueryClient } from "react-query";

const queryClient = new QueryClient();

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <QueryClientProvider client={queryClient}>
      <Component {...pageProps} />
    </QueryClientProvider>
  );
}

export default MyApp;
```
Let's update our `index.tsx` page to look like the following and we should be seeing our team rendering on the page.

```javascript
import type { NextPage } from "next";
import styles from "../styles/Home.module.css";

import { useQuery } from "react-query";
import { client } from "../../genql-client";
import { everything } from "../../genql-generated";

const fetchTeams = () => {
  return client.query({ teams: [{}, everything] });
};

const Home: NextPage = () => {
  const { data, isLoading } = useQuery("teams", fetchTeams);

  return (
    <div className={styles.container}>
      <h1>Teams</h1>
      {isLoading && <p>Loading...</p>}
      {data && data.teams.map((team) => <p key={team.id}>{team.name}</p>)}
    </div>
  );
};

export default Home;
```

There are certain best practices to follow when using React Query with SSR/NextJS that are beyond the scope of this walkthrough which can be found [here](https://react-query.tanstack.com/guides/ssr).

I may do a follow up post using mutations and GenQL to create a CRUD application but hopefully this has shown you the power of GenQL