Create a serverless book library app with HarperDB and Gatsbyjs

Create a serverless book library app with HarperDB and Gatsbyjs

Learn how to create a serverless app using HarperDB & Gatsbyjs. In this step-by-step tutorial, we will learn to create a book library app and deploy.

One of the many advantages of trying out side-projects is, you never run out of the ideas of trying out something new. For example, recently, I had an opportunity to try out a modern, easy-to-use, fast database, HarperDB. As I learned how to set up and use it, I could further explore building a serverless app using it.

This article will learn how to create a serverless book library app using HarperDB and Gatsbyjs. We will also learn to deploy the app on the Gatsby Cloud. I hope you enjoy following along and build the app with me.

So, What are we going to learn?

We will learn to,

  • Setting up HarperDB.
  • Configure the Schema and Table in HarperDB.
  • Populate data in HarperDB.
  • Setting up a Gatsbyjs project.
  • Use a Gatsbyjs Plugin to access HarperDB data at the build time.
  • Build the user interfaces for the book library app. It's all with Reactjs.
  • Deploy the app on the Gatsby Cloud to access it publicly.

At any point in time, feel free to refer to the source code of the app from the GitHub repository,

Here goes a quick demo of the app that we will build in the next 12-15 minutes.

Here is the demo of the library app. Please feel free to access and use.

https://flicks.gatsbyjs.io/

Prerequisites

You need a couple of prerequisites,

  • You must have Node.js installed. Please make sure you have installed Node.js version >= 12.13.0. You can download and install Node.js from here. You can check the version of the existing Node.js installation using the command, node -v.
  • Knowledge of Reactjs would be helpful as Gatsbyjs is React-based.

Before we begin, What is serverless anyway?

There is a high chance that you may have a couple of doubts as I had about the term serverless.

  • Does serverless mean there are no servers involved at all in the app development?
  • Are we talking about the Serverless Framework by any chance?

Nope, both of them are not true in the context of this article. A server exists to provide services. It could be e-mail, form, hosting, and even database. By serverless it doesn't mean there are no servers involved in the app development. It instead means we as developers do not set up, manage, and maintain these servers. Instead, we leverage the services made available and managed by providers like Google, Amazon, Netlify, Vercel, Gatsby, HarperDB, and many more.

Coming to the second point above, the Serverless Framework is a service that helps us to go serverless. However, we will not use it in this article.

HarperDB

HarperDB is a fast, flexible database that allows you to perform rapid application development, distributed computing, SaaS, and many more. To set up HarperDB in a serverless way, we need to configure a cloud instance. But the first thing first, let's create an account with HarperDB.

Setting Up HarperDB

Please browse to https://harperdb.io/ and create an account for free. Please click on the link Start Free as shown below. If you have an account already, please sign in using this link, https://studio.harperdb.io/

Create a Free Account Figure 1.1: Create a Free Account

As part of the signup process, you need to provide the details like name, email, subdomain name. HarperDB will now create a subdomain for you. So please provide the details and sign up for free.

Specify Details to Sign Up Figure 1.2: Specify Details to Sign Up

In the next step, you need to provide an account password. Please provide a strong password and complete the account creation process.

Account Password Figure 1.3: Specify the Account Password

Now, let's create a HarperDB cloud instance. We will use this cloud instance to create and fetch data for our application. Please click on the section Create New HarperDB Cloud Instance to move to the next step.

Create a HarperDB Cloud Instance Figure 1.4: Create a HarperDB Cloud Instance

Next, please select the Create HarperDB Cloud Instance as shown in the image below.

HarperDB Cloud Instance Figure 1.5: Create HarperDB Cloud Instance

Now we have to specify the cloud instance name and credentials. Please provide an instance name of your choice along with the credentials.

Credentials Figure 1.6: Specify Instance Name and Credentials.

Next, you need to select the RAM size, storage size, and other spec details. Please select all the free options.

7.png Figure 1.7: Select the specs

The last step is to confirm and add the HarperDB cloud instance. Again, please review the details and click the Add Instance button.

8.png Figure 1.8: Review the instance details and Add

You should see the instance creation getting started.

9.png Figure 1.9: Creating Instance is In-Progress

It may take a few minutes. However, you should see the status as OK after a successful HarperDB cloud instance creation.

10.png Figure 1.10: Status OK

