Building Headless CMS With Fauna And Vercel Functions.
Tags: serverless, fauna, database, react
Published: 2021-05-14 09:57:33

Written in connection with the [Write With Fauna program](https://www2.fauna.com/write-with-fauna).

Quick summary — *This article introduces the concept of the headless CMS, a backend-only content management system that allows developers to create, store, manage and publish the content over an API using the Fauna and Vercel functions. This improves the frontend-backend workflow, that enables developers to build excellent user experience quickly.*

In this tutorial, we will learn and use headless CMS, Fauna, and Vercel functions to build a blogging platform, Blogify🚀. After that, you can easily build any web application using a headless CMS, Fauna and Vercel functions. 


## Introduction

According to [MDN](https://en.wikipedia.org/wiki/Content_management_system), A content management system (CMS) is a computer software used to manage the creation and modification of digital content. CMS typically has two major components: a content management application (CMA), as the front-end user interface that allows a user, even with limited expertise, to add, modify, and remove content from a website without the intervention of a webmaster; and a content delivery application (CDA), that compiles the content and updates the website. 

### The Pros And Cons Of Traditional vs Headless CMS
Choosing between these two can be quite confusing and complicated. But they both have potential advantages and drawbacks.

**Traditional CMS Pros**

- Setting up your content on a traditional CMS is much easier as everything you need (content management, design, etc) are made available to you.

- A lot of traditional CMS has drag and drop, making it easy for a person with no programming experience to work easily with them. It also has support for easy customization with zero to little coding knowledge.


**Traditional CMS Cons**

- The plugins and themes which the traditional CMS relies on may contain malicious codes or bugs and slow the speed of the website or blog.


- The traditional coupling of the front-end and back-end definitely would more time and money for maintenance and customization.

**Headless CMS Pros**

- There’s flexibility with choice of frontend framework to use since the frontend and backend are separated from each other, it makes it possible for you to pick which front-end technology suits your needs. It gives the freewill to choose the tools need to build the frontend—flexibility during the development stage.


- Deploying works easier with headless CMS. The applications (blogs, websites, etc) built with headless CMS can be easily be deployed to work on various displays such as web device, mobile devices, AR/VR devices.

**Headless CMS Cons**

- You are left with the worries of managing your back-end infrastructures, setting up the UI component of your site, app.


- Implementation of headless CMS are known to be more costly against the traditional CMS. Building headless CMS application that embodies analytics are not cost-effective.

[**Fauna**](https://fauna.com/) uses a preexisting infrastructure to build web applications without the usually setting up a custom API server. This efficiently helps to save time for developers, and the stress of choosing regions and configuring storage that exists among other databases; which is global/multi-region by default, are nonexistent with Fauna. All maintenance we need are actively taken care of by engineers and automated DevOps at Fauna. We will use Fauna as our *backend-only content management system*.

**Pros Of Using Fauna**

- The ease to use and create a Fauna database instance from within development environment of the hosting platforms like Netlify or Vercel.


- Great support for querying data via GraphQL or use  Fauna’s own query language. Fauna Query Language (FQL), for complex functions.


- Access data in multiple models including relational, document, graph and temporal.


- Capabilities like built-in authentication, transparent scalability and multi-tenancy are fully available on Fauna.


- Add-on through Fauna Console as well as Fauna Shell makes it easy to manage database instance very easily.

**Vercel Functions**, also known as Serverless Functions, according to the [docs](https://vercel.com/docs/serverless-functions/introduction) are pieces of code written with [backend languages](https://vercel.com/docs/serverless-functions/supported-languages#supported-languages) that take an HTTP request and provide a response.


## Prerequisites

To take full advantage of this tutorial, ensure the following tools are available or installed on your local development environment:


- Access to Fauna dashboard
- Basic knowledge of React and React Hooks
- Have [`create-react-app`](https://reactjs.org/docs/create-a-new-react-app.html) installed as a global package or use npx to bootstrap the project.
- Node.js version >= `12.x.x` installed on your local machine.
- Ensure that `npm` or `yarn` is also installed as a package manager


## Database Setup With Fauna

Sign in into your fauna account to get started with Fauna, or first register a new account using either email credentials/details or using an existing Github account as a new user. You can register for a new account [here](https://dashboard.fauna.com/accounts/register?utm_source=DevTo&utm_medium=referral&utm_campaign=WritewithFauna_NodeFauna_FIkechi). Once you have created a new account or signed in, you are going to be welcomed by the dashboard screen. We can also make use of the [fauna shell](https://github.com/fauna/fauna-shell) if you love the shell environment. It easily allows you to create
and/or modify resources on Fauna through the terminal.

Using the fauna shell, the command is:


    npm install --global fauna-shell
    fauna cloud-login




But we will use the website throughout this tutorial. Once signed in, the dashboard screen welcomes you:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eowwocl1r62wi87yw0e2.png)

Now we are logged in or have our accounts created, we can go ahead to create our Fauna. We’ll go through following simple steps to create the new fauna database using Fauna services. We start with naming our database, which we’ll use as our content management system. In this tutorial, we will name our database **blogify**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suyyfxo0xcs5gzqjy7ak.png)

With the database created, next step is to create a new data collection from the Fauna dashboard. Navigate to the Collection tab on the side menu and create a new collection by clicking on the `NEW COLLECTION` button.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7m021xnxd2c0vts96cg.png)

We’ll then go ahead to give whatever name well suiting to our collection. Here we will call it  *blogify_posts*.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/91m031umkll276y3yxro.png)

Next step in getting our database ready is to create a new `index`. Navigate to the *Indexes* tab to create an index. Searching documents in Fauna can be done by using `indexes`, specifically by matching inputs against an index’s `terms` field. Click on the `NEW INDEX` button to create an index. Once in create index screen, fill out the form: selecting the collection we’ve created previously, then giving a name to our index. In this tutorial, we will name ours *all_posts*. We can now save our index.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6u8j47jwk5qj4azshr4.png)

After creating an index, now it’s time to create our **DOCUMENT**, this will contain the contents/data we want to use for our CMS website. Click on the `NEW DOCUMENT`  button to get started. With the text editor to create our document, we’ll create an object data to serve our needs for the website.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jiboync1t1f8fcpy2mzf.png)

