June 17, 2019

Building a Sticky Notes Application Using 8base, GraphQL and React

8base
@8base
You can play with the demo for this article here: https://note-app.now.sh/

Proper time management has been an utmost struggle for as long as I can remember, losing track of scheduled tasks and missing notes because they’re spread across several note saving applications is nothing to write home about. The solution doesn’t lie in a personalized note application, but there might be a chance that you would use this application because of the time you put into it.

To build a sticky notes app, we’ll need a simple stack, little to no backend development and an application that can be deployed easily. Based on these requirements, we’ll pick a stack for the application. It looks like this:

  • React — for frontend development. It is a popular library for building user interfaces and can be deployed easily on several platforms like Now and Netlify.
  • 8base GraphQL: A backend database layer for our app. This is where we will store and read the questions on our application. With 8base, there won’t be a need to create and maintain a backend for the application.

I successfully created a simple application using the tools above. In this tutorial, we’ll go through the process of creating this application and setting up a backend running on the 8base platform.

If you are not satisfied just seeing a screenshot, you can always play with the demo application. The notes are placed randomly across the web page and can be dragged to different locations.

To follow this tutorial, a basic understanding of React and Node.js is required. Please ensure that you have Node and npm/yarn installed before you begin.

We’ll also be making use of some GraphQL queries in the project, so some familiarity with GraphQL will be helpful.

8base is a platform offering a vast selection of features to help developers build applications at a faster and simpler rate. With 8base, you can easily curate your application’s backend without writing a line of code. Using the 8base platform, you can build your backend using a simple GUI that allows you to perform actions such as:

  • Define your data schema: create tables/table relationships.
  • Upload files.
  • Define permission and authorization roles.
  • Manage various projects using “Workspaces”.
  • Design queries using the API explorer (based on GraphQL).

In this tutorial, we’ll see how to make use of 8base to quickly define a backend and serve the data using a GraphQL API. Our frontend application will be built using React and will communicate with the server using GraphQL.

To get started using 8base, follow the steps listed below:

  1. Create an account on 8base. You can start using 8base for free.

2. After sign up is complete, click on the Data Builder button to navigate to the Data page and click on “New Table” to start building your backend.

3. After your new table is loaded, you’ll be taken to the schema to begin defining fields. Let’s take a look around and note a couple of things. On the left, you’ll see there are System Tables and Your Tables. Every new 8base workspace automatically comes prepackaged with a number of built-in tables. These tables are used to handle things like Files, Settings, and Permissions and can all be accessed through the 8base GraphQL API.

4. Go ahead and create a table named Notes that consists of the following fields:

  • title: the field type is Text. This will store the title of the note.
  • text: field type is Text. This field will hold the note body.

Our schema is quite simple and should be used to achieve the features we’re trying to accomplish.

5. Next, copy the API endpoint URL (available on the bottom left) — this is your main line of communication between your frontend and your 8base backend.

6. Finally, for the purpose of this tutorial we’re going to allow open access to Guests by default so that dealing with authentication is optional. To allow guest access to your new Notes table, navigate to Settings > Roles > Guest and check the appropriate boxes under Notes. All unauthenticated users who access your API endpoint are given the role of Guest by default. Authentication is not covered in this tutorial. You can see how authentication should be handled in more detail here.

Now that we’ve finished setting up the backend using 8base, we’ll start work on the frontend side of the application.

Getting Started

I created a starter project for easy setup and to reduce overhead, ensuring the article focuses on getting started with 8base and GraphQL. The skeleton of the application has already been set up, this includes styling and project structure. The following features are available in the starter branch:

  1. Note form component for creating a note.
  2. A note component that displays the details of a single note.
  3. Note list component that displays the list of available notes.

Run the following command to clone the repository:

{% code-block language="sh" %}
git clone -b starter https://github.com/HackAfro/note-app.git
{% code-block-end %}

Open the folder and install the project’s dependencies by running the following command:

{% code-block language="sh" %}
npm install
{% code-block-end %}

Start the React app server by running npm start in a terminal in the root folder of your project.

Connecting to the 8base backend with GraphQL

Now that our starter frontend application is up and running, the next step is to set up the client to communicate with the 8base backend using GraphQL. A useful library to help with the connection is apollo-boost; it provides a client for connecting to the GraphQL backend using a URI. The URI is the endpoint provided by 8base Let’s update the index.js file to set up the ApolloClient.

