Using Next.js with Fauna and GraphQL
Tags: fauna, graphql, nextjs, serverless
Published: 2021-09-07 20:13:48

[Next.js](http://nextjs.org/) is a frontend framework, powered by React, which provides optimizations and tools to create fast websites. It supports both hybrid static pages, as well as dynamic, real-time content. With built-in integrations like [TypeScript](https://nextjs.org/docs/basic-features/typescript) and [ESLint](https://nextjs.org/docs/basic-features/eslint), Next.js simplifies the Developer Experience (DX) and optimizes for serverless and [edge deployments](https://nextjs.org/docs/deployment).

Next.js is not opinionated about where you store your data. It provides APIs for the developer to hook into their content, commerce, or database of choice. Since Next.js is optimized for serverless, it makes sense to pair it with a serverless database. 

## Why Fauna with Next.js?

Fauna is a serverless, transactional database. It's flexible like NoSQL databases, with support for relational data and transactions. Plus, it has an [excellent free tier](https://fauna.com/pricing) and is a managed service. No servers to maintain or hidden fees!

Since it's serverless-first, you can consider Fauna "connectionless". Instead of battling connection limits, Fauna provides a database as an API, allowing developers to not worry about connection limits in serverless environments. Further, it provides a [GraphQL API](https://docs.fauna.com/fauna/current/api/graphql/), which simplifies creating, querying, and updating your data.

## Why Vercel with Next.js?

[Vercel](http://vercel.com/) is a serverless platform for static applications and frontend frameworks, providing a frictionless Developer Experience to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.

Vercel makes it easy for frontend teams to develop, preview, and ship delightful user experiences, where performance is the default. Fauna.com is built with Next.js and Vercel!

Let's explore deploying a Next.js app using Fauna to Vercel.

## Deploying Next.js and Fauna to Vercel

Getting started with Fauna and Next.js only takes a few minutes, thanks to the official Next.js template and the [Vercel Integration](https://vercel.com/integrations/fauna). The integration will allow you to create or connect to your Fauna database, create a new key, and then add it as an [Environment Variable](https://vercel.com/docs/environment-variables) to your Vercel project. The value will be saved as `FAUNA_ADMIN_KEY`.

### Manual Installation

Alternatively, you can start from the command line locally and clone the template using [create-next-app](https://nextjs.org/docs/api-reference/create-next-app).

```
npx create-next-app --example with-fauna with-fauna-app
```

Next, [create a database](https://dashboard.fauna.com/) and generate an admin token by going to the **Security** tab on the left and then click **New Key**. Give the new key a name and select the **Admin** Role. Save the token.

### Setting Up Your Schema

The Next.js and Fauna example includes a setup script (`npm run setup`). After providing your admin token, the script will:

- **Import your GraphQL schema:** Fauna automatically sets up collections and indexes to support your queries. You can view these in your [project dashboard](https://dashboard.fauna.com/) under **GraphQL**.
- **Create an index and function:** The script will create a GraphQL resolver that uses [User-defined functions](https://docs.fauna.com/fauna/current/api/graphql/functions?lang=javascript) based on a sorting index.
- **Create a scoped token:** This token is for use on the client side. The admin key can be used on the server side.

After the script completes, a `.env.local` [file](https://nextjs.org/docs/basic-features/environment-variables) will be created for you with the newly generated client token assigned to an Environment Variable.

## Connecting to Fauna in your Next.js Application

To connect to your Fauna database with GraphQL, you can use the [graphql-request](https://github.com/prisma-labs/graphql-request) library to securely fetch or mutate data inside `lib/fauna.js`.

```jsx
// lib/fauna.js

import { GraphQLClient, gql } from 'graphql-request'

const graphQLClient = new GraphQLClient('https://graphql.fauna.com/graphql', {
  headers: {
    authorization: `Bearer ${process.env.FAUNA_ADMIN_KEY}`,
  },
})
```

Alternatively, you can use the [Fauna JavaScript driver](https://docs.fauna.com/fauna/current/drivers/javascript) to execute FQL queries in Node.js.

## Querying Fauna with GraphQL

Now that your GraphQL client has been created, you can query or mutate data using your secure connection.

```jsx
// lib/fauna.js

export const listGuestbookEntries = () => {
  const query = gql`
    query Entries($size: Int) {
      entries(_size: $size) {
        data {
          _id
          _ts
          name
          message
          createdAt
        }
      }
    }
  `

  return graphQLClient
    .request(query, { size: 999 })
    .then(({ entries: { data } }) => data)
}
```

## Displaying your Fauna Data inside Next.js

Since Next.js can handle multiple rendering strategies, including static-site generation, server-side rendering, and client-side rendering, we can display our data from Fauna in many ways.

### Static Site Generation

Using [getStaticProps](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) and [getStaticPaths](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation), we can statically generate all of the content from our Fauna database.

```jsx
// pages/entries/[id].js

export default function Entry({ entry }) {
  // Render entry...
}

// This function gets called at build time
export async function getStaticPaths() {
  // Query all entries from Fauna
  const res = await listGuestbookEntries()
  const entries = await res.json()

  // Get the paths we want to pre-render based on entries
  const paths = entries.map((entry) => ({
    params: { id: entry['_id'] }
  }))

  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false }
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the key `id`.
  // If the route is like /entries/1, then params.id is 1
  // We could create getGuestbookEntry similar to listGuestbookEntries
  const res = await getGuestbookEntry(params.id)
  const entry = await res.json()

  // Pass entry data to the page via props
  return { props: { entry } }
}
```

### Server-Side Rendering

If we need to fetch new data from *every* request, we can use [getServerSideProps](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering). 

```jsx
// pages/index.js

export default function Entries({ entries }) {
  // Render entries...
}

export async function getServerSideProps(context) {
  // Query all entries from Fauna
  const res = await listGuestbookEntries()
  const entries = await res.json()

  // Pass entry data to the page via props
  return {
    props: { entries }
  }
}
```

### Client-Side Rendering

If we want to fetch data on the client-side, initially showing a loading state, we can use a React hook library like [SWR](https://swr.vercel.app/) to simplify data fetching.

```jsx
// pages/index.js

import useSWR from 'swr'

const fetcher = (...args) => fetch(...args).then(res => res.json())

export default function Entries() {
  const { data, error } = useSWR('/api/entries', fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}
```

This example fetches the Fauna data from an [API Route](https://nextjs.org/docs/api-routes/introduction), which allows us to securely connect to Fauna on the server.

```jsx
// pages/api/entries.js

import { listGuestbookEntries } from '../../lib/fauna'

export default async function handler(req, res) {
  const entries = await listGuestbookEntries()

  return res.json(entries)
}
```

## Next Steps

Try out Fauna and Next.js today and [deploy to Vercel](https://vercel.com/integrations/fauna). Next, you can explore:

- Explore Fauna's [built-in identity and authentication](https://docs.fauna.com/fauna/current/tutorials/authentication/user.html), which can be paired with NextAuth and their [built-in adapter for Fauna](https://next-auth.js.org/adapters/fauna).
- Learn more about [migrating your Fauna schema](https://fauna.com/blog/evolving-the-structure-of-your-fauna-database).
- Explore [GraphQL code generation](https://the-guild.dev/blog/graphql-codegen-plugin-typescript-swr) to auto-generate [SWR hooks](https://swr.vercel.app/).