The above `post` object represents the unit data we need to create our blog post. Your choice of data can be so different from what we have here, serving the purpose whatever you want it for within your website. You can create as much document you may need for your CMS website. To keep things simple, we just have three blog posts.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6h6t6fkvb3hz69c2khn8.png)

Now that we have our database setup complete to our choice, we can move on to create our React app, the frontend.


## Create A New React App And Install Dependencies

For the frontend development, we will need dependencies such as `FaunaDB SDK`, `styled-components` and `vercel` in our React app. We will use the `styled-components` for the UI styling, use the `vercel` within our terminal to host our application. The `FaunaDB SDK` would be used to access our contents at the database we had setup. You can always replace the `styled-components` for whatever library you decide to use for your UI styling. Also use any UI framework or library you preferred to others.




    npx create-react-app blogify
    # install dependencies once directory is done/created
    yarn add faunadb styled-components
    # install vercel globally
    yarn global add vercel


The `faunadb` package is Fauna JavaScript driver for Fauna. The library `styled-components` allows you to write actual CSS code to style your components. Once done with all the installation for the project dependencies, check the `package.json` file to confirm all installation was done 
successfully.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bkq3takuf7p9ef9pcqhw.png)

Now let’s start an actual building of our blog website UI. We’ll start with the header section. We will create a `Navigation` component within the `components` folder inside the `src` folder, `src/components`, to contain our blog name, Blogify🚀. 

```

import styled from "styled-components";
function Navigation() {
  return (
    <Wrapper>
      <h1>Blogify🚀</h1>
    </Wrapper>
  );
}
const Wrapper = styled.div`
  background-color: #23001e;
  color: #f3e0ec;
  padding: 1.5rem 5rem;
  & > h1 {
    margin: 0px;
  }
`;
export default Navigation;
```

After being imported within the `App` components, the above code coupled with the stylings through the `styled-components` library, will turn out to look like the below UI:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5orvlbtc2p5b5ilyz595.png)

Now time to create the body of the website, that will contain the `post` data from our database. We structure a component, called Posts, which will contains our blog posts created on the backend.

```

import styled from "styled-components";
function Posts() {
  return (
    <Wrapper>
      <h3>My Recent Articles</h3>
      <div className="container"></div>
    </Wrapper>
  );
}
const Wrapper = styled.div`
  margin-top: 3rem;
  padding-left: 5rem;
  color: #23001e;
  & > .container {
    display: flex;
    flex-wrap: wrap;
  }
  & > .container > div {
    width: 50%;
    padding: 1rem;
    border: 2px dotted #ca9ce1;
    margin-bottom: 1rem;
    border-radius: 0.2rem;
  }
  & > .container > div > h4 {
    margin: 0px 0px 5px 0px;
  }
  & > .container > div > button {
    padding: 0.4rem 0.5rem;
    border: 1px solid #f2befc;
    border-radius: 0.35rem;
    background-color: #23001e;
    color: #ffffff;
    font-weight: medium;
    margin-top: 1rem;
    cursor: pointer;
  }
  & > .container > div > article {
    margin-top: 1rem;
  }
`;
export default Posts;
```

The above code contains styles for JSX that we’ll still create once we start querying for data from the backend to the frontend. 


## Integrate Fauna SDK Into Our React App

To integrate the `faunadb` client with the React app, you have to make an initial connection from the app. Create a new file `db.js` at the directory path `src/config/`. Then import the `faunadb` driver and define a new client.
The `secret` passed as the argument to the `faunadb.Client()` method is going to hold the access key from `.env` file:

```
import faunadb from 'faunadb';
const client = new faunadb.Client({
  secret: process.env.REACT_APP_DB_KEY,
});
const q = faunadb.query;
export { client, q };
```

Inside the `Posts` component create a state variable called `posts` using `useState` React Hooks with a default value of an array. It is going to store the value of the content we’ll get back from our database using the `setPosts` function.
Then define a second state variable, `visible`, with a default value of `false`, that we’ll use to hide or show more post content using the `handleDisplay` function that would be triggered by a button we’ll add later in the tutorial.

```
function App() {
  const [posts, setPosts] = useState([]);
  const [visible, setVisibility] = useState(false);
  const handleDisplay = () => setVisibility(!visible);
  // ...
}
```





## Creating A Serverless Function By Writing Queries

Since our blog website is going to perform only one operation, that’s to get the data/contents we created on the database, let’s create a new directory called `src/api/` and inside it, we create a new file called `index.js`. Making the request with ES6, we’ll use `import`  to import the `client` and the `query` instance from the `config/db.js` file:

```
export const getAllPosts = client
  .query(q.Paginate(q.Match(q.Ref('indexes/all_posts'))))
    .then(response => {
      const expenseRef = response.data;
      const getAllDataQuery = expenseRef.map(ref => {
        return q.Get(ref);
      });
     return client.query(getAllDataQuery).then(data => data);
      })
    .catch(error => console.error('Error: ', error.message));
```



      })
      .catch(error => console.error('Error: ', error.message));

The query above to the database is going to return a `ref` that we can map over to get the actual results need for the application. We’ll make sure to append the `catch` that will help check for an error while querying the database, so we can log it out.

Next is to display all the data returned from our CMS, database—from the Fauna collection. We’ll do so by invoking the query `getAllPosts` from the `./api/index.js` file inside the `useEffect` Hook inside our `Posts` component. This is because when the `Posts` component renders for the first time, it iterates over the data, checking if there are any post in the database: 

```
useEffect(() => {
    getAllPosts.then((res) => {
      setPosts(res);
      console.log(res);
    });
  }, []);
```

Open the browser’s console to inspect the data returned from the database. If all things being right, and you’re closely following, the return data should look like the below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlih6s10fd4jqd4nmz8p.png)

With these data successfully returned from the database, we can now complete our `Posts` components, adding all necessary JSX elements that we’ve styled using `styled-components` library. We’ll use JavaScript `map` to loop over the `posts` state, array, only when the array is not empty:

```
import { useEffect, useState } from "react";
import styled from "styled-components";
import { getAllPosts } from "../api";

function Posts() {
  useEffect(() => {
    getAllPosts.then((res) => {
      setPosts(res);
      console.log(res);
    });
  }, []);

  const [posts, setPosts] = useState([]);
  const [visible, setVisibility] = useState(false);
  const handleDisplay = () => setVisibility(!visible);

  return (
    <Wrapper>
      <h3>My Recent Articles</h3>
      <div className="container">
        {posts &&
          posts.map((post) => (
            <div key={post.ref.id} id={post.ref.id}>
              <h4>{post.data.post.title}</h4>
              <em>{post.data.post.date}</em>
              <article>
                {post.data.post.mainContent}
                <p style={{ display: visible ? "block" : "none" }}>
                  {post.data.post.subContent}
                </p>
              </article>
              <button onClick={handleDisplay}>
                {visible ? "Show less" : "Show more"}
              </button>
            </div>
          ))}
      </div>
    </Wrapper>
  );
}

const Wrapper = styled.div`
  margin-top: 3rem;
  padding-left: 5rem;
  color: #23001e;
  & > .container {
    display: flex;
    flex-wrap: wrap;
  }
  & > .container > div {
    width: 50%;
    padding: 1rem;
    border: 2px dotted #ca9ce1;
    margin-bottom: 1rem;
    border-radius: 0.2rem;
  }
  & > .container > div > h4 {
    margin: 0px 0px 5px 0px;
  }
  & > .container > div > button {
    padding: 0.4rem 0.5rem;
    border: 1px solid #f2befc;
    border-radius: 0.35rem;
    background-color: #23001e;
    color: #ffffff;
    font-weight: medium;
    margin-top: 1rem;
    cursor: pointer;
  }
  & > .container > div > article {
    margin-top: 1rem;
  }
`;

export default Posts;
```

With the complete code structure above, our blog website, Blogify🚀, will look like the below UI:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbfnge7bv9knk5byijjk.png)


## Deploying To Vercel

[**Vercel CLI**](https://vercel.com/cli) provides a set of commands that allow you to deploy and manage your projects. The following steps will get your project hosted from your terminal on vercel platform fast and easy:


    vercel login
            
Follow the instructions to login into your vercel account on the terminal


    vercel
            
Using the `vercel` command from the root of a project directory. This will prompt questions that we will provide answers to depending on what’s asked.

```
vercel
? Set up and deploy “~/Projects/JavaScript/React JS/blogify”? [Y/n] 
? Which scope do you want to deploy to? ikehakinyemi
? Link to existing project? [y/N] n
? What’s your project’s name? (blogify) 
  # click enter if you don't want to change the name of the project
? In which directory is your code located? ./ 
  # click enter if you running this deployment from root directory
? ? Want to override the settings? [y/N] n
```

This will deploy your project to vercel. Visit your vercel account to complete any other setup needed for CI/CD purpose.


## Conclusion

I’m glad you followed the tutorial to this point, hope you’ve learned how to use Fauan as Headless CMS. The combination of Fauna with Headless CMS concepts you can build great web application, from e-commerce application to Notes keeping application, any web application that needs data to be stored and retrieved for use on the frontend. Here’s the [GitHub link](https://github.com/IkehAkinyemi/blogify) to code sample we used within our tutorial, and the [live demo](https://blogify-one.vercel.app/) which is hosted on vercel.


## Related Resources
- [Content Management System](https://en.wikipedia.org/wiki/Content_management_system) (CSM)
- [Fauna CRUD docs](https://docs.fauna.com/fauna/current/tutorials/crud.html#delete)
- [Fauna with GraphQL](https://fauna.com/blog/the-worlds-best-serverless-database-now-with-native-graphql)
- [Indexes in Fauna](https://docs.fauna.com/fauna/current/tutorials/indexes/search.html)
- [Vercel CLI](https://vercel.com/cli)