{% code-block language="js" %}
import React from "react";
import ReactDOM from "react-dom";
import { ApolloProvider } from "react-apollo";
import ApolloClient from "apollo-boost";
import * as serviceWorker from "./serviceWorker";

import "./index.css";
import App from "./App";

const client = new ApolloClient({
 uri: "<YOUR_8BASE_ENDPOINT>"
});

ReactDOM.render(
 <ApolloProvider client={client}>
   <App />
 </ApolloProvider>,
 document.getElementById("root")
);
serviceWorker.unregister();
{% code-block-end %}

The WORDS_HIGHLIGHT:1:ApolloClient takes an object with a uri property as an argument; with this, the client gives us access to fetch, update and delete data using the provided uri . Be sure to replace the placeholder value with your actual endpoint.

We then wrap the application with the ApolloProvider that takes a single prop, the client. The ApolloProvider loads the 8base table schema, which gives you access to all properties of the data model inside your frontend code.

Fetching and displaying notes

To fetch notes from the backend, we’ll write a GraphQL query to fetch the notes stored on 8base. Let’s get started with this by updating the App.js file.

Add the following imports where you find the comment "TODO--1":

{% code-block language="js" %}
import gql from "graphql-tag";
import { graphql } from "react-apollo";
{% code-block-end %}

The imports will be useful for creating GraphQL queries and for passing props to the App component for fetching notes. Next, we’re going to add the query for fetching notes from the backend. Update the NOTE_LIST_QUERY to be similar to the snippet below:

{% code-block language="js" %}
const NOTE_LIST_QUERY = gql`
 query {
   notesList {
     items {
       id
       title
       text
     }
   }
 }
`;
{% code-block-end %}

If you’re unsure of your queries, you can always test them on the 8base API explorer. Let’s test the query above in the API explorer.

If you run this query on your API explorer, it’ll most likely return an empty array because you haven’t created any notes. We can easily create notes using the 8base dashboard; navigate to the data link on the sidebar and follow the steps annotated to the screenshots below:

We’re getting the id, title and text from the note items. Next, we'll prepare the App component to make use of the items returned from the query. Finally, we'll make use of the graphql import to link the query with the App component. The function takes the query string and config options.

{% code-block language="js" %}
export default graphql(NOTE_LIST_QUERY, {
 props: result => {
   const { data } = result;
   const { loading, refetch } = data;
   let notes = [];
   if (data && data.notesList) notes = data.notesList.items;
   return {
     loading,
     notes,
     refetch
   };
 }
})(App);
{% code-block-end %}

Replace the existing export line beneath the comment TODO--3 with the snippet above

The graphql function returns a function which will inject any component with reactive GraphQL capabilities. This follows the React higher-order component pattern which is also used by react-redux's connect function .

By making use of the {% code-line %}graphql(){% code-line-end %} function, we can query the GraphQL endpoint for notes. It takes a query( NOTE_LIST_QUERY) as its first parameter, config as the second, the return value is a HOC that should be executed with the desired component as an argument. In the snippet, we pass the value of the {%code-line%}data.noteList{%code-line-end%} property to a new variable notes. By returning this value, we can access it within the component as {% code-line %}props.notes{% code-line-end%} and the loading state as {% code-line %}props.loading{% code-line-end %}.

The refetch method will be very useful in order for us to achieve something close to a real-time update. We will call the function after every mutation to ensure that the data in the application is always in-sync with the 8base backend.

If you navigate to http://localhost:3000, you’ll see the note you just created displayed. We need to be able to create notes without visiting the 8base dashboard, that means going to the next GraphQL level…. Mutations!!

Creating notes

After completing the setup to fetch notes from the backend, the next logical move is adding the ability to create notes. The starter files contain a form component, and we need to update the component to feature the mutation for saving created notes on the 8base backend.

In the previous section, we performed a query to fetch the notes from the backend, in this section we’ll be performing mutations to save new notes, the mutation bears similarities to a query, the only difference being that a mutation is called whenever there’s a need to change data. After creating tables on 8base, queries and mutations for that table become readily available in their API explorer; so we can always jump there if we're in doubt of how the mutation should be structured.

Let’s have a look at how the mutation for creating notes look like in the API explorer. Go the to 8base dashboard and click on the API explorer on the sidebar.

On the API explorer, click on the mutation link as seen above. This will display all the mutations available. Look for that of that handles creating notes:

The screenshot shows that the name of the mutation is noteCreate and it takes a single argument data of type NoteCreateInput. The NoteCreateInput is the type definition of the request body required to create a note.

