Skip to content

How to use HarperDB instance with React Hooks

Published:

8 min read

cover

HarperDB Cloud is a database service that supports both SQL and NoSQL queries for CRUD operations. It needs minimal configuration to get started and its realm is not only limited to build REST APIs with a server-side technology such as Node.js.

HarperDB offers a way to access the database cloud instance directly in your client-side application. It has a built-in HTTP API that allows us to query data directly.

In this post, let’s take a look at how we configure a HarperDB Cloud database instance and fetch the data by querying the database within a Reactjs app. HarperDB provides a React Hook, known as useHarperDB to make our jobs easier.

HarperDB instance glossary

This post is going to use a similar instance of the HarperDB database that we built using the REST API approach with Node.js in our previous post. Please take a look at the post on how to set up the cloud instance here. This way you will have a populated table of data fields.

The schema in the above database instance is called dev. A schema in HarperDB is necessary. It is equivalent to a collection of tables. Without an existing schema you cannot create a new table and without a table, you cannot add or update data in the HarperDB instance.

Below the schema, there is an option to add one or more tables. In the above instance, there is a table already created and is called outlets. This table has a unique identifier id for each row of data.

On the right-hand side, you will find the data inside the table. Do notice the two timestamp fields. They are auto inserted by HarperDB whenever a new row adds to the table and is auto-maintained.

ss0

Once you have set up the cloud instance, make sure to use a React project with the library’s version 16.8.0+.

Getting started

Start by creating a new React app. We are going to use this React app for building the example app in this post. Open up a terminal window and execute the following command:

npx create-react-app harperdb-integration-react

# after the project directory has been created
cd harperdb-integration-react

# install the following dependency
yarn add use-harperdb@0.1.2

Now you can start the development server to see the React app in action. Go to the terminal window and execute the command yarn start. You are going to get the following output in a browser window at URL: http://localhost:3000/.

ss1

Creating a user with custom roles

By default, the user created in the HarperDB Cloud instance is a superuser. It has admin rights to query and add data and rights to create and drop the table from the existing schema as well as create and drop new schemas. In a client-side application, we do not want to have a superuser. Since most client apps are public, this is never a good approach to use the default user.

The approach to resolve this is to create a new user that has the rights to only perform CRUD operations inside a data table. HarperDB provides a way to define custom user roles as well as create a new user using that role.

Start by opening your cloud instance and go to the tab roles from the menu bar.

ss2

Here you can define a new standard role to create a custom one. Let’s this new role, client_user.

ss3

Once the new role is created, the instance prompts with the permissions we can assign to any user with this role. You are now allowed to configure the access to tables and schemas for this specific role. There is only one schema right now and inside it, there is only one data table. For this example, let’s keep the default configuration and proceed by pressing the button Update Role Permissions.

ss4

Once the permissions are updated, go to the tab users next to the roles in the menu bar. This is used to add a new user with the custom role just created. From the drop-down menu, select the role client_user.

ss5

Click the Add user button to add the new user.

ss6

Now, we can use this custom user in the client React app to query the data from the table outlets.

Integrating HarperDB in a React

The use-harperdb hook comes with a HarperDBProvider that is used to wrap the instance of the React app or the App component in general inside the index.js file. This is mandatory to execute a CRUD operation on the database instance.

Add the following import statement inside src/index.js file:

// ... other import statements
import { HarperDBProvider } from "use-harperdb";

To execute any CRUD operation on the database instance, the provider requires the db instance URL, the username, and the password associated with that user. We are going to make use of environmental variables to add these values.

Create a new file called .env at the root of the React project with the following variables. The values of these variables are mentioned as a description in [] but you must replace the square brackets and your own values.

REACT_APP_DB_URL=[Your Cloud Instance Provider URL]
REACT_APP_USER=[The name of the custom user with client_user role]
REACT_APP_PASSWORD=[The password associated with the custom user]

Create React App reads any environmental variables that are prefixed with REACT_APP. Instead of using a third-party library, we can directly use these environmental variables to provide necessary attributes to HarperDBProvider.