A final step. Please go to the config tab and copy the API Auth Header Key. Please preserve it somewhere, as we will use it when we configure Gatsbyjs with HarperDB.

HarperDB_config.png Figure 1.11: HarperDB API Auth Header Key

That's all. We have successfully created a HarperDB cloud instance that is ready to use.

Configure the Schema and Table

We need to create a schema and table to insert a few records into the DB. To do that, load the HarperDB cloud instance from the dashboard. First, create a schema by specifying a schema name. For our app, let's give a schema name as library.

11.png Figure 2.1: Create a Schema

Next, let's specify a table name. Let's specify book as the table name and create. Please note, you have to specify a hash_attribute for the table. HarperDB will auto-generate the value for it. You may manually add it if you want to specify its value. In our case, we will let HarperDB create it. Let's specify the id column as the hash_attribute for the book table.

12.png Figure 2.2: Create a Table

Populate data in HarperDB

We will now populate data in HarperDB. We will insert a few records of books into the book table using the HarperDB user interface. You can insert one record by specifying a JSON object or multiple records at once by specifying an array of JSON objects. Let us create a book record by specifying these properties and values,

{
  author: [
    'Kyle Simpson'
  ],
  cover: 'https://res.cloudinary.com/atapas/image/upload/v1622356611/book-covers/you_dont_know_js_1_le1xk5.jpg',
  description: 'No matter how much experience you have with JavaScript, odds are you don’t fully understand the language. As part of the series, this compact guide focuses on new features available in ECMAScript 6 (ES6), the latest version of the standard upon which JavaScript is built.',
  isbn: 9781491904244,
  pages: 278,
  published: '2015-12-27T00:00:00.000Z',
  publisher: 'O\'Reilly Media',
  rating: 5,
  subtitle: 'ES6 & Beyond. It covers all aspects of javaScript deep down.',
  title: 'You Don\'t Know JS',
  topic: 'JavaScript',
  website: 'https://github.com/getify/You-Dont-Know-JS/tree/master/es6%20&%20beyond'
}

Click on the save icon to save the record.

13.png Figure 3.1: Insert a book record

Similarly, you can insert multiple records. So please insert a few more records as the book library must contain more than just one book!

14.png Figure 3.2: Book Records

You can make use of the JSON data from my GitHub Repository to create multiple records.

Congratulations 🎉 !!! You have completed the database setup with the required data. Now, we will move our focus towards building the User Interface for the book library app.

Gatsbyjs(aka Gatsby)

Gatsby is a React-based framework that allows you to build fast, secure, and robust websites. You can create markups with data at the build time and deploy the built artifacts to serve your pages much faster. It gives a phenomenal performance and speed improvement over the traditional client-server model. We will use Gatsby to create prebuilt markups using the data record added to the HarperDB.

Gatsby Plugin for HarperDB

Gatsby's massive plugin ecosystem allows us to pull data from several data sources, avail themes, and many more use-cases. Unfortunately, I couldn't find any existing Gatsby plugin for the HarperDB data source. But, as they say, necessity is the mother and all inventions, I thought of creating one!

So now we have a Gatsby Plugin for HarperDB (gatsby-source-harperdb) that allows you to use the HarperDB as the data source so that a Gatsby project can pull the data at the build time. You can find the source code of the plugin from here,

It is also available as an official plugin to install from the Gatsby's plugin page. So please give it a try.

Setting up a Gatsby Project

Alright, it's time to create our gatsby project. First, install the gatsby command-line interface(CLI) tool. Open a command prompt and use this command to install it globally,

npm install gatsby-cli -g

We will use the latest gatsby version 3.x to bootstrap our project. Gatsby provides many starter projects to get started with the development faster. Now let's create a gatsby project. Try this command from the command prompt,

gatsby new

It will ask you a few questions like the site name, what kind of CSS library support you need, what are plugins you want to install? The image below shows my answers. You can choose to go with the same or anything else suitable for you.

gatsby_new.png Figure 4.1: Creating a Gatsby Project

Please give it a while to complete the installation and set up the project for you. Once done, you should see a folder created with the site name provided by you in the last step. For me, it is flicks. Please change to the project directory and try this command from the command line,

gatsby develop

It will run the project in the development mode. First, it will build the project and then host the prebuilt markups from a server locally to access the app. By default, the app will run on the URL, http://localhost:8000. Please open a browser window/tab and try the URL. You should see your app up and running,