Now that we know what’s required of us, let’s implement! I was kind enough to create the note creation form (you can thank me later) so what’s left to do is add the mutation to the component using the graphql function.

Open the src/components/note-form/index.js file, add the following imports where you have the "TODO--1" comment:

{% code-block language="js" %}
import gql from "graphql-tag";
import { graphql } from "react-apollo";
{% code-block-end %}

Next, we’ll define the mutation, update the NOTE_MUTATION variable and assign it a new value similar to the snippet below:

{% code-block language="js" %}
const NOTE_MUTATION = gql`
 mutation NoteCreate($data: NoteCreateInput!) {
   noteCreate(data: $data) {
     id
   }
 }
`;
{% code-block-end %}

Next, we’ll connect the mutation with the component using the graphql function. Replace the export line in the file with the snippet below:

{% code-block language="js" %}
export default graphql(NOTE_MUTATION, {
 name: "noteCreate"
})(NoteForm);
{% code-block-end %}

By doing this, the graphql function passes a named function noteCreate that'll be used for performing the create mutation. Let's make use of the noteCreate function to create a note in submit of the form.

Go to the "TODO--4" comment and update the submit function to be similar to the snippet below:

{% code-block language="js" %}
const submit = async note => {
 const res = await noteCreate({ variables: { data: note } });
 if (res.data.noteCreate.id) {
   setNote({ title: "", text: "" });
   refetch();
 }
};
{% code-block-end %}

In the snippet above, we made use of the noteCreate function to perform a Create mutation on the notes table. The argument passed to the function is an object containing variables and the data body.

We wait for the request to be complete then we check if it succeeded by looking out for the id in the response body. We then reset that state of the note and Refetch. I mentioned the refetch function earlier in the article but let me refetch (refresh) your memory; refetch forces your component to refetch the query you defined in the {% code-line %}graphql(){% code-line-end %} function.

We should be ready to test now. Navigate to http://localhost:3000 and fill the form to witness the magic.

Deleting notes

What would be the good of any note application if you couldn’t delete all your notes and pretend they never existed. The flow for deleting a note should be:

  1. User clicks on the delete button
  2. Show confirmation dialog — this will reduce the number of angry users, very useful.
  3. Do the deed. Delete the note.

The setup for deleting notes lies in the src/note-card/delete-button.js file. Open the file and add the following imports at the top of the file. Specifically, below where the "TODO--1" is:

{% code-block language="js" %}
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
{% code-block-end %}

Then, write the delete mutation and assign it to the DELETE_MUTATION variable. It should look similar to the snippet below:

{% code-block language="js" %}
const DELETE_MUTATION = gql`
 mutation DeleteNote($data: NoteDeleteInput!) {
   noteDelete(data: $data) {
     success
   }
 }
`;
{% code-block-end %}

Next, we’ll connect the mutation with the component using the graphql function. Replace the export line in the file with the snippet below:

{% code-block language="js" %}
export default graphql(DELETE_MUTATION, {
 name: "deleteNote"
})(DeleteButton);
{% code-block-end %}

By doing this, the graphql function passes a named function deleteNote that'll be used for performing the create mutation. Now, we can update the click event handler and add the much-needed setup for deleting a note.

Hobble over to the onDeleteClick function in the component, place the following snippet within the function:

{% code-block language="js" %}
const onDeleteClick = async () => {
 const data = {
   id: noteId
 };
 const remove = window.confirm("Are you sure you want to delete this note?");
 if (remove) {
   await deleteNote({ variables: { data } });
   refetch();
 }
};
{% code-block-end %}

On click of the delete button, we set the data body passing the noteId as the id and then we show the user a confirmation message, explicitly declaring that we intend to delete this note permanently. If the user does as intended and decides to proceed, we call the deleteNote function to make the delete mutation; when this is complete, we refetch and refresh the view.

Here’s how it’s meant to look. Navigate to http://localhost:3000 and attempt deleting a note:

You can find the hosted demo of this application here

Conclusion

I’m sure we had a fun time building (filling in the missing holes at least) this application, we’ve seen how to write queries to fetch data from the 8base backend and also how to write create and delete mutations. 8base, as a platform, is relatively easy to navigate and offers an intuitive user interface. I spent a night figuring it out. You can fill a missing hole by learning how to write update mutations and adding the ability to edit a note. I'm sure the users will be enthralled by it. You can also visit their official documentation for more detailed information about the platform. You can find the source code of the application here.

Ready to try 8base?

We're excited about helping you achieve amazing results.