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.
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/
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.
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.
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.
Figure 1.4: Create a HarperDB Cloud Instance
Next, please select the Create HarperDB Cloud Instance
as shown in the image below.
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.
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.
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.
Figure 1.8: Review the instance details and Add
You should see the instance creation getting started.
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.
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.
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
.
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.
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.
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!
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.
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,
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 thesecret
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 thebook
table of thelibrary
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 specifybooks
as the type name, Gatsby will create GraphQL queries asallBooks
andbooks
. 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.
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.
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,
- We import
React
,Styled-Components
, andgatsby
libraries. - Create Styled Components for the main page, container inside it, and each box to show the book information.
- Then, we start the
IndexPage
component. - 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 theuseStaticQuery
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. - 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,
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,
- Import all required libraries for the template to work.
- Create Styled Components for UI structure to show the book details.
- 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 thedata
from an object. But, from which object? - 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,
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.
Figure 6.1: Add a Site
Select the Import from a Git repository
option and click on the Next
button.
Figure 6.2: Select from a Git Repo
Assuming you have pushed your code to the GitHub repository, please select the GitHub
option.
Figure 6.3: Select Repository Type
Please provide the repository details and the site name and got to the next step.
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.
Figure 6.5: Skip CMS
In the last step, please provide the environment variable details and finish the setup.
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.
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,