Building a Simple CRUD Application with React and PostgreSQL using Docker

Aadarsh Nagrath - Oct 15 - - Dev Community

We will be creating a simple CRUD (Create, Read, Update, Delete) application with React as the frontend, PostgreSQL as the database, and Docker to containerize both. This guide will cover setting up a backend with Node.js and Express, managing a PostgreSQL database in a Docker container, and creating a React-based frontend to interact with the backend API.

MY Github, Twitter

.

Step 1: Setting Up the Backend with Node.js and Express

Let's start by building the backend that will connect to a PostgreSQL database and handle CRUD operations.

1.1 Initialize Node.js Project

First, create a new directory for the backend and initialize a Node.js project:

mkdir react-postgres-crud-backend
cd react-postgres-crud-backend
npm init -y
Enter fullscreen mode Exit fullscreen mode

1.2 Install Required Dependencies

We’ll need express for creating the API, pg for interacting with PostgreSQL, cors for cross-origin support, and body-parser to handle incoming requests:

npm install express pg cors body-parser
Enter fullscreen mode Exit fullscreen mode

1.3 Create the Backend Structure

Create a basic project structure and files for the backend:

mkdir src
touch src/index.js
touch Dockerfile
Enter fullscreen mode Exit fullscreen mode

1.4 Write the Express Server Code

In src/index.js, define your Express server with CRUD routes that connect to the PostgreSQL database:

const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const { Pool } = require("pg");

const app = express();
const pool = new Pool({
  user: "postgres",
  host: "postgres",
  database: "cruddb",
  password: "password",
  port: 5432,
});

app.use(cors());
app.use(bodyParser.json());

// CREATE
app.post("/items", async (req, res) => {
  const { name } = req.body;
  const result = await pool.query("INSERT INTO items (name) VALUES ($1) RETURNING *", [name]);
  res.json(result.rows[0]);
});

// READ
app.get("/items", async (req, res) => {
  const result = await pool.query("SELECT * FROM items");
  res.json(result.rows);
});

// UPDATE
app.put("/items/:id", async (req, res) => {
  const { id } = req.params;
  const { name } = req.body;
  const result = await pool.query("UPDATE items SET name = $1 WHERE id = $2 RETURNING *", [name, id]);
  res.json(result.rows[0]);
});

// DELETE
app.delete("/items/:id", async (req, res) => {
  const { id } = req.params;
  await pool.query("DELETE FROM items WHERE id = $1", [id]);
  res.json({ message: "Item deleted" });
});

app.listen(5000, () => {
  console.log("Server running on port 5000");
});
Enter fullscreen mode Exit fullscreen mode

This basic setup will allow the backend to handle POST, GET, PUT, and DELETE requests for CRUD operations.

Step 2: Dockerizing PostgreSQL

Next, we'll set up a PostgreSQL database using Docker to easily manage and isolate our environment.

NOTE - Your docker-compose.yml file only defines the Postgres service, and the connection parameters in your Node.js backend are using localhost as the host. Since the backend is running inside a Docker container, localhost refers to the container itself, not the Postgres service.Sowe have host as 'postgres'

2.1 Create a docker-compose.yml File

In your project root, create a docker-compose.yml file to define the PostgreSQL service:

version: "3"
services:
  postgres:
    image: postgres:13
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: cruddb
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

  backend:
    build: .
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: cruddb
      POSTGRES_HOST: postgres 
    ports:
      - "5000:5000"
    depends_on:
      - postgres

volumes:
  pgdata:

Enter fullscreen mode Exit fullscreen mode

2.2 Run the PostgreSQL Container

Now, bring up the PostgreSQL container by running:

docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

This command will start a PostgreSQL database accessible on port 5432 with a database named cruddb.

2.3 Create the items Table

Once the PostgreSQL container is running, connect to it and create the necessary table:

docker exec -it <container_id> psql -U postgres -d cruddb
Enter fullscreen mode Exit fullscreen mode

Create the items table by running the following SQL command:

CREATE TABLE items (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100)
);
Enter fullscreen mode Exit fullscreen mode

Imfwe

Step 3: Building the Frontend with React

Now that our backend is set up, we can build the frontend using React to interact with the API.

3.1 Create a New React App

Create a new React app to manage the frontend:

npx create-react-app react-postgres-crud-frontend
cd react-postgres-crud-frontend
Enter fullscreen mode Exit fullscreen mode

3.2 Install Axios for API Requests

We'll use Axios to handle API requests from the React app:

npm install axios
Enter fullscreen mode Exit fullscreen mode

3.3 Implement CRUD Operations in App.js

Replace the contents of src/App.js with the following code to manage items in the frontend:

import React, { useEffect, useState } from "react";
import axios from "axios";

const API_URL = "http://localhost:5000/items";

function App() {
  const [items, setItems] = useState([]);
  const [newItem, setNewItem] = useState("");

  useEffect(() => {
    fetchItems();
  }, []);

  const fetchItems = async () => {
    const result = await axios.get(API_URL);
    setItems(result.data);
  };

  const createItem = async () => {
    if (newItem.trim()) {
      const result = await axios.post(API_URL, { name: newItem });
      setItems([...items, result.data]);
      setNewItem("");
    }
  };

  const updateItem = async (id, newName) => {
    const result = await axios.put(`${API_URL}/${id}`, { name: newName });
    setItems(items.map(item => (item.id === id ? result.data : item)));
  };

  const deleteItem = async id => {
    await axios.delete(`${API_URL}/${id}`);
    setItems(items.filter(item => item.id !== id));
  };

  return (
    <div className="App">
      <h1>CRUD App with React and PostgreSQL</h1>
      <input
        type="text"
        value={newItem}
        onChange={(e) => setNewItem(e.target.value)}
      />
      <button onClick={createItem}>Add Item</button>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            <input
              type="text"
              value={item.name}
              onChange={(e) => updateItem(item.id, e.target.value)}
            />
            <button onClick={() => deleteItem(item.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This code allows users to add, edit, and delete items, which are stored in the PostgreSQL database.

Iewfscription

3.4 Run the React App

Now, start the React development server:

npm start
Enter fullscreen mode Exit fullscreen mode

Your React app will be running on http://localhost:3000, and you can interact with your backend via the API.


Step 4: Running the Backend in Docker

You can also run the backend in a Docker container by creating a Dockerfile in the backend folder:

FROM node:16

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 5000

CMD ["node", "src/index.js"]
Enter fullscreen mode Exit fullscreen mode

Build and run the backend container:

docker build -t react-postgres-backend .
docker run -p 5000:5000 react-postgres-backend
Enter fullscreen mode Exit fullscreen mode

Irtdbiption

Conclusion

In this tutorial, you built a full-stack CRUD application using React for the frontend, Node.js and Express for the backend, and PostgreSQL for the database, all powered by Docker. This setup can easily be expanded to include more complex features like authentication, validation, and more!

. . . . . . . . . . . . . .