ReactDOM.render(
  <React.StrictMode>
    <HarperDBProvider
      url={process.env.REACT_APP_DB_URL}
      user={process.env.REACT_APP_USER}
      password={process.env.REACT_APP_PASSWORD}
    >
      <App />
    </HarperDBProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

That’s it. We have successfully configured the React app to use the HarperDB database instance.

Querying the data from HarperDB

To query the data from the database instance in the React app, the useHarperDB hook provides elements to do so.

Open the App.js file and import the hook from use-harperdb. Let’s also set up a basic UI to display data when fetched.

import React from "react";
import "./App.css";
import { useHarperDB } from "use-harperdb";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h2>Starbucks Outlets</h2>
        <table>
          <thead style={{ marginBottom: "20px" }}>
            <tr>
              <td style={{ textTransform: "uppercase" }}>City</td>
              <td style={{ textTransform: "uppercase" }}>Name</td>
              <td style={{ textTransform: "uppercase" }}>Latitude</td>
              <td style={{ textTransform: "uppercase" }}>Longitude</td>
            </tr>
          </thead>
          <tbody>
            <tr>{/* TODO: display data from query */}</tr>
          </tbody>
        </table>
      </header>
    </div>
  );
}

export default App;

The rule for using any hook is that they can only be consumed inside a functional component. The useHarperDB hook gives an array with elements to query the data which is returned as an array. It also provides:

To query the data from the database instance, pass the object inside the hook.

Add the following snippet query in the App.js file to fetch all the data from the database.

function App() {
  const [data, loading, error, refresh] = useHarperDB({
    query: { operation: "sql", sql: "select * from dev.outlets" },
    // interval: 5000
  });

  //...
}

In the above snippet, the second property of interval passed is optional to use when you don’t want to explicitly use the refresh function. It accepts a value in milliseconds.

Let’s add some JSX for loading and error by using if/else syntax inside the App component.

function App() {
  // ...

  if (loading) {
    return <div>Loading ...</div>;
  }

  if (error && data.length === 0) {
    return <div>Error, no data found</div>;
  }

  return (
    <div className="App">
      <header className="App-header">
        <h2>Starbucks Outlets</h2>
        <table>
          <thead style={{ marginBottom: "20px" }}>
            <tr>
              <td style={{ textTransform: "uppercase" }}>City</td>
              <td style={{ textTransform: "uppercase" }}>Name</td>
              <td style={{ textTransform: "uppercase" }}>Latitude</td>
              <td style={{ textTransform: "uppercase" }}>Longitude</td>
            </tr>
          </thead>
          <tbody>
            <tr>{/* TODO: display data from query */}</tr>
          </tbody>
        </table>
      </header>
    </div>
  );
}

For a very brief moment, you may see the loading message being displayed.

ss7

Also, if you are passing interval as the second property, you may notice that after every 5 seconds, the React app automatically refreshes the web page. To stop this behavior, you can remove the interval property.

Now using the JavaScript’s map function, let’s map over the array of data and display the contents. If data is fetched that means it exists and we can easily map over the array. Add the following code snippet in place of the comment.

<tbody>
  {data &&
    data.map((item, index) => (
      <tr key={index}>
        <td>{item.title}</td>
        <td>{item.author}</td>
      </tr>
    ))}
</tbody>

Go back to the browser window and you will get the following result:

ss8

The query can also be made using the useHarperdb hook with only the data array. Add the following query to fetch the total number of outlets or records from the database.

const [data2] = useHarperDB({
  query: {
    operation: "sql",
    sql: "select count(*) as totalOutlets from dev.outlets",
  },
});

To display the total count, add the modify the JSX rendered from App component:

// ...
<h2>Starbucks Outlets</h2>
<p>Total Outlets: {data2 && data2[0].totalOutlets}</p>
// ...

Here is the output after this step:

ss9

Conclusion

Hooks are a great addition in the React world and they certainly help us write less code. The objective of this post was to introduce you to the useHarperDB hook and how to integrate it into a React app to fetch the data from a database hosted in the cloud.

Resources & Further Reading:

I'm a software developer and a technical writer. On this blog, I write about my learnings in software development and technical writing.

Currently, working as a documentation lead at Expo.