Build a CRUD Template Using React, Bootstrap, Express & Postgres

This tutorial provides step-by-step instructions to quickly setup a CRUD app. The app will use Express and Postgres to build an API on the backend and React to display, add, edit and delete data on the frontend. As a bonus, I added a button on the frontend to download a CSV file of the data table.

Feel free to skip my intro about why I am doing this and jump straight to Step 1 to get started with the tutorial. I also created github repos for the frontend and backend if you would like to clone these projects…

Frontend Github Repo
Backend Github API Repo

UPDATE - The tutorial for the backend (Steps 1–4) is still accurate and current, however, the frontend portion of the tutorial (5–7) was using Classes instead of Hooks. You can still use Classes and follow this tutorial, however I now use a Hooks based frontend…

How to Convert Class Components in this Tutorial to Hooks
Frontend Github Repo with Hooks

Learn Lessons by Building This CRUD App

I started this project because on LinkedIn a friend posted an app almost exactly like this that a prospective employer requested he build as part of his interview process. When he was finished, someone asked him how long it took and he replied, “3–4 hours.”

I decided to see how long it would take me to build the same app. I timed myself during every step, including the beginning planning stage.

For every sequential step I recorded my time to completion and took some notes on my progress.

Unfortunately, it took me 5–6 hours to complete and I didn’t even include a couple of the enhanced features I wanted.

Fortunately, my time records of each step revealed to me some of my biggest weaknesses, but also some of my greatest strengths.

I was surprised to discover that the things I thought would be most difficult were the easiest, and things I thought would be the simplest were the most time consuming. But the important thing was that as a result of this record keeping I was able to focus on my weaknesses and turn them into strengths.

FYI- the things that took me the most time were figuring out the styling using reactstrap, which I had never used before, updating state properly when an item was edited, and passing functions from parent components to child components. I also spent a good amount of time researching how to export data to a CSV file, only to ultimately find there is a React app for that!

Anyway, the things that were most difficult for me were all frontend things. Much to my surprise, the backend was a piece of cake!

So I suggest you do the same as me. Challenge yourself to build a simple CRUD app using a frontend of your choice and a backend of your choice. Time yourself and keep notes on every step to find your weak spots.

After your done, write an article about your results with an instruction guide so you can get up and running quickly the next time you have to get started on a similar project.

App Planning

Frontend Summary
The frontend will display data from the database in a responsive table and will include a delete and edit button for each item. Under the table will be an ‘add item’ button, as well as a button to export the table to an excel file.

The form to add and edit an item will be included in a modal and will update dynamically depending on whether we are adding or editing an item.

The frontend will be built with React and use reactstrap for the styles, which provides bootstrap components to React apps. It will use the included fetch API to update state dynamically. We will use react-csv to quickly create the button for visitors to download the data table.

Backend Summary
The backend will be built separately using Express. It will use a Postgres database and knex to make queries. It will use cors and helmet middleware for protection against attacks. It will use body-parser to parse request bodies, dotenv to protect environment variables, morgan to log requests, and nodemon to watch for changes in the dev environment.

Advanced Implementation (Things we will NOT build!)
A more thorough, but more advanced solution, would include Redux on the frontend and bCrypt and an authentication API on the backend. I will not be using these in this implementation as it goes beyond what a prospective employer would likely request.

Other advanced features that should be included, but are not, are other form inputs like checkboxes, select options, dynamically changing drop downs, and textareas. We will only be using text inputs and an email input.

More complications would arise and would need to be dealt with in a production-ready app, such as setting a defaultValue for an input field and making it not editable and adding input validations for things like properly formatted email, phone numbers, or passwords.

Setting up Docker so this app can run quickly from any dev machine, creating and running some tests, and table pagination, would all be needed in production apps, but we will not worry about all of that in this tutorial.

I will not be deploying this app. If I did I would probably deploy the frontend to Github and the backend to Heroku.

Now, let’s get started with the tutorial…

1. Setup the Backend

The plan…
Create express app, include all dependencies.
Create a postgres database and a db table within it.
Create a single page API that handles all the requests.

Take Action…
Command Line…

mkdir crudPractice-api
cd crudPractice-api
git init
mkdir controllers
subl .

NPM Installs…

npm init -y
npm install express body-parser pg knex dotenv helmet cors morgan
npm install nodemon --save-dev

In the package.json file, do the following :

1. change the “name” to “Crud Practice”
2. change the “description” to “Practicing crud.”
3. replace the “main” line with…
“main”: “server.js”,
4. replace the scripts content with…
“start”: “nodemon server.js”

Add server.js file…
create server.js file in root directory
copy the contents of the code below to the new server.js file

the server.js file will now include:
- the express/app creation/connection/porting
- the postgres connection via knex
- all middleware
- api routes

Add the controller…
create main.js in the controllers folder
copy the contents of the code below into the new main.js file

the main.js will now include:
- a get function that returns all data from the db table
- a post function that will add a row to the table
- a put function that will update a row with a given id
- a delete function that will delete a row with a given id

Setup dotenv…
this is not a priority yet, but it is ready to go when you are ready to deploy and hide some stuff like connection details

Git … Command Line…

git add .
git commit -m “initial commit”

Command Line…

npm start

Now your backend is completely setup and should be running on Port 3000.

All requests to the backend should be logged as a result of our morgan middleware.

Cors is setup to allow requests from localhost:3001, which is what we will use to run the frontend. This will need to be changed when in production or if you are running your frontend from a different port in dev.

The folder structure can easily be changed to your liking. All parts of the server.js file and main.js file can easily be abstracted to other files as well.

2. Setup the Database

Command Line // start postgres and create a db

brew services start postgresql
createdb crud-practice-1

Open pSequel, connect to the new database with no username/password, use the query command to execute the following command which will create a table in our database.

