How to integrate Next.js with Fauna using React Query
Nirmalya Ghosh
Posted on February 8, 2021
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:
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
Technologies used in the application
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 where we should be able to see the following screen:
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
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;
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 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 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 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.
- Click on the New Database button from the Fauna dashboard.
- 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.
The database will be created and we should be able to see a similar screen:
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.
Next, if we visit the GraphQL tab on the Fauna dashboard, we should be able to execute a GraphQL query or mutation.
The HTTP headers will already be pre-populated in the GraphQL Playground:
We can also check out the Schema of our database:
We can also checkout the Docs tab to know about the available GraphQL queries and mutations:
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:
- Create a new file name .env.local in the root of our application to store all our environment variables.
- 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=
- 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
- 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.
- 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;
- 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
- 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, we should be able to view the following screen:
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:
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 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:
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:
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:
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.
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
Next, we can commit all our files by running the following command from the root of our application:
git commit -m "Adds all files"
- 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.
- 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.
Deploying the application to Vercel
In this step, we'll deploy our code using Vercel. We'll need to do the following steps:
- 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.
- 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.
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.
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.
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.
Once the application is deployed, we should be able to view the following screen:
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.
Posted on February 8, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.