Open In App

Address Book using MERN

Last Updated : 15 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we will build a Address Book using the MERN stack. The application will enable users to add, remove, edit and view addresses. The project will use the mongoDB database for storage and React for UI part. This classic address book helps you to save your contacts easily and data can be retrieved easily.

Preview of Final Output: Let us have a look at how our final project will look like:

Screenshot-(136)

Prerequisites of Address Book :

  • Model schema for address should be designed before implementing the database .
  • The routes and endpoints of NodeJS backend must be designed before implementation .

Technologies Used in Address Book:

Approach To create Address Book:

  1. We will build a frontend in React that will use bootstrap to develop a UI which will be used for below mentioned functionalities .
  2. Then we will create MongoDB collection to store the data related to addresses .
  3. NodeJS will be used for developing backend server and Express will be used to implement endpoints used by our frontend .

Functionalities of Address Book :

The project will have following functionalities .

  • Add Address : User can create a new address and save it to address book.
  • Remove Address : User can remove existing addresses from address book.
  • Edit Address : User can edit and update existing address.
  • View Addresses : User can view all the addresses from address book.

Steps to create a Address Book :

Frontend

Step 1: First we will create a frontend for our application in React . start a new react project using below command .

npx create-react-app address-book

Step 2: For this project we will require axios to make API calls to backend . Also we will require bootstrap for elegant design . We are also going to use react-bootstrap-icons for icons . finally we will use react-router-dom package for routing in frontend.

Step 3: Install all the dependencies using npm as below inside frontend folder.

npm i axios react-bootstrap-icons react-router-dom 

Step 4: For bootstrap get CDN links of bootstrap and jquery and paste it inside index.html file in public folder as below.

<link href=”https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css” rel=”stylesheet” integrity=”sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC” crossorigin=”anonymous”>

<script src=”https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js” integrity=”sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM” crossorigin=”anonymous”></script>

<script src=”https://code.jquery.com/jquery-3.7.1.min.js” integrity=”sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=” crossorigin=”anonymous”></script>

Step 5: Create a folder called components in src directory and create files AddAddress.js, AddressList.js, Navigation.js inside it.

Backend

For backend create a separate folder outside of React folder called ‘address-book-backend’.

Step 1: Inside this folder initiate node project using below command.

npm init

Step 2: Now lets install required packages nodemon , express , mongoose , cors , body-parser as below

npm i express nodemon mongoose cors body-parser

Step 3: Once the packages are installed create a app.js file which will contain our backend code . Also create a folder models and create a file address.js

Database :

  • For database we are using local MongoDB you can use Atlas also .
  • Inside MongoDB create a new database with the name ‘addressbook’.
  • Now create ‘addresses’ collection inside this database and we are done with database part.

Project Structure of Address Book:

Screenshot-(134)

The updated dependencies in package.json will look like:

Frontend :

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.5.1",
"http": "^0.0.1-security",
"react": "^18.2.0",
"react-bootstrap-icons": "^1.10.3",
"react-dom": "^18.2.0",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Backend :

"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^7.6.0",
"nodemon": "^3.0.1"
}

Example: Write the following code in respective file

Frontend Code :

  • App.js: This file implements routing and imports all components
  • AddAddress.js: This component creates a forma to save new addresses
  • AddressList.j: This component displays Address in a list format
  • Navigation.js: This component implements navigation and handle update and delete function.

Javascript




//App.js
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import AddressList from './components/AddressList';
import AddAddress from './components/AddAddress';
 
function App() {
  return (
    <Router>
      <div className='container container-fluid min-vh-100 d-flex flex-column'>
        <Routes>
          <Route exact path="/" element={<AddressList/>} component={AddressList} />
          <Route exact path="/add" element={<AddAddress/>} component={AddAddress} />
        </Routes>
      </div>
    </Router>
  );
}
 
export default App;


Javascript




