Let’s Build a Note-Taking App With React and FaunaDB

Here’s how you can use React with FaunaDB to create modern, dynamic single-page applications (SPAs)

Indrek Lasn
Jan 10 · 11 min read
What we’ll be building — a note-taking app

It’s almost 2020, and there are many ways you can build your web applications. We’ll be looking into how you can use React and FaunaDB to build fast, responsive, and fun-to-work-with applications.

Developing applications should be fun. FaunaDB and React let us just do that.


What We’re Building

In order to take notes, we need to implement the CRUD (create, read, update, delete) functionality, which is the basis of all modern applications.

How React and FaunaDB work together visualized

We want to have an architecture that lets our users create new notes, edit existing notes, and delete and fetch the notes from the database as well.

Speed, correctness, and security are everything, thus we need to be able to interact with our database directly from the browser and ensure consistent writes and reads to the database (so our notes are stored in order) — e.g., guaranteed transactions.

This all makes FaunaDB and React an attractive choice.


Why FaunaDB

Instead of spending hours on database setup and support maintenance, FaunaDB takes care of all that quite nicely. The less we have to think about scaffolding, the more we can focus on actually building the application.

We don’t have to think about consistency, scale, replication, redundancy, etc. — FaunaDB takes care of all of that. Instead of dealing with the database scaffolding and setup process, we can focus on the core application instead.

FaunaDB uses the Calvin protocol to maintain several consistent and full copies of the data, called replicas, with the ability to both read and write on every node. A replica consists of several geographically aware nodes, each with a partition of the full dataset in a single local environment.

FaunaDB also offers support for authentication via secure access keys, attribute-based access controls, native instance-level permissions, TLS/SSL for the database and clients, client/tenant separation via database hierarchies, priority workloads, as well as secure-access tokens for direct client access to the database. FaunaDB clusters require authentication and can’t be left accidentally unprotected.

Note: In case you’d like to learn more, I wrote about an extensive article about why React and FaunaDB are a good choice.


Why React

I’ve been a React user for over five years by now. As a side note, I think there are multiple good solutions out there, such as Vue, Angular, Svelte, etc. At the end of the day, you shouldn’t care about which library to pick, as long as the job gets done.

Without further ado, let’s get started!


Getting Started

Prerequisites


create-react-app

npx create-react-app react-faunadb-note-app && cd react-faunadb-note-app && start
Starting our React app

Now that we have the project set up, let’s create our folders. We’re going to need the following directories;

  • config — place all database related logic here
  • components — React-related stuff
  • api — methods for fetching, editing, and deleting notes
Creating directories with the mkdir command

Note: If you’re not too familiar with the terminal, I recommend reading through “Here Are 11 Console Commands Every Developer Should Know.”


Setting Up FaunaDB

Signing up for Fauna

Once you have an account and are signed in, create a database — I called my database notes_app.

FaunaDB interface console

Great! Once we have the database, create an access key. This is used for connection authorization to the database. We want to control who can read and write to the database, and the key will allow us that. But before we can do that, let’s create a new role for our database.

Find the roles option here: console → app → security → roles → new role

Creating a FaunaDB role

Next, go ahead and create the database key. We’ll be needing the key to authorize access to the database.

console → app → security → keys → new key

Creating a new key

Once you press save, you should see the FaunaDB key.

FaunaDB

Something to keep in mind is the key has admin privileges, which means if this key is leaked, it’s a big security flaw.

You might want to start out with a less powerful key. Something to consider.

Optional: Creating a key with the Server role instead

Copy the key to your text editor or somewhere safe — we’re going to need it later.

We’re going to use FaunaDB as a document-store database. There are other options as well — check out the options here. Since we’re already at the console, let’s create the notes collection. A collection is a group of documents. Each note is a document, in this case.

Creating a FaunaDB collection — this is where we’re going to store our notes

Once we created a collection, FaunaDB will index the collection by default.

all_notes index for the notes collection

Indexes are storage structures maintained by the database system to facilitate efficient retrieval of subsets of the data. Effective schema design in FaunaDB takes advantage of both the first-class support for relational modeling and materialized indexes.

If you’re interested in learning more about indexing queries, check out “Index Queries in FaunaDB.”


Connecting React to a FaunaDB Instance

Installing dependencies

  • faunadb — the FaunaDB JavaScript driver for FaunaDB
  • antd — an enterprise-class UI design language and React UI library
  • react-toastify — React notifications made easy
Installing dependencies

Creating the .env File

Creating .env files

Go ahead and add the .env file to the .gitignore. The .env.example should be pushed into the repository without the values.

Adding the FaunaDB key to our .env file

Make sure to replace the YOURKEYHERE with the FaunaDB key. Go ahead and restart the server. By doing this, we now have access to the FaunaDB key via the process.env.REACT_APP_FAUNADB_KEY value.

Your project architecture should look like the following now:

Our current React and FaunaDB project architecture

Making the Initial Connection to the FaunaDB

