How to build a serverless app with Gatsby, Netlify and FaunaDB - Part 2

Co-Founder, @CreoWis | Teacher, @tapaScript | Founder, @ReactPlay | YouTuber | Writer | Human
Search for a command to run...

Co-Founder, @CreoWis | Teacher, @tapaScript | Founder, @ReactPlay | YouTuber | Writer | Human
This is a mini series to build a serverless app with Gatsby, Netlify and FaunaDB. Learn how to create serverless functions, access using APIs and build the client side using it. Hands-on Time!!
Every story has an end Finally, we are at the end of the series, Go Serverless. Thank you for reading and liking previous articles of the series. Truly appreciate. If you are new to the series, here is a quick recap of what we have covered so far: 👉...
Traditional SSR is great, but it has a massive buffering problem. Let's look into the SSR Streaming and learn how it could be a game changer.

Learn how to build maintainable, reusable forms in React using design patterns like custom hooks, compound components, and context for scalable CRUD

Learn how JavaScript variables work, including let vs const, primitive and reference types, pass by value, and how JavaScript stores data in memory.

A beginner-friendly introduction to JavaScript covering what it is, how it works, and how to set up your JavaScript environment

Learn how the promises are handled internally using event loop. This concept is essentials to debug and work with async programming.

GreenRoots Blog - A Blog by Tapas Adhikary
186 posts
Educator | YouTuber | Building ReactPlay | Let's Connect
Welcome to the Part 2 of the series Go Serverless. Hope you are enjoying it so far by following the steps to develop the serverless testimonial app.
In the last article(Part 1), we have established a ground by,
FaunaDB.Netlify and finally tested them like APIsIn this article, we will be using the serverless functions to build a user interface of the testimonial app. To do that, we will use Gatsby which is a super cool, react-based static site generator. We will also learn about using Netlify to build and deploy the testimonial app.
We will start where we left off in the last article. Let us continue to develop the app on top of the code implemented so far as part of the previous article.
If you haven't got a chance to go through the part 1 of the series yet, here is the link:
As usual, here is the source code repo link:
There are multiple ways to setup a Gatsby based project. There are plenty of starter projects to help you get going. You can check out this quick start guide to learn more.
For a better understanding sake, we will not be using any of the starter projects here. We will build things from the scratch.
Install gatsby-cli globally. This tool will help us to work with the Gatsby environment.
npm install -g gatsby-cli
Install gatsby, react and react-dom
yarn add gatsby react react-dom
Edit the scripts section of the package.json file to add a script for 'develop'.
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"develop": "gatsby develop"
}
Gatsby projects need a special configuration file called, gatsby-config.js. At this time, we will need an empty file. Please create a file named, gatsby-config.js with the following content:
module.exports = {
// keep it empty
}
Now it is the time to create our first page with Gatsby. Create a folder named, src at the root of the project folder. Create a sub-folder named, pages under src. Create a file named, index.js under src/pages with the following content:
import React, { useEffect, useState } from 'react';
export default () => {
const [status, setStatus ] = useState('loading...');
const [testimonials, setTestimonials] = useState(null);
return (
<>
<h1>Testimonials to load here...</h1>
</>
)
}
The code above is a simple react component. We import React and two in-built hooks called, useState and useEffect. We have got a couple of state variables to keep track of the array of testimonials and the API responses.
Let's run it. We generally need to use the command, gatsby develop to run the app locally. As we have to run the client side application with netlify functions, we will continue to use, netlify dev command.
This single command will take care of running the serverless functions locally along with the client side gatsby application. Open a command prompt at the root of the project folder and type,
netlify dev
That's all. Try accessing the page at localhost:8888. You should see something like this,

Gatsby project build creates couple of output folders which you may not want to push to the source code repository. Let us add few entries to the .gitignore file so that, we do not get unwanted noise.
Add .cache and public to the .gitignore file. Here is the full content of the file:
.cache
public
node_modules
*.env
At this stage, your project directory structure should match with the following:

Our goal here is to fetch all the testimonials using, /api/get-testimonials call. We would like to show the fetched testimonials in the following fashion:

First thing first. We need to load all the testimonials using the serverless function and show it to the page we have just created.
We will follow only a few steps to load all the testimonials. You need to edit the index.js file with the following changes:
Import axios library so that, we can make the API calls.
import axios from "axios";
Make a call to the serverless function using the URI, api/get-tetsimonials. We will use the hook, useEffect to accomplish it. We use axios to make this call. On a successful response, testimonials are stored in the testimonials state variable.
Note, we also change the status as loaded to indicate that, the data has been loaded successfully.
useEffect(() => {
if (status !== "loading...") return;
axios("/api/get-testimonials").then(result => {
if (result.status !== 200) {
console.error("Error loading testimonials");
console.error(result);
return;
}
setTestimonials(result.data.messages);
setStatus("loaded");
});
}, [status]);
Have you noticed those cool avatars in the testimonial interface above? I am using them from https://avatars.dicebear.com/api/. These avatars are available to fetch for free using the URLs.
We will write a simple util function named, getAvatar() to fetch some random 'happy' avatars.
const getAvatar = () => {
const random = Math.floor(Math.random() * (testimonials.length - 0 + 1) + 0);
const imgUrl = `https://avatars.dicebear.com/api/human/${random}.svg?mood[]=happy`;
return imgUrl;
}
Last is the render function to show the testimonial message, rating along with an avatar in the UI. We loop through the testimonials array and render the details with the ui elements.
return (
<>
{testimonials && testimonials.map((testimonial, index) => (
<div key={ index }>
<img
src={ getAvatar() }
height="50px"
width="50px"
alt="avatar" />
<div className="testimonial">
<span>{ testimonial.rating }</span>
<p className="text">
{ testimonial.text }
</p>
</div>
</div>
))}
</>
);
That's all about it! We have completed the loading of all the testimonials and showing them in the UI. Here is the complete code of index.js:
import React, { useEffect, useState } from 'react';
import axios from "axios";
export default () => {
const [status, setStatus ] = useState('loading...');
const [testimonials, setTestimonials] = useState(null);
useEffect(() => {
if (status !== "loading...") return;
axios("/api/get-testimonials").then(result => {
if (result.status !== 200) {
console.error("Error loading testimonials");
console.error(result);
return;
}
setTestimonials(result.data.messages);
setStatus("loaded");
});
}, [status]);
const getAvatar = () => {
const random = Math.floor(Math.random() * (testimonials.length - 0 + 1) + 0);
const imgUrl = `https://avatars.dicebear.com/api/human/${random}.svg?mood[]=happy`;
return imgUrl;
}
return (
<>
{testimonials && testimonials.map((testimonial, index) => (
<div key={ index }>
<img
src={ getAvatar() }
height="50px"
width="50px"
alt="avatar" />
<div className="testimonial">
<span>{ testimonial.rating }</span>
<p className="text">
{ testimonial.text }
</p>
</div>
</div>
))}
</>
);
}
How does the UI look now? Well, it looks like this:

No doubt, we have fetched all the testimonials and showing them in the UI. But it doesn't look great, right?
Our vision is this,

Notice, there is a rating component(with stars) and a carousel component to browse through the testimonials. We will use a couple of react-based npm to achieve these.
Open a command prompt at the root of the project folder. Try this command(or npm i) to install these libraries.
yarn add react-stars react-responsive-carousel
We have imported ReactStars and Carousel components along with the carousel.min.css to the index.js file.
Only few changes that we have to do are,
JSX portion of the code with the Carousel componentReactStars component for the ratings.Here is the complete code with the changes:
import React, { useEffect, useState } from 'react';
import axios from "axios";
// import these libraries
import ReactStars from 'react-stars';
import "react-responsive-carousel/lib/styles/carousel.min.css";
import { Carousel } from "react-responsive-carousel";
export default () => {
const [status, setStatus ] = useState('loading...');
const [testimonials, setTestimonials] = useState(null);
useEffect(() => {
if (status !== "loading...") return;
axios("/api/get-testimonials").then(result => {
if (result.status !== 200) {
console.error("Error loading testimonials");
console.error(result);
return;
}
setTestimonials(result.data.messages);
setStatus("loaded");
});
}, [status]);
const getAvatar = () => {
const random = Math.floor(Math.random() * (testimonials.length - 0 + 1) + 0);
const imgUrl = `https://avatars.dicebear.com/api/human/${random}.svg?mood[]=happy`;
return imgUrl;
}
return (
<Carousel
className="main"
showArrows={true}
infiniteLoop={true}
showThumbs={false}
showStatus={false}
autoPlay={false} >
{testimonials && testimonials.map((testimonial, index) => (
<div key={ index } className="testimonial">
<img
src={ getAvatar() }
height="50px"
width="50px"
alt="avatar" />
<div className="message">
<ReactStars
className="rating"
count={ testimonial.rating }
size={24}
color1={'#ffd700'}
edit={false}
half={false} />
<p className="text">
{ testimonial.text }
</p>
</div>
</div>
))}
</Carousel>
);
}
Please create a file named, index.css with the following content under the directory, src/pages.
.rating {
display: flex;
justify-content: center;
}
.carousel .slide {
padding: 20px;
font-size: 20px;
}
body {
background-color: #000000;
color: #FFFFFF;
}
Import the index.css file into the index.js file as,
import './index.css';
Try netlify dev and access the url localhost:8888. You should see the UI appearing like this:

All Good so far. But there is one issue. We are running the app locally. It is fun but, not as much as we will get by running it publicly. Let's do that with few simple steps.
testimonial.You have an account with netlify already. Please login and click on the button, New site from Git.

Provide the one-click authorization to your git repo and select the testimonial repository.

You need to provide few details to deploy the app. Please provide the details as it is shown below and deploy the app.

Do you remember, we have used a API server key locally for accessing the data from the database? Now we need to tell netlify about this key. Go to the environment setting under Build & deploy option to create a new environment variable.
Create the new environment variable with the key as, FAUNA_SERVER_SECRET and value is the actual server key from the .env file.

Netlify allocates a domain with a random name for your app/website. You can change it to something more meaningful to you. In my case, I have given the name as, testimonial-greenroots.
Hence the app will be available on this URL: https://testimonial-greenroots.netlify.app/.

Finally, deploy the app once again by clearing the cache.

Congratulations!!!! Your app/site is publicly available now.

Up next, the last article of the series is to integrate the authentication module to our app painlessly. We will allow users to create a testimonial only after they authenticate to our application.
Sounds like fun? Yeah, stay tuned to get there soon.
Follow me on twitter @tapasadhikary for more updates.