//AddAddress.js
import React, { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import Navigation from './Navigation';
 
const AddAddress = () => {
  const nav = useNavigate();
  //state for saving form data
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    contact: '',
    address: ''
  });
 
  //handle for updating form
  const handleChange = (e) => {
    setFormData({ ...formData, [e.target.name]: e.target.value });
  };
 
  //handle for creating new address
  const handleSubmit = (e) => {
    e.preventDefault();
    axios.post('http://localhost:5000/api/addresses', formData).then(() => {
      nav('/');
    });
  };
 
  return (
    <div className='container container-fluid min-vh-100 justify-content-center'>
      <h2 className='display-2 text-center'>Add Address</h2>
      < Navigation />
        <form onSubmit={handleSubmit}>
          <div className='form-group'>
            <label>Name:</label>
            <input type="text" name="name" onChange={handleChange} required className='form-control'/>
          </div>
          <div className='form-group'>
            <label>Email:</label>
            <input type="email" name="email" onChange={handleChange} required className='form-control'/>
          </div>
          <div className='form-group'>
            <label>Contact:</label>
            <input type="text" name="contact" onChange={handleChange} required className='form-control'/>
          </div>
          <div className='form-group'>
            <label>Address:</label>
            <input type="text" name="address" onChange={handleChange} required className='form-control'/>
          </div>
          <div>
            <button type="submit" className='btn btn-primary p-2 m-2'>Add Address</button>
          </div>
        </form>
      </div>
  );
};
 
export default AddAddress;


Javascript




//AddressList.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Navigation from './Navigation';
import { PencilSquare, Trash } from 'react-bootstrap-icons';
 
const AddressList = () => {
  const [addresses, setAddresses] = useState([]);
  const [selectedAddress, setSelectedAddress] = useState(null);
 
  //handle for fetching addresses on page load
  useEffect(() => {
    axios.get('http://localhost:5000/api/addresses').then((response) => {
      setAddresses(response.data);
    });
  }, []);
 
  //handle for deleting address
  const handleDelete = (addressId) => {
    axios
      .delete(`http://localhost:5000/api/addresses/${addressId}`)
      .then(() => {
        axios.get('http://localhost:5000/api/addresses').then((response) => {
          setAddresses(response.data);
        });
      })
      .catch((error) => {
        console.error('Error deleting address: ', error);
      });
  };
 
  //handle for setting address to be deleted
  const handleEdit = (address) => {
    setSelectedAddress(address);
  };
 
  //handle for updating address
  const handleUpdate = (updatedAddress) => {
    axios
      .put(`http://localhost:5000/api/addresses/${updatedAddress._id}`, updatedAddress)
      .then(() => {
        axios.get('http://localhost:5000/api/addresses').then((response) => {
          setAddresses(response.data);
          setSelectedAddress(null);
        });
      })
      .catch((error) => {
        console.error('Error updating address: ', error);
      });
  };
 
  return (
    <div className='container container-fluid min-vh-100 justify-content-center'>
      <h2 className='display-2 text-center'>Address Book</h2>
      < Navigation />
      {selectedAddress && (
        <div>
          <h2>Edit Address</h2>
          <form onSubmit={() => handleUpdate(selectedAddress)}>
            <div className="form-group">
              <label>Name:</label>
              <input
                type="text"
                className="form-control"
                name="name"
                value={selectedAddress.name}
                onChange={(e) =>
                  setSelectedAddress({
                    ...selectedAddress,
                    name: e.target.value,
                  })
                }
                required
              />
            </div>
            <div className="form-group">
              <label>Email:</label>
              <input
                type="email"
                className="form-control"
                name="email"
                value={selectedAddress.email}
                onChange={(e) =>
                  setSelectedAddress({
                    ...selectedAddress,
                    email: e.target.value,
                  })
                }
                required
              />
            </div>
            <div className="form-group">
              <label>Phone:</label>
              <input
                type="text"
                className="form-control"
                name="phone"
                value={selectedAddress.contact}
                onChange={(e) =>
                  setSelectedAddress({
                    ...selectedAddress,
                    contact: e.target.value,
                  })
                }
                required
              />
            </div>
            <div className="form-group">
              <label>Address:</label>
              <input
                type="text"
                className="form-control"
                name="name"
                value={selectedAddress.address}
                onChange={(e) =>
                  setSelectedAddress({
                    ...selectedAddress,
                    address: e.target.value,
                  })
                }
                required
              />
            </div>
            <div>
              <button type="submit" className="btn btn-primary m-2">
                Update Address
              </button>
            </div>
          </form>
        </div>
      )}
      <ul>
        {addresses.map((address) => (
          <div className='container border border-dark rounded m-2 p-2 text-right' key={address._id}>
            <h5>Name : {address.name}</h5>
            <h5>Email : {address.email}</h5>
            <h5>Contact : {address.contact}</h5>
            <h5>Address : {address.address}</h5>
            <button
              className="btn btn-sm"
              onClick={() => handleDelete(address._id)}
            >
              <h5>< Trash /></h5>
            </button>
            <button
              type="button" className="btn"
              onClick={() => handleEdit(address)}
            >
              <h5>< PencilSquare /></h5>
            </button>
          </div>
        ))}
      </ul>
    </div>
  );
};
 