CREATE TABLE testtable1 (
id serial PRIMARY KEY,
first VARCHAR(100),
last VARCHAR(100),
email text UNIQUE NOT NULL,
phone VARCHAR(100),
location VARCHAR(100),
hobby VARCHAR(100),

Now that command will be saved in pSequel for future use. Simply pull it up and edit it whenever you want to add a new table to any Postgres db.

3. Test API in Postman

Test the get, post, put and delete queries from the backend api. If all works, be sure to post a few entries so we have data to use in the frontend.

4. Setup the Frontend

Command Line…

npx create-react-app crudPractice-Frontend
cd crudPractice-Frontend
// install bootstrap and reactstrap with its dependencies…npm install bootstrap
npm install --save reactstrap react react-dom
// install the react-csv pluginnpm install react-csv// create some folders and files in the src srcmkdir Components
cd Components/
mkdir Forms Modals Tablescd Forms/
touch AddEditForm.js
cd ../Modals/
touch Modal.js
cd ../Tables/
touch DataTable.js

Cleanup Files…
In index.js add…

import ‘bootstrap/dist/css/bootstrap.min.css’

In app.js remove logo.svg and App.css

In index.css file remove the code block and add this line…

table th, table td { white-space: nowrap; }

5. Setup App.js

In this section we are just going to copy the files I already created. All of the files should either be self-explanatory, or include comments with further information. The comments are not needed, but are in areas I had a difficult time with and needed help reinforcing as I reviewed it.

Copy and paste the code below into your App.js (replace what was there).

This is mostly reactstrap components. A couple of the inline styles are just for quick setup, but of course in a more complicated app you would want to create classes and add them to your index.css file (or create other css files).

As this app is only one page the state will be contained in this file. We create a function to fetch all data from our api and add it to the state.

Because fetch is an asynchronous function that returns a promise, we call the getItems() function from componentDidMount() in order to not block the initial rendering of the page.

We create a function to add an item to the state, another to edit an item in the state, and another to delete an item from the state. We pass those functions to child Components.

The child Components will call their passed in functions when a user performs an action and will pass back the added, edited, or deleted item.

The state will update dynamically and the page will thus always display the latest data from the database.

5. Setup /Tables/DataTable.js

Copy and paste the code below into your DataTable.js Component.

This page displays all the data from the table using the “items” from state passed in via props from App.js. It includes a function that fetches our delete API, which is called if a delete button is clicked on an item. The delete button, as well as an edit button, are included in each item of the row. We will explain more about the Edit Modal in the next section.

We could have abstracted the table row to its own Component, but this is such a small app I didn’t feel it necessary. We also could have added a loading Component to display when performing any action, but I did not feel the need to include that upgrade in this project either.

6. Setup /Modals/Modal.js

This was the first part of the project where reactstrap proved to be truly valuable. I was expecting to build a Modal from scratch the way React explains how to do, however, reactstrap provides a Modal component which already has the setup for a react modal built in.

This means all you have to do is import the Modal, copy the code from reactstrap and update the data you want in the Modal. In my case, I wanted a form in the Modal, so I replaced reactstrap’s content with a reactstrap Form Component, which we will go over in the next step.

The Add or Edit Button
Caution: This part is confusing. Read it a couple times to ‘get’ it…
Before looking at the Modal.js file below, you must look back at the DataTable.js file in Step 5 on line 42. Notice how we are passing in the buttonLabel prop to the Modal component.

This means we don’t actually put the Button component in the DataTable.js file, we put the full Modal component. The Modal component contains the button! And it will display no matter what.

We are doing the same thing in line 40 of the App.js file.

The DataTable.js buttonLabel prop will be displayed in the row of each individual item, thus, the buttonLabel is ‘Edit.’ We also pass in the ‘item’ as a prop from the DataTable.js file so that we can update the form with the item details.

The App.js buttonLabel is ‘Add Item,’ as that is the label for the button we place underneath the table.

We handle which button to display within the Modal. Now, we can continue and I will explain the logic afterwards, though it should be obvious now.

Copy and paste the code below into the Modal.js file.

First, I had to name this component ModalForm because Modal is already taken by reactstrap and causes an error. This a modal with a form, thus ModalForm.

We then have a simple toggle state and method to open and close the Modal when the button is clicked.

Then we get the buttonLabel prop and assign it to const label. We display the appropriate button depending on whether the buttonLabel is ‘Edit’ or not.

Then there is the Modal which is a Component provided by reactstrap that has an isOpen method and a bunch of child components.

The AddEditForm component is one I created and it holds the add/edit form. We pass as props to the form the functions to update state which were passed in from App.js. If the button that was clicked was the ‘Edit’ button, it will include the item object as a prop and we pass that to the form as well.


7. Setup /Forms/AddEditForm.js

Finally, we have come to the last step in this app!

Copy and paste the code below to the AddEditForm.js file.

The trick to my React form working for both add and edit is detecting whether an ‘item’ was passed in as props.

First, onComponentDidMount() will check if there is an item. If there is, it will populate the state with the contents of the item.

Second, the Form component will either call the submitFormAdd or submitFormEdit function depending on whether an ‘item’ was passed in.

One catch was that when an ‘item’ was detected and the state was populated, the values of the form fields would update, but if one of the values was null the form would throw a React error because it cannot handle ‘null’ values. To fix this I had to do a simple check in the value of each item — if the value was null, replace with blank instead of null.

In both the add and edit function, upon success we pass in the returned item to the appropriate function passed in from App.js so that App.js can update the state accordingly.

Obviously, the two submit functions could be abstracted out, but no need to do that in this simple app, this is easy enough to understand.

8. Start it Up and Git

npm start your app, it should be running on port 3001. If it is running on any other port be sure to change which port to allow in cors in your backend on your server.js file.

Finally, don’t forget to store your version of the working frontend to git.

9. Writing this article

Just to be thorough, I should include the fact that it took me two days and many more hours than it took me to build the app to write this article. Perhaps I should have taken notes and timed myself through every step of my writing process as well!

I don’t why it is important, so just for fun, please give this article a lot of claps if you like it and leave me a message if you have a question.

developer. entrepreneur. student.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store