Editor’s note: This article was last updated on 10 October 2023 to enhance clarity and improve the comprehensibility of the code.
Frontend developers traditionally focus on user interfaces and don’t usually need to understand the process of receiving, recording, and removing information. But gaining an understanding of backend and database interactions comes with many advantages:
In this tutorial, we’ll demonstrate how to create a small application using Express and Node.js that can record and remove information from a PostgreSQL database according to the HTTP requests it receives. We’ll then create a simple React app to test how the entire application flows from front to back.
Jump ahead:
I published a GitHub repo for this tutorial so you can compare your code if you get stuck. Now let’s get our database running.
PostgreSQL, or Postgres, is a relational database management system that claims to be the world’s most advanced open source relational database. It has been maintained since 1996 and has a reputation for being reliable and robust.
Start by downloading and installing PostgreSQL. It supports all major operating systems, so choose the right one for your computer and follow the instructions to set up the database. The setup wizard will prompt you to enter a superuser password. Make sure you remember this password; you’ll need it to log in later.
Once the installation is complete, you can access your database by using pgAdmin, a graphical interface tool that is installed automatically with PostgreSQL.
Once opened, pgAdmin will ask for your password to log in. Below is the overview of a newly installed PostgreSQL database:
To better understand the SQL language, we need to create a database and table from the terminal.
To access PostgreSQL from the terminal, use the command psql
with the option -d
to select the database you want to access and -U
to select the user. If the terminal replies that the psql
command is not found, you’ll most likely need to add the Postgres bin/
and lib/
directories into your system path:
psql -d postgres -U postgres
You will also be asked to input the password you created earlier. Once you’re logged in, create a new user by adding a login permission with the password “root
“:
CREATE ROLE my_user WITH LOGIN PASSWORD 'root';
A user is simply a role that has login permission. Now that you have one, give it permission to create databases by issuing the ALTER ROLE [role name] CREATEDB
syntax:
ALTER ROLE my_user CREATEDB;
In order to see what permissions you’ve granted to different users, you should run \du
. This lists all users (roles) and what actions you’ve granted permissions for.
Log out from your postgres
superuser and log in as my_user
using the \q
command. The password that you input here is the one you created for the my_user
role:
\q psql -d postgres -U my_user;
Now that you’re back inside, create a new database named my_database
:
CREATE DATABASE my_database;
After creating your database, you will have to navigate into it before you start creating any tables. To do that, you can run \c my_database
.
You might be wondering why we can’t just use the default postgres
user to create the database. That’s because the default user is a superuser, which means it has access to everything within the database. According to the Postgres documentation, superuser status is dangerous and should be used only when needed.
An SQL-based database stores data inside a table. Now that you have a database, let’s create a simple table where you can record your data:
CREATE TABLE merchants( id SERIAL PRIMARY KEY, name VARCHAR(30), email VARCHAR(30) );
One database can have multiple tables, but we’ll be fine with one table for this tutorial.
If you’d like to check the created database and table, you can use the commands \list
and \dt
, respectively. You might see more rows or less, but as long as you have the database and the table you created previously, your table should look like this:
my_database=> \list List of databases Name | Owner | Encoding my_database | my_user | UTF8 postgres | postgres | UTF8 template0 | postgres | UTF8 template1 | postgres | UTF8 my_database=> \dt List of relations Schema | Name | Type | Owner --------+-----------+-------+--------- public | merchants | table | my_user
You can also create a database using pgAdmin 4 in only a few steps:
Now that we can create databases and consequently tables, we can start inserting data.
Postgres is an SQL-based system, which means you need to use SQL language to store and manipulate its data. Let’s explore four basic examples of SQL queries you can use.
To retrieve data from a table, use the SELECT
key, followed by the name of the columns you want to retrieve and the name of the table:
SELECT id, name, email from merchants;
To retrieve all columns in the table, you can simply use SELECT *
:
SELECT * from merchants;
To insert new data into a table, use the INSERT
keyword followed by the table name, column name(s), and values:
INSERT INTO merchants (name, email) VALUES ('john', '[email protected]');
You can delete a row from a table by using the DELETE
keyword:
DELETE from merchants WHERE id = 1;
When you use the delete query, don’t forget to specify which row you want to delete with the WHERE
keyword. Otherwise, you’ll delete all the rows in that table.
Here’s how to delete multiple rows at the same time:
DELETE FROM merchants WHERE id IN(1,2)
We are using the above query with the assumption that we have more than one row in our database with IDs of 1
and 2
. These two rows will be deleted.
To update a certain row, you can use the UPDATE
keyword:
UPDATE merchants SET name = 'jake', email = '[email protected]' WHERE id = 1;
Now that you know how to manipulate data inside your table, let’s examine how to connect your database to React.
For this next section, we’re going to create a simple server with Node.js and Express and connect it to the PostgreSQL database we created. To imitate the behavior of a typical full-stack application, we’ll build a React frontend and communicate with the CRUD API we build on the server.
To connect your React app with a PostgreSQL database, you must first create an API server that can process HTTP requests.
Create a new directory and set a new npm package from your terminal with the following commands. I’ll create a backend folder just so I can split my work for better understanding. We’ll build our entire server inside this backend folder:
mkdir node-postgres && cd node-postgres mkdir backend && cd backend npm init
I like to run npm init -y
so that it creates the package.json
file without me having to fill in anything.
You can fill in your package information as you prefer, but here is an example of my package.json
:
{ "name": "node-postgres", "version": "1.0.0", "description": "Learn how NodeJS and Express can interact with PostgreSQL", "main": "index.js", "license": "ISC" }
Next, install the required packages:
npm i express pg
Express is a minimalist web framework you can use to write web applications on top of Node.js technology, while node-postgres(pg)
is a client library that enables Node.js apps to communicate with PostgreSQL.
After this step, feel free to open the directory (node-postgres
) in an editor of your choice.
Create an index.js
file in the root directory with the following contents:
const express = require('express') const app = express() const port = 3001 app.get('/', (req, res) => { res.status(200).send('Hello World!'); }) app.listen(port, () => { console.log(`App running on port ${port}.`) })
Open your terminal in the same directory and run node index.js
. Your Node application will run on port 3001, so open your browser and navigate to http://localhost:3001. You’ll see “Hello World!” text displayed in your browser.
You now have everything you need to write your API.
The pg
library allows your Node application to talk with Postgres, so you’ll want to import it first. Create a new file named merchantModel.js
and input the following code:
const Pool = require('pg').Pool const pool = new Pool({ user: 'my_user', host: 'localhost', database: 'my_database', password: 'root', port: 5432, });
Please note that putting credentials such as user, host, database, password, and port like in the example above is not recommended in a production environment. We’ll keep it in this file to simplify the tutorial.
The right way to do this, however, would be to install a package called dotenv
(remember to do this in your backend folder still) and use environment variables to reference these values from a .env
file. Before pushing your project to a public platform such as GitHub, be sure to include the .env
file in your .gitignore
.
Back in our merchantModel.js
code, the pool object you created above will allow you to query the database that it’s connected to. Let’s create three queries to make use of this pool. These queries will be placed inside a function, which you can call from your index.js
:
const Pool = require("pg").Pool; const pool = new Pool({ user: "my_user", host: "localhost", database: "my_database", password: "root", port: 5432, }); //get all merchants our database const getMerchants = async () => { try { return await new Promise(function (resolve, reject) { pool.query("SELECT * FROM merchants", (error, results) => { if (error) { reject(error); } if (results && results.rows) { resolve(results.rows); } else { reject(new Error("No results found")); } }); }); } catch (error_1) { console.error(error_1); throw new Error("Internal server error"); } }; //create a new merchant record in the databsse const createMerchant = (body) => { return new Promise(function (resolve, reject) { const { name, email } = body; pool.query( "INSERT INTO merchants (name, email) VALUES ($1, $2) RETURNING *", [name, email], (error, results) => { if (error) { reject(error); } if (results && results.rows) { resolve( `A new merchant has been added: ${JSON.stringify(results.rows[0])}` ); } else { reject(new Error("No results found")); } } ); }); }; //delete a merchant const deleteMerchant = (id) => { return new Promise(function (resolve, reject) { pool.query( "DELETE FROM merchants WHERE id = $1", [id], (error, results) => { if (error) { reject(error); } resolve(`Merchant deleted with ID: ${id}`); } ); }); }; //update a merchant record const updateMerchant = (id, body) => { return new Promise(function (resolve, reject) { const { name, email } = body; pool.query( "UPDATE merchants SET name = $1, email = $2 WHERE id = $3 RETURNING *", [name, email, id], (error, results) => { if (error) { reject(error); } if (results && results.rows) { resolve(`Merchant updated: ${JSON.stringify(results.rows[0])}`); } else { reject(new Error("No results found")); } } ); }); }; module.exports = { getMerchants, createMerchant, deleteMerchant, updateMerchant };
The three functions above don’t do anything extraordinary:
getMerchants
function gets all the merchants from our databasecreateMerchant
creates a new merchant, with the name and email received from the body. These values will be passed by our frontend — we’ll see how this happens as we codedeleteMerchant
function deletes a particular merchant from the database. This merchant is specified by id
, which is passed in as a parameter. On the frontend, id
is a promptupdateMerchant
updates a specific merchant, determined by that merchant’s id
The code above will process and export the getMerchants
, createMerchant
, updateMerchants
, and deleteMerchant
functions. Now it’s time to update your index.js
file and use these functions:
const express = require('express') const app = express() const port = 3001 const merchant_model = require('./merchantModel') app.use(express.json()) app.use(function (req, res, next) { res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5173'); res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers'); next(); }); app.get('/', (req, res) => { merchant_model.getMerchants() .then(response => { res.status(200).send(response); }) .catch(error => { res.status(500).send(error); }) }) app.post('/merchants', (req, res) => { merchant_model.createMerchant(req.body) .then(response => { res.status(200).send(response); }) .catch(error => { res.status(500).send(error); }) }) app.delete('/merchants/:id', (req, res) => { merchant_model.deleteMerchant(req.params.id) .then(response => { res.status(200).send(response); }) .catch(error => { res.status(500).send(error); }) }) app.put("/merchants/:id", (req, res) => { const id = req.params.id; const body = req.body; merchant_model .updateMerchant(id, body) .then((response) => { res.status(200).send(response); }) .catch((error) => { res.status(500).send(error); }); }); app.listen(port, () => { console.log(`App running on port ${port}.`) })
Now the app has three HTTP routes that can accept requests. The app uses middleware to ensure that requests are being processed properly and that certain requests are valid. For example, app.use(express.json())
is written so that Express can accept incoming requests with JSON payloads.
We also have this:
app.use(function (req, res, next) { res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000"); res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"); res.setHeader( "Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers" ); next(); });
The above middleware lets our Express app allow requests to this app from React.
Note: because we are using plain Node to start our server, whenever you make changes to the backend, you need to run node index
to make sure that your new changes are reflected. To get past this, you can install nodemon
, which monitors the server for changes and restarts the server automatically.
Your API is ready to serve and process requests, so now it’s time to create a React application to send requests into it. Before moving on, navigate out of the backend
folder and back into the root node-postgres
folder.
Let’s bootstrap your React app with Vite. To do this, run npm create vite@latest
in the terminal, and give the name frontend
when prompted to give the project name.
Choose React as the framework you want to work with, and simultaneously run the next three commands you are prompted to run to finish setting up your app: cd <project name>
, npm install
, npm run dev
.
You can freely delete files and folders that are not needed, as they’ll only clutter up your directory.
Now let’s write a simple React app from scratch. You can replace the contents of the App.js
file with the following contents:
import {useState, useEffect} from 'react'; function App() { const [merchants, setMerchants] = useState(false); function getMerchant() { fetch('http://localhost:3001') .then(response => { return response.text(); }) .then(data => { setMerchants(data); }); } function createMerchant() { let name = prompt('Enter merchant name'); let email = prompt('Enter merchant email'); fetch('http://localhost:3001/merchants', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({name, email}), }) .then(response => { return response.text(); }) .then(data => { alert(data); getMerchant(); }); } function deleteMerchant() { let id = prompt('Enter merchant id'); fetch(`http://localhost:3001/merchants/${id}`, { method: 'DELETE', }) .then(response => { return response.text(); }) .then(data => { alert(data); getMerchant(); }); } function updateMerchant() { let id = prompt('Enter merchant id'); let name = prompt('Enter new merchant name'); let email = prompt('Enter new merchant email'); fetch(`http://localhost:3001/merchants/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({name, email}), }) .then(response => { return response.text(); }) .then(data => { alert(data); getMerchant(); }); } useEffect(() => { getMerchant(); }, []); return ( <div> {merchants ? merchants : 'There is no merchant data available'} <br /> <button onClick={createMerchant}>Add merchant</button> <br /> <button onClick={deleteMerchant}>Delete merchant</button> <br /> <button onClick={updateMerchant}>Update merchant</button> </div> ); } export default App;
This React app will send requests to the Express server you created. It has two buttons to add and delete a merchant. The getMerchant
function will fetch merchant data from the server and set the result to the merchant
state.
createMerchant
, deleteMerchant
, and updateMerchnat
will start the process to add, remove, and update merchants, respectively, when you click on the buttons.
Now run your React app with npm
run dev
. You can test and see how the data collected from your React application is recorded in PostgreSQL:
Now you know how to install the PostgreSQL database, create a database and table, and build a minimal CRUD API to function as a bridge between your React app and your database.
We created an end-to-end example of how to use Postgres with React and demonstrated exactly what happens when you send those HTTP requests from your React app. This small application can be improved upon to handle different errors and edge cases, but you see the general idea of how to manipulate data in PostgreSQL.
This tutorial is far from a complete guide to backend programming, but it’s enough to help you get started in understanding how the backend works.
If you find any error or can’t insert data from the application, you can clone the complete source code from this repo and compare the code with your example.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
31 Replies to "Getting started with Postgres in your React app"
As a DBA I have to say that mentioning the wild card * in a select without mentioning the problems it can cause is a rookie mistake.
Never select more than you need, especially if the given table contains indexes that might be used if you select only the required columns (which are also present in the index) and thus relying on an index that might make a difference from having your query running at 20 seconds to less than 200ms when an unnecessary key lookup occures.
Never use * in production, especially when defining views, they tend to parse the * into the actual columns in that time the view got created, meaning – if you add another column to the underlying table the view won’t return it, and if you remove a column some RDBMS will not validate dependency and cause an error when the view is queried.
I am getting a warning when I render the react page: functions are not valid as a React child.
I would like to suggest that you add a step to your tutorial. After `my_database` is created, you will want to change into the database before you create the table with the following command:
`\c my_database`
Thank you. This is the step i missed out cause me to not find the table in my database
You have a mistake in:
const deleteMerchant = () => {
return new Promise(function(resolve, reject) {
const id = parseInt(request.params.id)
pool.query(‘DELETE FROM merchants WHERE id = $1’, [id], (error, results) => {
if (error) {
reject(error)
}
resolve(`Merchant deleted with ID: ${id}`)
})
})
}
it should be:
const deleteMerchant = (id) => {
return new Promise(function(resolve, reject) {
pool.query(‘DELETE FROM merchants WHERE id = $1’, [id], (error, results) => {
if (error) {
reject(error)
}
resolve(`Merchant deleted with ID: ${id}`)
})
})
}
Thanks for the rest of the tutorial!
Cheers,
Nathan
Excellent tutorial!! I learned a lot.
I just had one small query.
I think the delete method is not working. I tried as Nathan pointed out but that doesn’t seem to work either
When I try to install react-postgres, I receibe this error:
npm ERR! code E404
npm ERR! 404 Not Found – GET https://registry.npmjs.org/react-postgres – Not found
npm ERR! 404
npm ERR! 404 ‘react-postgres@latest’ is not in the npm registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/daniel/.npm/_logs/2020-11-09T23_53_15_619Z-debug.log
Install for [ ‘react-postgres@latest’ ] failed with code 1
Perphaps the code it’s bad written?
Hey there, `react-postgres` isn’t a library. It’s the name the author gave to the CRA project he created with the `npx create-react-app` command.
Thanks! Now I can solve this problem.
I copied Nathan’s into mine and it still worked. What is the error message?
Hi there,
I’ve been trying to follow along with this tutorial in a project I’m working on, but
when I try to use the pg library, I receive the following error:
./node_modules/pg/lib/native/client.js
Module not found: Can’t resolve ‘pg-native’
I checked and it’s in the folder listed, but when I try to run “index.js” in that folder, it returns a syntax error. I haven’t made any changes to the library…so I’m not sure why it’s doing it.
Here’s a link to my code:
https://github.com/medemak/can-dash
Any help would be greatly appreciated!
Hi Kara, as you can see in my source code here (https://github.com/nsebhastian/react-node-postgres) The code to connect to the Postgres database is run under Node server, separated from the React code. You need to create a different project folder. Try clone my code and run it first. Hope it helps!
hi – i am getting a ‘Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3001/. (Reason: CORS request did not succeed).’ error. Please let me know how I can overcome the CORS issue?
I am on a Ubuntu machine.
Hi.. I followed the exact steps. but still could not access the pgsql. pgsql server is running and is able to insert data into it directly via console. could not insert data from application. there is no error! hence couldn’t find a way to make it working
Same here.
Same here. Did you find any solution?
Hi I followed the same tutorial. But when I’m trying to insert the data or delete the data. I am not able to do that operations. Checked for errors didn’t find anything. Can you please help me out?
I think you can try to inspect the source code provided for the tutorial here and compare it with yours:
https://github.com/nsebhastian/react-node-postgres
You can also clone the project into your computer and try to run it.
Hi Nathan, still I am not able to insert the data. When I inspect on the app screen it shows me an error on console that App.js:18 POST http://localhost:3001/merchants net::ERR_CONNECTION_REFUSED and when I try to do on postman for post method it shows title error with code 404 Cannot POST /
Did you run the Node server? Head to the node-postgres folder and execute `node index.js` command from the terminal to start the server on localhost:3001
Thank you Nathan. It worked when I run the node server. But When I’m inserting the data which is post request it’s inserting the null values even though I gave the data.
That probably means the value of `name` and `email` from the POST body is NULL.
Did you fill the prompt dialog box with values?
You need to make sure that React is sending a POST request with the right values.
You can see the function you need to inspect here:
https://github.com/nsebhastian/react-node-postgres/blob/master/react-postgres/src/App.js#L20
At line 29, the `body` property must be assigned a JSON that contains `name` and `email`
Hi Nathan, I figured it out. I was not running the node server. I’m just running the react app so, whatever values I’m inserting will be null. Then, I have installed the Nodemon which, keeps running the node server automatically even though we make any changes on the server-side.
Hi Nik, glad to hear that!
Yeah, Nodemon is definitely nice to have when you’re developing a Node-based application because `node server` command doesn’t apply the code changes when you hit the save button.
Hi Nathan, I have question, when I’m inserting the data for the first time it’s inserting the null values because I was not running the node server. But if my server is not running when it getting into the database?
Actually I have no idea 🙂
Maybe you insert the data from Postman or Postgres command line?
Can you help me I got this message “Unhandled Rejection (TypeError): Failed to fetch”
10 | function getMerchant() {
> 11 | fetch(“http://localhost:3001”)
| ^ 12 | .then((response) => {
13 | return response.text();
14 | })
I’m having trouble with this line https://github.com/nsebhastian/react-node-postgres/blob/432a5e8e00bee1cce5c36e6fe7edf5150b4d9103/node-postgres/merchant_model.js#L26
I had to change the query to ‘INSERT INTO person (id, name, country) VALUES ($1, $2, $3) RETURNING *’ and pass in an id variable. Could you elaborate how this line is supposed to work with out that parameter?
Question, is it possible to upload a picture of what the file structure looks like for this project? We created two index.js files and I am unsure of what file is suppose to be where when resolving this project. Any information on this matter helps! Thank you.
Hi there, when i wrote this codes on the pgAdmin 4 it’s works but how can i use it in the backend server
INSERT INTO products (product_name,main_category, img_url, description, price)
SELECT ‘Margarita Pizza’, categories.cat_name, ‘https://cdn.yemek.com/mnresize/1250/833/uploads/2022/03/pizza-margherita-tarifi-yemekcom.jpg’, ‘Monza Sos, Mozarella, FesleÄźen’, ‘119.90’
FROM categories WHERE cat_name = ‘pizza’
Did anyone have an issue installing PostgreSQL? If so how did you resolve it? I’m getting two errors
– Failed to load SQL modules into the database cluster.
– Problem running post-install step. Installation may not complete correctly Error reading file C:/Program Files/PostgreSQL/16/data/postgresql.conf