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

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
I have 🍵, 🎵 and 🖥️ but no 🕗...
But I'll definitely follow this series till the end and bookmark all of them so I can 👩💻 whenever I'm ready! Sorry and thank you for writing such fantastic articles!
Haha... Sure..
Thank you for noticing it.. Looking forward to get your feedback, whenever you get a chance to try out!
Skay Surely you will..
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!!
Let's Continue... 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, Creating an account ...
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 a brand new series called, Go Serverless. 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,
This article is Part 1 of the series. Hope you enjoy the journey!
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!
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:
But wait, there are few important aspects,
serverless.Here is the preview of the testimonial app,

If you want to get to the code first, here is the link:
Alright, let's get started!
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 JavaScript, reusable APIs, and prebuilt Markups.
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.
FaunaDB 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.
testimonial as the database name.

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

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

Great, a testimonial has been created. Let us try fetching all the testimonials. Here is the GraphQL query,
query {
allMessages {
data {
_id
text
rating
}
}
}
Let us try the above query in the playground as before,

That's cool, now we have a database with a schema and fully operational with creating and fetch functionality.
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,

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

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>).
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.
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,

We have a server key now. Time to make use of it as an API key for invoking the serverless function.
Netlify is a great platform to create hassle-free serverless functions. These functions can interact with databases, file-system, and, in-memory objects.
Netlify functions are Powered by AWS 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.
Netlify. This is free and just like the FaunaDB free tier, Netlify is also very flexible.npm or yarn. Make sure, you have nodejs installed.npm init
netlify-cli utility so that, we can run the serverless function locally.npm install netlify-cli -g
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.yarn add axios dotenv
functions at the root of the project folder. We are going to keep all the serverless functions under it.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.
// 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.
// 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,
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,
[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,
netlify dev

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:

Congratulations!!! You have got your first serverless function up and running.

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.
// 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,

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.
// 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,

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,
// 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,

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

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!!!
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.
Follow me on Twitter @tapasadhikary for more updates.