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

# Introduction
Welcome to a brand new series called, [Go Serverless](https://hashnode.com/series/go-serverless-ckf15vq040510sus1amb3goa2). Unlike the previous series, this is going to be a short one and fully focused on doing hands-on app development.

With this series, we will develop a `serverless` application using, `Gatsbyjs`, `Netlify`, and `FaunaDB`. 

The series will be delivered in three articles,
- **Part 1**: Build a serverless Data API with [FaunaDB](https://fauna.com/) and access it using [Netlify functions](https://docs.netlify.com/functions/overview/).
- **Part 2**: Build the skin. Make use of the functions and APIs to build something cool using [Gatsby](https://www.gatsbyjs.org/tutorial/).
- **Part 3**: Painless integration of Authentication and Authorization using [Netlify Identity](https://docs.netlify.com/visitor-access/identity/).

This article is `Part 1` of the series. Hope you enjoy the journey!

# How to follow this series?
I think the best way to follow this series would be, developing the app alongside of it. 

Just follow the steps, take a pause to understand what just happen, and move on. I hope you like building it and feel free to improvise on it. 

Make sure to keep your favorite beverages(☕, 🍵, 🍺) nearby!

# What are we building?
Have you written any testimonials recently? Let us build an app to allow users to write testimonials with a rating. The app should allow any user to read all the testimonials submitted by other users as well.

We will be taking care of the following use-cases while building the app:
- All testimonials to be stored in a database.
- APIs to support creating a testimonial and fetching all testimonials.
- The user interface to make use of these APIs to show all testimonials and create them.
- User needs to authenticate to create a testimonial.

But wait, there are few important aspects,
- We will not be installing or maintaining any database by ourselves.
- We will not be using any server to host  APIs.
- We will not be using any application server to host the client-side app. Remember, we are `serverless`.

Here is the preview of the testimonial app,

![part_3_flow.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1600069491779/7mC0brwWT.gif)

> If you want to get to the code first, here is the link:

%[https://github.com/atapas/testimonial/tree/part-1-function-api-db]

Alright, let's get started!

# Quick Background
Have you heard of `JAMstack`? 

If so, the concept of the serverless app shouldn't be new to you. One of the fundamentals of `JAMstack` is, it is `practically serverless`. It means, you as a programmer, code owner, or business owner do not manage and maintain the servers. You just focus on building applications using client-side **J**avaScript, reusable **A**PIs, and prebuilt **M**arkups.

> You do not need any prior experience with JAMstack to follow this article. If you are new to `JAMstack`, the link below may be helpful to you.
- [JAMstack for All](https://blog.greenroots.info/jamstack-for-all-an-introduction-cke2fxc5800jvabs15lhn4a9x)

# FaunaDB - Let us set up the database
[FaunaDB](https://fauna.com/) is the data API for client-serverless applications. It transforms the traditional DBMS into a Data API that gives you all of the capabilities of an old-guard database, without sacrificing flexibility, scale, and performance.

It has multiple APIs for data access, including native `GraphQL` and a DSL-like functional query language. We will be using GraphQL type for the testimonial app.

- First thing first, sign up using [this url](https://dashboard.fauna.com/accounts/register). Please select the free plan which is with generous daily usage quota and more than enough for our usage.
- Next, create a database by providing a database name of your choice. I have used `testimonial` as the database name.
 ![create_db.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1599731829629/5jNwTgtHM.png)
- After creating the database, we will be defining the `GraphQL` schema and import it into the database. 

 At this stage, let us create our project folder. Create a project folder somewhere on your hard drive with the name, `testimonial`. Create a file with the name, `testimonial.gql` with the following content,

  ```js
   type Message {
      text: String!
      rating: Int!
   }

   type Query {
      allMessages: [Message!]!
   }
  ```
 Note, we have defined a `Message` type which consists of a text, i.e, the testimonial message, and a rating. We also have a `Query` type which returns an array of messages.

 As a next step, upload the `testimonial.gql` file from the FaunaDB dashboard using the `IMPORT SCHEMA` button,
 ![import_schema.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1599731834840/DE4fw8AIT.png)
- That's all, our database has been created successfully and it is ready for use.

## Let us try some queries
Our testimonial app will allow us to create testimonials and fetch them all at a time. To support that, we will be using `mutation`(for creating, update and delete) and `query`(for fetch) of `GraphQL`.

- Create a testimonial with the text, "This is so cool" with a rating of 5.

  ```js
   mutation {
      createMessage(data: { text: "This is so cool", rating: 5 }) {
         _id
          text
          rating
       }
   }
  ```
 Try the above query in the GraphQL playground of FaunaDb to see the response,
  ![mutation_query.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1599731837652/Dqw1q3_gI.png)

 Great, a testimonial has been created. Let us try fetching all the testimonials. Here is the GraphQL query,

  ```js
   query {
      allMessages {
        data {
           _id
           text
           rating
        }
      }
   }
  ```
 Let us try the above query in the playground as before,
 ![fetch_query.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1599732021719/l4rpu8R0l.png)

That's cool, now we have a database with a schema and fully operational with creating and fetch functionality.

## Create a server secret key
Next, we need to create a secured server secret key to make sure the access to the database is authenticated and authorized. 

Click on the `SECURITY` option available in the FaunaDB interface to create the key,
![create_security_key.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1599732310872/wHZuuyPR1.png)

On successful creation of the key, you will be able to view it and make sure to copy and save it somewhere safe.

![key.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1599732670781/UcQy__fxU.png)

We do not want anyone else to know about this key. Not even to commit it to the source code repository. To maintain this secrecy, create an empty file called, `.env` at the root level of your project folder.

Edit the `.env` file and add the following line to it(paste the generated server key in the place of, `<your secret fauna key>`).

```shell
FAUNA_SERVER_SECRET=<your secret fauna key>
```
Add a `.gitignore` file and add the following content to it. This is to make sure, we do not commit the `.env` file to the source code repo accidentally.

```shell
node_modules
*.env
```

We are done with all that we had to do with the database setup. Let us move to the next phase to create `serverless` functions and APIs to access data from the `FaunaDB` database.

This is how the faunadb dashboard may look like based on your usage,
![faunadb_dashboard.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600073037975/jsyj1EV2e.png)

# Netlify - Functions and APIs
We have a server key now. Time to make use of it as an API key for invoking the `serverless` function. 

[Netlify](https://www.netlify.com/)  is a great platform to create hassle-free `serverless` functions. These functions can interact with databases, file-system, and, in-memory objects. 

[Netlify functions](https://www.netlify.com/products/functions/) are Powered by [AWS Lambda](https://aws.amazon.com/lambda/). Setting up AWS Lambdas on our own can be a fairly complex job. With Netlify, we will simply set a folder and drop our functions. Writing simple functions automatically become APIs.

- First, [create an account](https://app.netlify.com/signup) with `Netlify`. This is free and just like the `FaunaDB` free tier, Netlify is also very flexible.
- Now we need to install a few dependencies using either `npm` or `yarn`. Make sure, you have [nodejs](https://nodejs.org/en/download/) installed.
- Open a command prompt at the root of the project folder. Use the following command to Initialize the project with node dependencies,
  ```shell
  npm init
  ```
- Install the `netlify-cli` utility so that, we can run the serverless function locally.
  ```shell
  npm install netlify-cli -g
  ```
- Now we will install two important libraries, [axios](https://www.npmjs.com/package/axios) and [dotenv](https://www.npmjs.com/package/dotenv). `axios` will be used for making the HTTP calls and `dotenv` will help to load the `FAUNA_SERVER_SECRET` environment variable from the .env file into process.env.
  ```shell
  yarn add axios dotenv
  ```

## Create serverless functions
- Create a folder with the name, `functions` at the root of the project folder. We are going to keep all the serverless functions under it.
- Now create a sub-folder called, `utils` under the `functions` folder. Create a file called, `query.js` under the `utils` folder. We will need some common code to query the database for all the serverless functions. The common code will be in the `query.js` file.
- First we import the axios library functionality and load the `.env` file. Next, we export and async function that takes the `query` and `variables`. Inside the async function, we make calls using `axios` with the secret key. Finally, we return the response.

  ```js
   // query.js

   const axios = require("axios");
   require("dotenv").config();

   module.exports = async (query, variables) => {
     const result = await axios({
         url: "https://graphql.fauna.com/graphql",
         method: "POST",
         headers: {
             Authorization: `Bearer ${process.env.FAUNA_SERVER_SECRET}`
         },
         data: {
           query,
           variables
         }
    });

    return result.data;
   };
  ```
- Create a file with the name, `get-testimonials.js` under the `functions` folder. We will perform a query to fetch all the testimonial messages.

   ```js
   // get-testimonials.js

   const query = require("./utils/query");

   const GET_TESTIMONIALS = `
       query {
           allMessages {
             data {
                _id
                text
                rating
             }
           }
        }
   `;

    exports.handler = async () => {
       const { data, errors } = await query(GET_TESTIMONIALS);

       if (errors) {
          return {
            statusCode: 500,
            body: JSON.stringify(errors)
          };
       }

       return {
         statusCode: 200,
         body: JSON.stringify({ messages: data.allMessages.data })
       };
     };
   ```
- Time to test the `serverless function` like an API. We need to do a one time setup here. Open a command prompt at the root of the project folder and type,

  ```js
   netlify login
  ```
 This will open a browser tab and ask you to login and authorize access to your netlify account. Please click on the `Authorize` button.

 Create a file called, `netlify.toml` and add this content to it,
   
  ```shell
   [build]
      functions = "functions"

   [[redirects]]
     from = "/api/*"
     to = "/.netlify/functions/:splat"
     status = 200
  ```

 This is to tell netlify about the location of the functions we have written so that, it is known at the build time.

 Netlify automatically provides the APIs for the functions. The URL to access the API is in this form, `/.netlify/functions/get-all-testiminials` which may not be very user-friendly. We have written a `redirect` to make it like, `/api/get-all-testimonials`.
- Ok, we are done. Now in command prompt type,
  
  ```js
   netlify dev
  ```
 ![netlify_dev.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1599817881992/f_Dhzj6xY.png)

- Now the netlify dev server is running locally and you can access the first serverless function. Open a browser tab and try this url, `http://localhost:8888/api/get-testimonials`:
 ![get_testimonial_call.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1599818156660/cNBtEBpNy.png)

 Congratulations!!! You have got your first `serverless function` up and running.
 
 ![congrats.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1600014753029/qBhb7Y7iy.gif)

- Let us now write the next serverless function to create a testimonial. This is going to be simple. Create a file named, `create-testimonial.js` under the 'functions` folder. We need to write a query by passing the testimonial message text and rating.

  ```js
  // create-testimonial.js

  const query = require("./utils/query");

  const CREATE_TESTIMONIAL = `
    mutation($text: String!, $rating: Int!){
      createMessage(data: {text: $text, rating: $rating}){
        _id
        text
        rating
      }
    }
  `;

  exports.handler = async event => {
    const { text, rating } = JSON.parse(event.body);
    const { data, errors } = await query(
            CREATE_TESTIMONIAL, { 
  text, rating });

    if (errors) {
      return {
        statusCode: 500,
        body: JSON.stringify(errors)
      };
    }

    return {
      statusCode: 200,
      body: JSON.stringify({ testimonial: data.createMessage })
    };
  };
  ```
 What do you think? Yeah right, we have de-structured the payload to get the message text and rating. On getting those, we just call the `query` method to create a testimonial. Have you noticed, the query, in this case, is the `mutation` query?

 Alright, let us test it out. I shall be using the `postman` tool to test the API. You can use postman or any other tools of your choice,

 ![create_testimonial.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600009321310/NzgeQCiAd.png)

- Ok, let us quickly create another serverless function to update a testimonial. Create a file named, `update-testimonial.js` under the `functions` folder. We need to pass the `id` of the testimonial message we want to update along with the message itself, i.e, the `text` and the `rating`.

  ```js
  // update-testimonial.js

  const query = require("./utils/query");

  const UPDATE_TESTIMONIAL = `
      mutation($id: ID!, $text: String!, $rating: Int!){
          updateMessage(id: $id, data: {text: $text, rating: $rating}){
              _id
              text
              rating
          }
      }
  `;

  exports.handler = async event => {
    const { id, text, rating } = JSON.parse(event.body);
    const { data, errors } = await query(
         UPDATE_TESTIMONIAL, { id, text, rating });

    if (errors) {
      return {
        statusCode: 500,
        body: JSON.stringify(errors)
      };
    }

    return {
      statusCode: 200,
      body: JSON.stringify({ updatedMessage: 
  data.updateMessage })
    };
  };
  ```
 Let us test this API,

 ![update_testimonial.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600008625357/tdjCY8VWo.png)

- Great, now you have already guessed the `delete` part, isn't it? For deleting a testimonial, we just need the id of it. Here is the function for deleting a testimonial,

  ```js
  // delete-testimonial.js

  const query = require("./utils/query");

  const DELETE_TESTIMONIAL = `
    mutation($id: ID!) {
      deleteMessage(id: $id){
        _id
      }
    }
  `;

  exports.handler = async event => {
    const { id } = JSON.parse(event.body);
    const { data, errors } = await query(
          DELETE_TESTIMONIAL, { id });

    if (errors) {
      return {
        statusCode: 500,
        body: JSON.stringify(errors)
      };
    }

    return {
      statusCode: 200,
      body: JSON.stringify({ deletedMessage: data.deleteMessage 
     })
    };
  };
  ```
 Time to test it out,

 ![delete_testimonial.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600008770755/7xfhrbqa0.png)

At this stage, you should have a directory structure like this:

![dir_part_1.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1600009495156/cyeimazv3.png)

That's all for now. You have successfully created a database with the FaunaDB, set it up for use, created serverless functions using Netlify Functions, and tested them as APIs.

Congratulations, you did it and that's awesome!!!

# What's Next?
In the next article of the series, we will be using these APIs to develop the client-side of the testimonial app with Gatsby. Until then, keep exploring by forking the github repo. See you soon.

%[https://github.com/atapas/testimonial/tree/part-1-function-api-db]

<hr />
If it was useful to you, please Like/Share so that, it reaches others as well. To get an 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.