gatsby_8.png Figure 4.2: The Initial App

Configure HarperDB with our Gatsby App

Let's configure the HarperDB with our Gatsby app. We will use the gatsby-source-harperdb plugin. Please install it using the following command,

npm install gatsby-source-harperdb

# If you are using yarn, try this,
# yarn add gatsby-source-harperdb

Now create a file called .env at the root of the project folder. Please specify the API auth header key and the instance URL in the .env file.

HARPER_DB_SECRET_KEY=API_KEY_VALUE
HARPER_DB_URL=CLOUD_INSTANCE_VALUE

Please replace the API_KEY_VALUE with the API auth header key we copied before. Also, replace the CLOUD_INSTANCE_VALUE with your cloud instance value. The URL ends with .harperdbcloud.com.

Please make sure you add an entry of .env in your .gitignore file. You may not want to commit and push this file to your GitHub repository.

We need to install the dotenv package to read environment variables from the .env file. You can install it using this command,

npm install dotenv

Now open the gatsby-config.js file at the root of the project folder. It is a file to configure all the gatsby plugins required for the project. You may find a few plugin entries already. We have installed those while creating the project. Add this line at the top of the gatsby-config.js file,

require('dotenv').config();

Next, please add the configuration for the gatsby-source-harperdb in the config file.

plugins: [
     ....
    {
      resolve: `gatsby-source-harperdb`,
      options: {
        secret: process.env.HARPER_DB_SECRET_KEY,
        url: process.env.HARPER_DB_URL,
        payload: {
          "operation": "sql",
          "sql":"SELECT * FROM library.book"
        },
        type: "books"
      },
    },
  ],

Please note the options in the above configuration,

  • We read the API Key from the .env file and use it for the secret value.
  • Similarly, we get the HarperDB cloud instance URL from the .env file and use it in the configuration.
  • Next is the payload that we use to query HarperDB. Here we are specifying the SQL query to retrieve the data from the book table of the library schema.
  • Last, specify the value of the type property. It can be any string of your choice. It is the name under which your data will appear in Gatsby GraphQL queries. For example, if we specify books as the type name, Gatsby will create GraphQL queries as allBooks and books. We will see that in a while.

If you are running the gatsby develop already, please stop it(using the control + c key combination) and start again.

Fetch the Book records in the UI

Gatsby source plugins make the data available to query using GraphQL queries. In addition, it provides a GraphQL playground for us to try out the queries before we use them in the app. To Open the GraphQL playground and query the book data from the HarperDB, please open this URL in your browser tab: http://localhost:8000/___graphql. You should see the allBooks and books types under the explorer.

GraphQL Explorer Figure 5.1: GraphQL Explorer

Now expand the allBooks type from the explorer. Then expand the nodes node and select the attributes to query. As you select, you will see the query is getting built automatically. Now, execute the query using the Execute Query button at the top. You will see the result of the query at the rightmost pane. Please refer to the image below.

GraphQL Query Figure 5.2: GraphQL Query Execution

We will now use this query in our UI code(React components) to build the user interfaces.

Build the User Interfaces(UI)

Now we will build the user interfaces using this query to show the books in the UI. In the UI, we will first list all the books with details like title, topic, cover, author, subtitle. Then, when users click on any of the books, we take them to a details page to show more details about that book.

Create the Book Listing Page

Let us create the book listing page. Open the index.js file under the src/pages folder. Replace the content of the file with the following content,


// 1. Import React, Styled-Components, and gatsby
import * as React from "react";
import styled from "styled-components";
import { useStaticQuery, graphql } from "gatsby";

// 2. Create Styled Components
const Main = styled.div`
    display: flex;
    flex-direction: column;
`;

const Container = styled.div`
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
`;

const Book = styled.div`
    border: 1px solid #4e4e4e;
    border-radius: 4px;
    padding: 0.5rem;
    width: 250px;
    min-height: 450px;
    margin: 1rem;
`;

const ResponsiveImage = styled.img`
    width: 100%;
    height: auto;
`;

const Title = styled.span`
    font-size: 20px;
    font-weight: 500;
`;