export default AddressList;


Javascript




//Navigation.js
import React from 'react';
import { useNavigate } from 'react-router-dom';
import {  PlusCircleFill , JournalText} from 'react-bootstrap-icons';
 
const Navigation = () => {
  const nav = useNavigate();
  //route for home page
  const gotoHome = ()=>{
    nav('/');
  }
 
  //route for add address page
  const gotoAdd = ()=>{
    nav('/add');
  }
 
  return (
    <nav className='container d-flex flex-row m-3'>
        <h2 className='p-2 m-1' onClick={gotoHome}><JournalText /></h2>
        <h2 className='p-2 m-1' onClick={gotoAdd}><PlusCircleFill /></h2>
    </nav>
  );
};
 
export default Navigation;


Backend Code :

  • app.js: This file creates a connection between backend and frontend.
  • address.js: This file creates a schema for saving data in database.

Javascript




//app.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
 
const app = express();
 
//bodyparser used for parsing request body
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(cors());
 
 
const Address = require('./models/address');
 
//Get route for fetching addresses from database
app.get('/api/addresses', async (req, res) => {
  try {
    const addresses = await Address.find();
    res.json(addresses);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});
 
//Post route for storing new address
app.post('/api/addresses', async (req, res) => {
  try {
    const newAddress = new Address(req.body);
    const savedAddress = await newAddress.save();
    res.status(201).json(savedAddress);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});
 
//Put route for updating address with new data
app.put('/api/addresses/:id', async (req, res) => {
  try {
    const updatedAddress = await Address.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true }
    );
    res.json(updatedAddress);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});
 
//Delete route for deleting address with specified id
app.delete('/api/addresses/:id', async (req, res) => {
  try {
    await Address.findByIdAndRemove(req.params.id);
    res.json({ message: 'Address deleted' });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});
 
const PORT = 5000;
app.listen(PORT, () => {
  console.log(`Server is started on port ${PORT}`);
});


Javascript




//address.js
const mongoose = require('mongoose');
 
//address schema for storing address
const addressSchema = new mongoose.Schema({
  name: String,
  email: String,
  contact: String,
  address : String
});
 
module.exports = mongoose.model('Address', addressSchema , 'addresses');


Steps to run the application :

Steps for running frontend code :

Step 1: Open terminal inside frontend folder and run below command .

npm start 

Step 2: The browser will automatically open tab with home page.

Steps for running backend code :

Step 1: For running application first lets add start script inside package.json of backend. add line for nodemon start script.

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon app.js"
}

Step 2: Open another terminal inside backend folder and run below command.

npm start

Output :

ezgif-2-8b0ab74a68

The saved data in database will look like:

gfg



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads