DEV Community

Cover image for How to integrate Next.js with Fauna using React Query
Nirmalya Ghosh
Nirmalya Ghosh

Posted on

How to integrate Next.js with Fauna using React Query

Introduction

In this tutorial, we'll learn about how we can integrate Next.js with Fauna. We'll also learn about how we can fetch GraphQL data on the server using React Query. We'll be building a server-side rendered application which will look similar to the following:

image3

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 and the code is available on GitHub.

Introduction

Fauna is a flexible, developer-friendly, secure database which delivers a web-native API. It has a lot of features and is very easy to get up and running.

Next.js is one of the most popular React frameworks which has a lot of features like file-system routing, built-in CSS support, API routes, 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
  2. git

Technologies used in the application

  1. Fauna
  2. Next.js
  3. Chakra UI
  4. React Query

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

image20

Adding Chakra UI

Chakra UI 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
Enter fullscreen mode Exit fullscreen mode

We'll be using TypeScript 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;
Enter fullscreen mode Exit fullscreen mode

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).
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Adding React Query

React Query 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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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 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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

This file will be responsible for handling the Fauna authentication using Basic token for our GraphQL client.

Next, let's create a new Fauna database.

You need to visit 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

  1. 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

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

image21

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

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

image8

The HTTP headers will already be pre-populated in the GraphQL Playground:

image16

We can also check out the Schema of our database:

image19

We can also checkout the Docs tab to know about the available GraphQL queries and mutations:

image6

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
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
          }
        }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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.
  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=
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode
  1. 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;
Enter fullscreen mode Exit fullscreen mode

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

  1. 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;
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode
  1. Start our Next.js server by running the following command from the root of our application:
yarn dev
Enter fullscreen mode Exit fullscreen mode

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

image2

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

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

getServerSideProps is a function through which we can fetch data on the server in Next.js.

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

image17

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

image1

Next, we'll push our code to GitHub and deploy to Vercel.

Pushing our code to GitHub

In this section, we’ll commit our code and push it to GitHub. We'll need to do the following steps:

  1. Log into your GitHub Account.** **You’ll need a GitHub account to store our code. This is required because we want to deploy our application on Vercel.

  2. Commit our code using Git. 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 all our files:

git add --all
Enter fullscreen mode Exit fullscreen mode

Next, we can commit all our files by running the following command from the root of our application:

git commit -m "Adds all files"
Enter fullscreen mode Exit fullscreen mode
  1. Create a new GitHub repository by visiting https://github.com/new. We'll need to enter the name of our repository and click on the Create Repository button.

image13

  1. 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
Enter fullscreen mode Exit fullscreen mode

Now, our code should be available on GitHub.

image4

Deploying the application to Vercel

In this step, we'll deploy our code using Vercel. 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.

  1. Importing our repository to Vercel

We can import our repository to Vercel by visiting 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

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

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

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

Once the application is deployed, we should be able to view the following screen:

image14

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 and the code is available on GitHub.

Top comments (0)