// 3. The IndexPage Component
const IndexPage = () => {
    // 4. Query the data using GraphQL query
    const data = useStaticQuery(graphql`
        {
            allBooks {
                nodes {
                    title
                    id
                    author
                    cover
                    rating
                    subtitle
                    topic
                }
            }
        }
    `);
    const books = data.allBooks.nodes;
    books.sort((a, b) => {
        return b.rating - a.rating;
    });

    // 5. The Markup to render with the data
    return (
        <Main>
            <Container>
                {books.map((book, index) => (
                    <Book key={index}>
                        {book.topic}
                        <div>
                            {book.cover && (
                                <ResponsiveImage
                                    src={book.cover}
                                    alt={`${book.title}`}
                                />
                            )}
                        </div>
                        <Title>{book.title}</Title> by{" "}
                        <span>{book.author.join(", ")}</span>
                        <p>{book.subtitle}</p>
                        <p>{book.rating}</p>
                    </Book>
                ))}
            </Container>
        </Main>
    );
};

export default IndexPage;

Let us go over the code above and understand. It is a regular React component where,

  1. We import React, Styled-Components, and gatsby libraries.
  2. Create Styled Components for the main page, container inside it, and each box to show the book information.
  3. Then, we start the IndexPage component.
  4. In the component, we use the GraphQL query to fetch the books data. We fetch only the required attributes for the listing page. Please notice we use the useStaticQuery hook from gatsby to perform the fetch. Gatsby recommends this hook to fetch data using the GarphQL queries inside a Gatsby component. We also sort the books based on the rating.
  5. Last, we have the markup to render using the data.

Now refresh the page where the app is running. You will see a list of books with details like the image below,

Book Listing Page Figure 5.2.1: The Book Listing Page

Create the Book Details Page

Great! Let us now implement the book details page. It will show a book's details when the user clicks on book information from the listing page. Gatsby provides a super cool feature of creating pages ahead in time(build time) using templates. So, we can create a single template for all the book details as we will show a similar structure for all the books.

Create a folder called templates under the src folder. Now create a file called BookDetails.js under src\templates with the following content.


// 1. Import required libraries
import React from "react";
import styled from "styled-components";
import { graphql } from "gatsby";

// Create the Styled Components
const Container = styled.div`
    display: flex;
    flex-direction: column;
    align-items: center;
`;

const AnchorButton = styled.a`
    display: block;
    width: auto;
    height: 25px;
    background: #00ebff;
    padding: 10px;
    text-align: center;
    border-radius: 5px;
    color: #000000;
    font-weight: bold;
    line-height: 25px;
    text-transform: uppercase;
    &:hover {
        color: #2e2222;
    }
`;

// 3. The BookDetails Component
const BookDetails = ({ data }) => {
    const details = data.books;

    return (
        <Container>
            <h1>{details.title}</h1>
            <p>
                <span>By</span> {}{" "}
                <span style={{ fontSize: "20px" }}>
                    <b>{details.author.join(", ")}</b>
                </span>
            </p>
            <img
                src={details.cover}
                alt={details.title}
                width="40%"
                height="auto"
            />

            Rating: {details.rating}

            <p style={{ fontSize: "20px" }}>
                <span>
                    <b>Pages</b>: {details.pages}
                </span>{" "}
                {" | "}
                <span>
                    <b>Publisher</b>: {details.publisher}
                </span>{" "}
                {" | "}
                <span>
                    <b>ISBN</b>: {details.isbn}
                </span>
            </p>

            <p style={{ fontSize: "25px" }}>{details.description}</p>

            <AnchorButton
                href={details.website}
                target="_blank"
                rel="noreferrer"
            >
                Go to the Website
            </AnchorButton>
        </Container>
    );
};

// 4. Gatsby Page/Template Query
export const query = graphql`
    query ($title: String!) {
        books(title: { eq: $title }) {
            author
            cover
            description
            id
            isbn
            pages
            published
            publisher
            rating
            subtitle
            title
            topic
            website
        }
    }
`;

export default BookDetails;

We are doing the followings in the template code above,

  1. Import all required libraries for the template to work.
  2. Create Styled Components for UI structure to show the book details.
  3. Then, we create the BookDetails React component and render the book details. Please note, we pass a prop to the component as { data }. It means we are performing destructuring here to extract the data from an object. But, from which object?
  4. In a gatsby project, pages and templates use the result of a query as the prop. Please note the GraphQL query at the bottom of the source code. Here we are performing a filter query to filter out a book by its title. The result of this query will be passed automatically to the BookDetails component as a prop. We extract the data from that and use it for the rendering.

