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

# Let's Continue...

Welcome to the `Part 2` of the series [Go Serverless](https://hashnode.com/series/go-serverless-ckf15vq040510sus1amb3goa2). Hope you are enjoying it so far by following the steps to develop the serverless testimonial app.

[In the last article(Part 1)](https://blog.greenroots.info/how-to-build-a-serverless-app-with-gatsby-netlify-and-faunadb-part-1-ckf2dpwy200idbns1fbgpg7kx), we have established a ground by,

- Creating an account with the client-serverless data API provider called, `FaunaDB`.
- Setting up the database, schema, document and the server key.
- Writing the serverless functions using, `Netlify` and finally tested them like APIs

In 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:
- [How to build a serverless app with Gatsby, Netlify and FaunaDB - Part 1](https://blog.greenroots.info/how-to-build-a-serverless-app-with-gatsby-netlify-and-faunadb-part-1-ckf2dpwy200idbns1fbgpg7kx)

As usual, here is the source code repo link:

%[https://github.com/atapas/testimonial/tree/part-2-client-side]

# Gatsby: Install and initial setup
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](https://www.gatsbyjs.com/docs/quick-start/) 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.

  ```shell
   npm install -g gatsby-cli
  ```
- Install gatsby, react and react-dom

  ```shell
   yarn add gatsby react react-dom
  ```
- Edit the `scripts` section of the `package.json` file to add a script for 'develop'.

  ```js
   "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:

  ```js
   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:

  ```js
    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](https://reactjs.org/docs/hooks-intro.html) 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,

  ```shell
   netlify dev
  ```
- That's all. Try accessing the page at `localhost:8888`. You should see something like this,
 ![part_2_inital_page.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600016569995/0PNmOocSa.png?border=1,CCCCCC&auto=compress)

- 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:

 ```shell
  .cache
  public
  node_modules
  *.env
 ```

 At this stage, your project directory structure should match with the following:

 ![dir_part_2.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600018876733/Ofupr5x1Z.png?border=1,CCCCCC&auto=compress)

# Load all the Testimonials
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: 

![part_2_flow.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1600164363334/rQe_2v5tL.gif?border=1,CCCCCC&auto=compress)

First thing first. We need to load all the testimonials using the `serverless function` and show it to the page we have just created.

## Load data
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.

  ```js
  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.

  ```js
   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/](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.

  ```js
  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.

  ```js
  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`:
 
```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:

![part_2_loaded_testi.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600018801706/xAsu8M686.png?border=1,CCCCCC&auto=compress)

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

## So, Let's make things look better
Our vision is this,

![part_2_ui.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600163723685/UU8fp0oTx.png?border=1,CCCCCC&auto=compress)

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.

## Install libraries
Open a command prompt at the root of the project folder. Try this command(or `npm i`) to install these libraries. 

```shell
 yarn add react-stars react-responsive-carousel
```

## Use the libraries
We have imported [ReactStars](https://www.npmjs.com/package/react-stars) and [Carousel](https://www.npmjs.com/package/react-responsive-carousel) components along with the `carousel.min.css` to the index.js file. 

Only few changes that we have to do are, 

- Wrap the `JSX` portion of the code with the `Carousel` component
- Use the `ReactStars` component for the ratings.

Here is the complete code with the changes:

```js
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`.

```css
.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,

```js
import './index.css';
```

Try `netlify dev` and access the url `localhost:8888`. You should see the UI appearing like this:

![part_2_flow.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1600053637324/VpFLn-3bu.gif?border=1,CCCCCC&auto=compress)

# Deploy and Publish
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.

- Make sure to commit all the code changes to the git repository, say, `testimonial`.
- You have an account with `netlify` already. Please login and click on the button, `New site from Git`.
 ![new_site_netlify.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600053973405/KVdYcdKTB.png?border=1,CCCCCC&auto=compress)

- Provide the one-click authorization to your git repo and select the `testimonial` repository.
 ![select_repo_netlify.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600053988996/v-k4L_pGT.png?border=1,CCCCCC&auto=compress)

- You need to provide few details to deploy the app. Please provide the details as it is shown below and deploy the app.
 ![deploy_site_netlify.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600054618252/VeLM1F4UK.png?border=1,CCCCCC&auto=compress)

- 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_env.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600054860269/blNg2-SvK.png?border=1,CCCCCC&auto=compress)

- 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/](https://testimonial-greenroots.netlify.app/).
 ![change_site_name_netlify.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600054967630/rOMk4p5WP.png?border=1,CCCCCC&auto=compress)

- Finally, deploy the app once again by clearing the cache.
 ![deploy_final.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600055776184/oJKiXOizt.png?border=1,CCCCCC&auto=compress)

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

![production_part_2.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600055850664/WuWx0A8uy.png?border=1,CCCCCC&auto=compress)

# What's Next?
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.

<hr />
If it was useful to you, please Like/Share so that, it reaches others as well. To get email notification on my latest posts, please subscribe to my blog by hitting the Subscribe button at the top of the page.

Follow me on twitter [@tapasadhikary](https://twitter.com/tapasadhikary) for more updates.