src/config/db.js — we connect to the FaunaDB and export the instances

After making the connection, we can start querying our FaunaDB. Remember the api directory? This is where we’re going to place all query-related logic. This way, we can simply call something like fetchAllNotes() inside our React component.

We’re doing this since we want to separate concerns and make our React components easy to read and understand.


Writing FaunaDB Queries

Fetching all notes

src/api/getAllNotes.js

It might seem like a lot, but it’s relatively simple. Here’s what the code above does:

  • Imports the FaunaDB database and query instance
  • Queries all the notes with the notes index. This query returns all refs, which we map over and return the results.
  • In the case of network errors, we catch the error and log out a warning. This is handy when we want to give out feedback to users.

Notice we’re using asynchronous promises. If this concept is new to you, check out “How to Improve Your Asynchronous JavaScript Code With Async and Await.” I explain promises there.


Editing a Note

src/api/editNote.js

Did you catch the minor detail?

We’re wrapping our query with a function that takes two arguments, namely the noteId we’re editing and the new note text.


Deleting a Note


Creating a Note

Notice FaunaDB has logically named its queries, such as q.Create, q.Delete, q.Update, etc.


Exporting the Queries

Importing and exporting our queries

Instead of writing four lines of import code, the code above lets us do the following in one single line.

import { getAllNotes, deleteNote, editNote, createNote } from ‘./api’

If you ever got lost, here’s the source code.


Calling the FaunaDB queries with React

App.js

We’re:

  • Importing our FaunaDB queries from the api directory
  • Creating a state for the notes with the useState Hook. If you’re unsure about React Hooks, check out the “Demystifying React Hooks” piece I wrote earlier.
  • Calling the getAllNotes query inside the useEffect Hook. This Hook is called immediately before the component mounts. If you’re unfamiliar with this pattern, check out this piece for an explanation of how it works.

Here’s the result of what the query returns. We’re saving the result as the notes state and rendering it with React.

The getAllNotes query result

You might wonder what these timestamps are for. Well, FaunaDB tracks these for you since it has built-in versioning. You can even query the history of your notes, if you’d like.


Creating Notes

Creating new component files
  • NoteForm.js — this is the file for creating new notes
  • NoteList.js — renders all notes and the functionality associated with the notes, such as editing and deleting
  • index.js — used for imports and exports

Great, let’s get started with the NoteForm.js firstly.

src/components/NoteForm.js

Notice at the bottom we’re wrapping our NoteForm component with the Ant Design Form higher-order component (HOC). This gives injects and gives us access to the form prop inside the NoteForm component.

The form property consists of methods for form validation. We’re making use of the getFieldDecorator, validateFields, and resetFields methods.

Implement handling form submission

We’re validating the form. If there are no errors and there’s at least one character, we’re submitting the form and querying our FaunaDB instance to save the note.

Notice we’re using the notes and setNotes props. We have to pass these from App.js — if you paid extra attention, you noticed we’re deconstructing values from the props and form objects. If this pattern is unfamiliar, check out “How To Use Destructuring.”

Now, go ahead and import the components to index.js so we can conveniently import them.

src/components/index.js

Styling

src/App.css

Using the NoteForm Component

Don’t forget to pass the props to the NoteForm component. This is what you should see in the browser.

NoteForm.js React component

The form works, but the results are not being rendered yet. Let’s fix that. Open the NoteList.js component, and write the following code.

  • We’re mapping over all the notes and rendering them
  • On each note, we’re adding event listeners to allow the editing of content and the removing of notes

Import the NoteList to App, and place it below the NoteForm component.

App.js

Don’t forget to implement the handleRemove and handleEdit methods and pass them as props to the NoteList component.

There we go! Open the browser and type something to the NoteForm, and press enter. We should see the following now:

Adding a new note

If we inspect our FaunaDB collection, we should see new notes inserted. Try creating new notes, editing, and deleting.

New notes in the FaunaDB collection

Great job — you did very well!


Optional: Adding Toast Notifications

  • First, import the styles and the ToastContainer from the react-toastify library
  • Place the ToastContainer inside the return method. This gives us access to the toast notifications. We can now simply call toast.success(‘message here’) or toast.error(‘error message’) from anywhere in our app.

Try it out — place the toast message to the handleRemove method. Don’t forget to import the toast method from the package wherever you’re using it.

import { toast } from 'react-toastify';
App.js

And now if we click on the remove icon, we should see our toast.

Showing the toast notification when removing notes

Excellent job! If you ever got lost, here’s the source code for this piece.

Something to consider: For demonstration purposes, the current application allows any user that accesses the website to create and read notes. In a real scenario, that’d not be a good idea for security reasons. Usually, you’d want to put the application behind a secure login page, which you can implement using FaunaDB’s login function or other solutions.


Conclusion

Better Programming

Advice for programmers.

Indrek Lasn

Written by

JS, Swift, C++ Indie Dev. Oh, and startups. Connect with me on Twitter @ https://twitter.com/lasnindrek

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade