How to integrate Next.js with Fauna using React Query
Tags: fauna, nextjs, javascript, react
Published: 2021-02-08 03:37:29

## **Introduction**

In this tutorial, we'll learn about how we can integrate[ Next.js](http://nextjs.org/) with[ Fauna](https://fauna.com/). We'll also learn about how we can fetch GraphQL data on the server using[ React Query](https://react-query.tanstack.com/). We'll be building a server-side rendered application which will look similar to the following:

![image3](https://dev-to-uploads.s3.amazonaws.com/i/93978ddg9r7hp9btgg1h.png) 
 
In this application, we’ll be showing a list of customers, products and orders. The data for these will be fetched from Fauna and rendered on the server side. This application won’t make any client side requests to Fauna even during routing.

An online demo of the application that we built is hosted on[ Vercel](https://nextjs-react-query-faunadb.vercel.app/) and the code is available on[ GitHub](https://github.com/ghoshnirmalya/nextjs-react-query-faunadb).

## **Introduction**

Fauna is a flexible, developer-friendly, secure database which delivers a web-native API. It has a lot of[ features](https://fauna.com/features) and is very easy to get up and running.

Next.js is one of the most popular[ React](https://reactjs.org/) frameworks which has a lot of features like[ file-system routing](https://nextjs.org/docs/routing/introduction),[ built-in CSS support](https://nextjs.org/docs/basic-features/built-in-css-support),[ API routes](https://nextjs.org/docs/api-routes/introduction),[ fast refresh](https://nextjs.org/docs/basic-features/fast-refresh), etc. I've been building applications using Next.js and it has a good developer experience.

## **Softwares required for running the application**

1. [npm](https://www.npmjs.com/)
2. [git](https://git-scm.com/)

## **Technologies used in the application**

1. [Fauna](https://fauna.com/)
2. [Next.js](https://nextjs.org/)
3. [Chakra UI](http://chakra-ui.com/)
4. [React Query](https://react-query.tanstack.com/)

## **Creating a new Next.js application**

Let’s start by creating a new Next.js application. We can create a new Next.js application by running the following command from our terminal:

```
yarn create next-app
```

We'll have to enter the name of our application when the command prompts for it. We can name it anything we want. However, in this case, we'll name it **nextjs-faunadb**. The above command will create a new Next.js application for us with the following structure:

```
.
├── README.md
├── package.json
├── pages
│   ├── _app.js
│   ├── api
│   └── index.js
├── public
│   ├── favicon.ico
│   └── vercel.svg
├── styles
│   ├── Home.module.css
│   └── globals.css
└── yarn.lock
```

We can now go inside the **nextjs-faunadb** directory and start our Next.js application’s development server using the following command:

```
cd nextjs-faunadb && yarn dev
```

Our Next.js application should be up and running on[ http://localhost:3000](http://localhost:3000) where we should be able to see the following screen:

![image20](https://dev-to-uploads.s3.amazonaws.com/i/pn4rxpggt53j4399dgmg.png) 
 
## **Adding Chakra UI**

[Chakra UI](https://chakra-ui.com/) is a popular React component library. Personally, I like using it on most of my applications as it's very flexible and easy to understand.

We'll be using Chakra UI for styling the user interface of our application. We can install that package by running the following command from the root of our application:

```
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
```

We'll be using[ TypeScript](https://www.typescriptlang.org/) for our application as well. So, let's rename the **_app.js** file inside the **pages** directory to **_app.tsx** with the following content:

```
// pages/_app.tsx

import {
  Box,
  ChakraProvider,
  Container,
  HStack,
  Link as ChakraLink,
} from "@chakra-ui/react";
import type { AppProps } from "next/app";
import Link from "next/link";
import React from "react";

const App = ({ Component, pageProps }: AppProps) => {
  return (
    <ChakraProvider>
      <Box bg="gray.100" h="100vh" w="100vw">
        <Box borderWidth={1} rounded="md" bg="white" p={6}>
          <Container maxW="4xl">
            <HStack spacing={16}>
              <Link href="/">
                <ChakraLink>Customers</ChakraLink>
              </Link>
              <Link href="/products">
                <ChakraLink>Products</ChakraLink>
              </Link>
              <Link href="/orders">
                <ChakraLink>Orders</ChakraLink>
              </Link>
            </HStack>
          </Container>
        </Box>
        <Container maxW="4xl" centerContent>
          <Component {...pageProps} />
        </Container>
      </Box>
    </ChakraProvider>
  );
};

export default App;
```

Since we're using TypeScript, we'll need to restart our Next.js server. Once we restart our server, we'll get the following error:

```
$ yarn dev

yarn run v1.22.5
$ next dev
ready - started server on <http://localhost:3000>
It looks like you're trying to use TypeScript but do not have the required package(s) installed.

Please install typescript, @types/react, and @types/node by running:

	yarn add --dev typescript @types/react @types/node

If you are not trying to use TypeScript, please remove the tsconfig.json file from your package root (and any TypeScript files in your pages directory).
```

This is because we added TypeScript to our application but didn't add the necessary dependencies. We can fix that by installing the missing dependencies. From the root of our application, we can execute the following command to install the missing dependencies:

```
yarn add --dev typescript @types/react @types/node
```

Now, if we start our Next.js server, our application should compile fine:

```
$ yarn dev

yarn run v1.22.5
$ next dev
ready - started server on <http://localhost:3000>
We detected TypeScript in your project and created a tsconfig.json file for you.

event - compiled successfully
```

## **Adding React Query**

[React Query](https://react-query.tanstack.com/) is a data-fetching library for React. It helps in fetching, caching, synchronizing and updating server state in React applications. We will use React Query to fetch data in our application. We can install it by running the following command from the root of our application:

```
yarn add react-query
```

Next, we'll need to modify our **pages/_app.tsx** file with the following content:

```
// pages/_app.tsx

....

import { QueryClient, QueryClientProvider } from "react-query";
import { Hydrate } from "react-query/hydration";

const queryClient = new QueryClient();

const App = ({ Component, pageProps }: AppProps) => {
  return (
    <ChakraProvider>
      <QueryClientProvider client={queryClient}>
        <Hydrate state={pageProps.dehydratedState}>
          <Box bg="gray.100" h="100vh" w="100vw">

              ....

          </Box>
        </Hydrate>
      </QueryClientProvider>
    </ChakraProvider>
  );
};

export default App;
```

## **Fetching GraphQL data from Fauna using React Query on the server side**

In this step, we'll connect our Next.js application with Fauna and fetch data using the[ graphql-request](https://github.com/prisma-labs/graphql-request) plugin. Let's start by adding the plugin to our application. We can do that by running the following command from the root of our application:

```
yarn add graphql-request graphql
```

Let's also create a new file **graphql-client.ts** inside the **lib** directory with the following content:

```
// lib/graphql-client.ts

import { GraphQLClient } from "graphql-request";

const endpoint = process.env.FAUNA_GRAPHQL_ENDPOINT;

const graphQLClient = new GraphQLClient(endpoint, {
  headers: {
    authorization: `Basic ${process.env.FAUNA_AUTH_HEADER}`,
  },
});

export default graphQLClient;
```

This file will be responsible for handling the[ Fauna authentication](https://docs.fauna.com/fauna/current/api/graphql/endpoints#authentication) using Basic token for our GraphQL client.

Next, let's create a new Fauna database.

You need to visit[ http://fauna.com](http://fauna.com/) and log into your account. If you don't have an account, you'll need to create a new account. Once you log into your account, you'll be redirected to the Fauna dashboard.

1. Click on the New Database button from the Fauna dashboard.

![image10](https://dev-to-uploads.s3.amazonaws.com/i/nbsoh3klzh0hmkrb6lqk.png) 
 
2. Create a new database by entering the name of your database. Check the Pre-populate with demo data checkbox so that there is some dummy data in the database and click on the Save button.

![image12](https://dev-to-uploads.s3.amazonaws.com/i/uuxw8yc56xmsriubh6eb.png) 

The database will be created and we should be able to see a similar screen:

![image21](https://dev-to-uploads.s3.amazonaws.com/i/q91lsznnzgqc45jjrg4w.png) 

If we click on the **customers** collection, we can see the pre-populated data. The pre-populated data will help us in getting started with Fauna very easily.

![image9](https://dev-to-uploads.s3.amazonaws.com/i/zf4qgzk51sgfoln1qfdw.png) 

Next, if we visit the GraphQL tab on the Fauna dashboard, we should be able to execute a GraphQL query or mutation.

![image8](https://dev-to-uploads.s3.amazonaws.com/i/quvehnuf6nwjx019r0wc.png)
 
The HTTP headers will already be pre-populated in the GraphQL Playground:

![image16](https://dev-to-uploads.s3.amazonaws.com/i/1sx4j6lks0kqpd7qpywb.png)
 
We can also check out the **Schema** of our database:

![image19](https://dev-to-uploads.s3.amazonaws.com/i/sdg5slu4937zxfyjwt5w.png)
 
We can also checkout the **Docs** tab to know about the available GraphQL queries and mutations:

![image6](https://dev-to-uploads.s3.amazonaws.com/i/x67n0pd517eau8vcpc8l.png)
 
We can run a simple GraphQL query to check out the playground. Let's run the following GraphQL query:

```
query {
  allCustomers {
    data {
      _id
      firstName
      lastName
      address {
        street
      }
    }
  }
}
```

If we click on the **Execute Query** button, we'll get the following output:

```
{
  "data": {
    "allCustomers": {
      "data": [
        {
          "_id": "287346643083198981",
          "firstName": "Auria",
          "lastName": "Osgardby",
          "address": {
            "street": "87856 Mendota Court"
          }
        },
        {
          "_id": "287346643084247557",
          "firstName": "Skipper",
          "lastName": "Scanes",
          "address": {
            "street": "72 Waxwing Terrace"
          }
        },
        {
          "_id": "287346643084248581",
          "firstName": "Ardith",
          "lastName": "Probert",
          "address": {
            "street": "5 Troy Trail"
          }
        }
      ]
    }
  }
}
```

Let's try to do a similar query from our Next.js application. To do that, we need to do the following:

1. Create a new file name **.env.local** in the root of our application to store all our [environment variables](https://medium.com/chingu/an-introduction-to-environment-variables-and-how-to-use-them-f602f66d15fa).
2. Copy the value of the **authorization** key in the HTTP header (without the **Basic** text) from the GraphQL playground and past it inside the **.env.local** file. It should look like the following:

```
// .env.local

FAUNA_AUTH_HEADER=Zm5BRF9OdnBFN0FDQUpxcXF2V3dkRFpQaGFjpxcXF2V3dkRFpQaRGVIbEpxcXF2V3dkRFpQasxa0Yjpu=
```

3. Copy the value of the Fauna GraphQL API's endpoint from the playground and paste it inside the **.env.local** file as well. Our .env.local file should look like the following:

```
// .env.local
// `FAUNA_AUTH_HEADER` will be different in your case.

FAUNA_AUTH_HEADER=Zm5BRF9OdnBFN0FDQUpxcXF2V3dkRFpQaGFjpxcXF2V3dkRFpQaRGVIbEpxcXF2V3dkRFpQasxa0Yjpu=
FAUNA_GRAPHQL_ENDPOINT=https://graphql.fauna.com/graphql
```

4. Create a new file named **get-all-customers.ts** inside the **lib** directory with the following content:

```
// lib/get-all-customers.ts

import { gql } from "graphql-request";
import graphQLClient from "./graphql-client";

const getAllCustomers = async () => {
  const query = gql`
    {
      allCustomers {
        data {
          _id
          firstName
          lastName
        }
      }
    }
  `;

  const response = await graphQLClient.request(query);
  const data = JSON.parse(JSON.stringify(response));

  return data.allCustomers.data;
};

export default getAllCustomers;
```

The above code will be responsible for fetching all the customer data from our Fauna database.

5. Rename our **pages/index.js** file to **index.tsx** and replace its content with the following:

```
// pages/index.tsx

import { Box, Grid, Text } from "@chakra-ui/react";
import getAllCustomers from "../lib/get-all-customers";
import { NextPage } from "next";
import React from "react";
import { QueryClient, useQuery } from "react-query";
import { dehydrate } from "react-query/hydration";

const CustomersPage: NextPage = () => {
  const { data } = useQuery("allCustomers", getAllCustomers, {
    staleTime: Infinity,
  });

  return (
    <Grid gap={4} m={4} gridTemplateColumns="1fr" w="100%">
      <Box borderWidth={1} rounded="md" bg="white">
        <Box borderBottomWidth={1} px={8} py={6} bg="gray.200">
          <Text fontWeight="bold" textTransform="uppercase">
            Customers
          </Text>
        </Box>
        <Box>
          {data.map((user) => {
            return (
              <Text key={user._id} p={8} color="gray.700" borderBottomWidth={1}>
                {user.firstName} {user.lastName}
              </Text>
            );
          })}
        </Box>
      </Box>
    </Grid>
  );
};

export const getServerSideProps = async () => {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery("allCustomers", getAllCustomers, {
    staleTime: Infinity,
  });

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
};

export default CustomersPage;
```

6. Stop our Next.js server and reload our **.env.local** file by running the following command from the root of our application:

```
source .env.local
```

7. Start our Next.js server by running the following command from the root of our application:

```
yarn dev
```

Now, if we visit[ http://localhost:3000](http://localhost:3000/), we should be able to view the following screen:

![image2](https://dev-to-uploads.s3.amazonaws.com/i/2jowcs05rmn2p1s6kjjh.png)
 
If we inspect using our browser's dev-tools, we'll be able to see that the page is rendered on the server and no data-fetching happens on the client:

![image22](https://dev-to-uploads.s3.amazonaws.com/i/pxhpvw8y5riq4s317r4x.png)
  
Next, let's create a similar page for **Products** so that we can demonstrate that the fetching of data while routing also happens on the server.

First, we need to create a new file named **get-all-products.ts** inside the **lib** directory with the following content:

```
// lib/get-all-products.ts

import { gql } from "graphql-request";
import graphQLClient from "./graphql-client";

const getAllProducts = async () => {
  const query = gql`
    {
      allProducts {
        data {
          _id
          name
          description
          price
        }
      }
    }
  `;

  const response = await graphQLClient.request(query);
  const data = JSON.parse(JSON.stringify(response));

  return data.allProducts.data;
};

export default getAllProducts;
```

Next, we'll need to create a new file named **products.tsx** inside the **pages** directory with the following content:

```
// pages/products.tsx

import { Badge, Box, Grid, HStack, Text } from "@chakra-ui/react";
import getAllProducts from "../lib/get-all-products";
import { NextPage } from "next";
import React from "react";
import { QueryClient, useQuery } from "react-query";
import { dehydrate } from "react-query/hydration";

const ProductsPage: NextPage = () => {
  const { data } = useQuery("allProducts", getAllProducts, {
    staleTime: Infinity,
  });

  return (
    <Grid gap={4} m={4} gridTemplateColumns="1fr" w="100%">
      <Box borderWidth={1} rounded="md" bg="white">
        <Box borderBottomWidth={1} px={8} py={6} bg="gray.200">
          <Text fontWeight="bold" textTransform="uppercase">
            Products
          </Text>
        </Box>
        <Box>
          {data.map((product) => {
            return (
              <Box
                key={product._id}
                p={8}
                color="gray.700"
                borderBottomWidth={1}
              >
                <HStack spacing={8} justifyContent="space-between">
                  <Text>{product.name}</Text>
                  <Badge colorScheme="green">{product.description}</Badge>
                </HStack>
              </Box>
            );
          })}
        </Box>
      </Box>
    </Grid>
  );
};

export const getServerSideProps = async () => {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery("allProducts", getAllProducts, {
    staleTime: Infinity,
  });

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
};

export default ProductsPage;
```

[getServerSideProps](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) is a function through which we can fetch data on the server in Next.js.

Now, if we visit[ http://localhost:3000](http://localhost:3000/) and click on the **Products** tab, we'll be able to view the products listing page:

![image17](https://dev-to-uploads.s3.amazonaws.com/i/awqb0payj8scgr5jkp6p.gif)
 
If we inspect the request, we can see that the fetching of data happens on the server. No external API request to the Fauna GraphQL servers happens on the client-side of the Next.js application:

![image15](https://dev-to-uploads.s3.amazonaws.com/i/g60tchcbtd8s395xkjsb.png)
 
![image1](https://dev-to-uploads.s3.amazonaws.com/i/4ost9kuwa5k4gy6yckd1.png)
 
Next, we'll push our code to[ GitHub](https://github.com/) and deploy to[ Vercel](http://vercel.com/).

## **Pushing our code to GitHub**

In this section, we’ll commit our code and push it to [GitHub](https://github.com/). We'll need to do the following steps:

1. Log into your GitHub Account.** **You’ll need a [GitHub account](https://github.com/join) to store our code. This is required because we want to deploy our application on Vercel.

2. Commit our code using[ Git](https://git-scm.com/). You’ll need to have Git installed on your computer for this step.

From the root of our application, we can run the following command to[ stage](https://git-scm.com/docs/git-add) all our files:

```
git add --all
```

Next, we can[ commit](https://git-scm.com/docs/git-commit) all our files by running the following command from the root of our application:

```
git commit -m "Adds all files"
```

3. Create a new GitHub repository by visiting[ https://github.com/new](https://github.com/new). We'll need to enter the name of our repository and click on the **Create Repository** button.

![image13](https://dev-to-uploads.s3.amazonaws.com/i/b6y9vjv6h090qnzggp9a.png)
 
4. Push our code to GitHub by running the following command from the root of our application.

```
git remote add origin <https://github.com/><your-github-username>/<your-repository-name>b.git
git branch -M main
git push -u origin main
```

Now, our code should be available on GitHub.

![image4](https://dev-to-uploads.s3.amazonaws.com/i/1baanjhz15nsrof4r9or.png)

## **Deploying the application to Vercel**

In this step, we'll deploy our code using[ Vercel](http://vercel.com/). We'll need to do the following steps:

1. **Log into your Vercel account**

We’ll need a Vercel account to deploy our code. You can create one if you don't already have one.

2. **Importing our repository to Vercel**

We can import our repository to Vercel by visiting[ https://vercel.com/new](https://vercel.com/new) and searching for our GitHub repository.

We might need to grant Vercel access to the Git repositories that we want to import.

![image11](https://dev-to-uploads.s3.amazonaws.com/i/t82bd30lv1lcx4uryugs.png)

We can configure the GitHub app by clicking on the **Configure GitHub App** button. Once we give the necessary access to Vercel, we should be able to view our repository and click on the **Import** button next to it.

![image18](https://dev-to-uploads.s3.amazonaws.com/i/7rpjfek4d8vx85a7gwo1.png)
 
In the next step, we can select the scope of Vercel. For this application, we'll select our **personal account** by clicking on the **Select** button next to it.

![image7](https://dev-to-uploads.s3.amazonaws.com/i/43m3w6lxbztb1slnopyf.png)

In the next step, we'll be able to view the details of our project. We'll need to enter the environment variables from our **.env.local** file and click on the **Deploy** button to deploy our application.

![image5](https://dev-to-uploads.s3.amazonaws.com/i/3gkirkmyc2zm5bk7k33v.png)
 
Once the application is deployed, we should be able to view the following screen:

![image14](https://dev-to-uploads.s3.amazonaws.com/i/gtzrepmgb9gz31qotsk6.png)

If we click on the **Visit** button, we should be able to view our application deployed on Vercel.

## **Conclusion**

In this tutorial, we learned about how we can integrate Next.js with Fauna, as well as how we can fetch GraphQL data on the server using React Query.

An online demo of the application that we built is hosted on[ Vercel](https://nextjs-react-query-faunadb.vercel.app/) and the code is available on[ GitHub](https://github.com/ghoshnirmalya/nextjs-react-query-faunadb).