Now, as we have the template ready, we need to use it to create the pages for each of the books. Let's configure that.

Create a file called gatsby-node.js at the root of the project folder with the following content. It is a special file that helps in invoking Gatsby APIs and overrides them to customize things.

const path = require(`path`);
const _ = require("lodash");

exports.createPages = async ({ graphql, actions }) => {
    const { createPage } = actions;
    const result = await graphql(`
        query {
            allBooks {
                edges {
                    node {
                        title
                    }
                }
            }
        }
    `);

    result.data.allBooks.edges.forEach((edge) => {
        createPage({
            path: `/${_.kebabCase(edge.node.title)}/`,
            component: path.resolve(`./src/templates/BookDetails.js`),
            context: {
                // Data passed to context is available
                // in page queries as GraphQL variables.
                title: edge.node.title,
            },
        });
    });
};

Here we are using the createPages API of Gatsby to create pages based on the query. First, the query fetches all the book titles. Then, it creates a page using the template for each of the titles and passes the title as a context. Each page will be accessed using a unique URL we build with the path attribute. Also, if you recall, we use the title in the template to filter the book information.

Please note, we use the lodash library to utilize its method for formatting a URL fragment. Usually, a title may have spaces, and the URL doesn't accept that. So we use the _.kebabCase(edge.node.title) to replace the spaces with a hyphen(-) character. Please install lodash as a dependency.

npm install lodash

Now restart gatsby develop once more. Next time when the gatsby develop runs, it will create the pages for all the books using the template.

So let us now create a link from each of the books on the book listing page to its respective details page. Please open the index.js file. Include these two imports at the top,

// ... Other imports

import _ from "lodash";
import { Link } from "gatsby";

Then, wrap the <Book> tag using the Link like this,

{books.map((book, index) => (
    <Link to={`/${_.kebabCase(book.title)}`}>
        <Book key={index}>
                ... All the code as previous
        </Book>
    </Link>
  ))}

Please note the to attribute of the Link. It links you to the URL fragment created using the title the same way we mapped the pages in the gatsby-node.js file. Save your changes and refresh the app in the browser. Now, you will be able to click on the books from the listing page. Click on a book, and you should land on a details page like the one shown below,

image.png Figure 5.2.2: Book Details Page

That's it. We have the book library app ready with basic functionality. Please feel free to enhance the look and feel of the app using styles. You can add features like searching a book, filtering, many more.

Deploying on Gatsby Cloud

Welcome to the last section of the article. Now we will deploy the library app to the Gatsby Cloud with a few quick steps. At this stage, please create a repository on GitHub and push all the code.

Please Create an account with Gatsby Cloud and Log in. Please select the free build and hosting plans while creating the account. Next, click on the Add a site button.

Add a Site Figure 6.1: Add a Site

Select the Import from a Git repository option and click on the Next button.

Git Repo Figure 6.2: Select from a Git Repo

Assuming you have pushed your code to the GitHub repository, please select the GitHub option.

Select Repository Type Figure 6.3: Select Repository Type

Please provide the repository details and the site name and got to the next step.

Repo Details Figure 6.4: Provide Repository Details

We will not use any CMS for our application. Therefore, you can skip the step of selecting the CMS.

Skip CMS Figure 6.5: Skip CMS

In the last step, please provide the environment variable details and finish the setup.

ENV Details Figure 6.6: Provide Environment Details

Now a build should get triggered automatically. Once the build is successful, the app will be available at https://<YOUR_SITE_NAME>.gatsbyjs.io. In my case, it is flicks.gatsbyjs.io.

Trigger Build Figure 6.7: Trigger Build

That's all. We have come to the end of this article. I hope you found it insightful. Thanks for reading and trying out. Please feel free to comment below with the link to your app. Also, feel free to reach out to me if you face any issues in following the article.


I hope you enjoyed this article or found it helpful. Let's connect. You can find me on Twitter(@tapasadhikary), sharing thoughts, tips, and code practices. Please hit the Subscribe button at the top of the page to get an email notification on my latest posts.

You may also like,

Did you find this article valuable?

Support Tapas Adhikary by becoming a sponsor. Any amount is appreciated!