loading...
Cover image for How to use HarperDB instance with React Hooks

How to use HarperDB instance with React Hooks

amanhimself profile image Aman Mittal Originally published at amanhimself.dev ・7 min read

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.

createdtime: to record the timestamp when data is inserted.
updatedtime: to record the timestamp when any data field is updated for the last time.

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:

  • loading where you can let the user know if the query is running the data is not yet loaded
  • error determines if there is an error when querying the data
  • refresh it is a function that allows fetching the data

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.

Discussion

pic
Editor guide
Collapse
andrewbaisden profile image
Andrew Baisden

Cool seeing HarperDB running straight on the client in React.

Collapse
sqlrob profile image
Robert Myers

This seems like an incredibly bad idea. Yeah, you removed DDL changes from the user, but most real apps are going to need row level permissions. You can't enforce that in the client, it has to be done at the server.

I think the only case I'd think about putting something like this in production is a read-only app with no sensitive information. Maybe something that could write (insert only, no update, no delete) if the writes were going to be vetted by something else later and not directly exposed to users.

Your example is such an app, but the permissions are still too wide. I'd give it 15 minutes before there was a Starbucks at 1600 Pennsylvania Ave or in R'Lyeh.

ETA: Or probably less time until there was one at "BUY <ED DRUG> AT <some url>"

Collapse
jacob_b_cohen profile image
Jacob Cohen

This looks to be a quick example application for those getting started with coding. You are correct that the some of the design patterns would not hold up in a production system, but that doesn't seem to be the point of this post.

Collapse
amanhimself profile image
Aman Mittal Author

I'd like to add, not every web app needs a design pattern where all CRUD operations are required or are necessary. I really love the auto refresh feature.

Thread Thread
sqlrob profile image
Robert Myers

Right. And those operations should be shut off at the server, not at the app. You've got db credentials, just because it's not in the app doesn't mean someone won't take advantage of it.

Never trust the client.

Collapse
margo_hdb profile image
Margo McCabe

Awesome tutorial!

Collapse
amanhimself profile image
Aman Mittal Author

Thanks Margo!