Organizing your vehicle data and keeping the information updated is very necessary for managing your cars. In this article, we'll explore the process of building a scalable car vault system using the MERN stack – MongoDB, Express, React, and Node.
Preview of final output: Let us have a look at how the final output will look like.

Approach to create vehicle tracker
- In the app we have used MongoDB to store the data.
- There is some initial sample data of the cars are added in the code.
- You can do CRUD operation on the data with the help of buttons and forms provided.
- By clicking on the add vehicle button you will be able to add new entry of a vehicle.
- In the frontend App.js is the main component which is used to fetch data from the backend with the help of Axios.
- There are various components for each part which all together do the functioning.
- There is an option for filtering the vehicle on the basis of name, milage and distance covered.
- There is an option for contact owner, delete and update vehicle information on the card.
Project Structure

Steps to create the application
Step 1: Open the root directory in vs code and create a folder `server` and initialize the express application.
cd server
npm init -y
Step 2: Install the required dependencies
npm install express mongoose body-parser cors nodemonDependencies(Backend):
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.3",
"nodemon": "^3.0.2"
}
Example: Create files server.js and seedData.js and add the required codes.
//server.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const seedData = require('./seedData')
const cors = require('cors')
const app = express();
app.use(cors())
const PORT = process.env.PORT || 3001;
// Connect to MongoDB (make sure MongoDB is running)
mongoose.connect('mongodb://localhost:27017/vehicle-tracking', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
// Middleware
app.use(bodyParser.json());
// MongoDB Schema for Car
const carSchema = new mongoose.Schema({
companyName: String,
distanceCovered: Number,
mileage: Number,
serviceDates: [Date],
owner: {
name: String,
email: String,
},
image: String,
});
const Car = mongoose.model('Car', carSchema);
// Function to seed data
const seedDatabase = async () => {
try {
const existingCars = await Car.find();
if (existingCars.length > 0) {
// If there are existing cars, delete them
await Car.deleteMany();
console.log('Existing cars deleted.');
}
// Seed the database with new data
await Car.create(seedData);
console.log('Seed data added to the database.');
} catch (error) {
console.error('Error seeding the database:', error);
}
};
// Call the seedDatabase function when the server starts
seedDatabase();
// API Endpoints
// Get all cars
app.get('/api/cars', async (req, res) => {
try {
const cars = await Car.find();
res.json(cars);
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Get a specific car by ID
app.get('/api/cars/:id', async (req, res) => {
try {
const car = await Car.findById(req.params.id);
if (!car) {
return res.status(404).json({ error: 'Car not found' });
}
res.json(car);
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Add a new car
app.post('/api/cars', async (req, res) => {
try {
const newCar = await Car.create(req.body);
res.status(201).json(newCar);
} catch (error) {
res.status(400).json({ error: 'Bad Request' });
}
});
// Update a car by ID
app.put('/api/cars/:id', async (req, res) => {
try {
const updatedCar = await Car.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true }
);
if (!updatedCar) {
return res.status(404).json({ error: 'Car not found' });
}
res.json(updatedCar);
} catch (error) {
res.status(400).json({ error: 'Bad Request' });
}
});
// Delete a car by ID
app.delete('/api/cars/:id', async (req, res) => {
try {
const deletedCar = await Car.findByIdAndDelete(req.params.id);
if (!deletedCar) {
return res.status(404).json({ error: 'Car not found' });
}
console.log('car is deleted successfully');
res.json({ message: 'Car deleted successfully' });
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Start the server
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
// seedData.js
module.exports = [
{
companyName: 'Toyota',
distanceCovered: 10000,
mileage: 25,
serviceDates: [new Date('2022-01-15'), new Date('2022-03-20')],
owner: {
name: 'John Doe',
email: 'john.doe@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122182422/images1.jpg',
},
{
companyName: 'Honda',
distanceCovered: 8000,
mileage: 28,
serviceDates: [new Date('2022-02-10'), new Date('2022-04-25')],
owner: {
name: 'Jane Smith',
email: 'jane.smith@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122184958/images2.jpg',
},
{
companyName: 'Volkswagen',
distanceCovered: 10500,
mileage: 26,
serviceDates: [new Date('2022-10-05'), new Date('2022-12-15')],
owner: {
name: 'Ava Anderson',
email: 'ava.anderson@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122184958/images2.jpg',
},
{
companyName: 'Toyota',
distanceCovered: 10000,
mileage: 25,
serviceDates: [new Date('2022-01-15'), new Date('2022-03-20')],
owner: {
name: 'John Doe',
email: 'john.doe@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122184958/images2.jpg',
},
{
companyName: 'Honda',
distanceCovered: 8000,
mileage: 28,
serviceDates: [new Date('2022-02-10'), new Date('2022-04-25')],
owner: {
name: 'Jane Smith',
email: 'jane.smith@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122182422/images1.jpg',
},
{
companyName: 'Volkswagen',
distanceCovered: 10500,
mileage: 26,
serviceDates: [new Date('2022-10-05'), new Date('2022-12-15')],
owner: {
name: 'Ava Anderson',
email: 'ava.anderson@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122182422/images1.jpg',
},
{
companyName: 'Toyota',
distanceCovered: 10000,
mileage: 25,
serviceDates: [new Date('2022-01-15'), new Date('2022-03-20')],
owner: {
name: 'John Doe',
email: 'john.doe@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122185312/images3.jpg',
},
{
companyName: 'Honda',
distanceCovered: 8000,
mileage: 28,
serviceDates: [new Date('2022-02-10'), new Date('2022-04-25')],
owner: {
name: 'Jane Smith',
email: 'jane.smith@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122185312/images3.jpg',
},
{
companyName: 'Volkswagen',
distanceCovered: 10500,
mileage: 26,
serviceDates: [new Date('2022-10-05'), new Date('2022-12-15')],
owner: {
name: 'Ava Anderson',
email: 'ava.anderson@example.com',
},
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20240122185312/images3.jpg',
},
];
Step 3: To start the server run the following command.
nodemon server.jsStep 4: Open a new terminal in project root directory and run the following command to create react app.
npx create-react-app client
cd client
Step 5: Install Axios
npm install axiosProject dependencies:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.3",
"moment": "^2.30.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
Example: Create the required files and add the below code.
/* src/App.css */
.vehicle-list {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.vehicle-card {
border: 1px solid #ddd;
border-radius: 18px;
padding: 15px;
width: -moz-fit-content;
width: fit-content;
box-shadow: 0 14px 18px rgba(0, 0, 0, 0.1);
background-color: #fff;
}
.vehicle-card h3 {
margin-bottom: 10px;
}
.vehicle-card button {
background-color: #007BFF;
color: #fff;
border: none;
padding: 8px;
cursor: pointer;
border-radius: 4px;
}
.vehicle-card button:hover {
background-color: #0056b3;
}
img {
height: 200px;
width: 300px;
border-radius: 10px;
margin-bottom: 10px;
}
.list-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 15px;
overflow-x: hidden;
}
h1,
h2 {
text-align: center;
}
.vehicle-card {
border: 1px solid #ddd;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
padding: 20px;
margin: 20px;
width: 300px;
}
.vehicle-card h2 {
margin-bottom: 15px;
color: #333;
}
.vehicle-card label {
display: block;
margin-bottom: 10px;
color: #333;
}
.vehicle-card input {
width: 100%;
padding: 8px;
margin-bottom: 10px;
box-sizing: border-box;
}
.vehicle-card button {
background-color: #007BFF;
color: #fff;
border: none;
padding: 10px;
cursor: pointer;
border-radius: 4px;
}
.vehicle-card button:hover {
background-color: #0056b3;
}
.form-container {
max-width: 300px;
margin: 20px auto;
padding: 20px;
background-color: #f7f7f7;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.form-row {
display: flex;
gap: 20px;
margin-bottom: 15px;
}
.form-row label {
flex: 1;
}
.form-row input {
flex: 2;
padding: 8px;
width: 100%;
box-sizing: border-box;
}
button {
background-color: #007BFF;
color: #fff;
border: none;
padding: 10px;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background-color: #0056b3;
}
form {
padding: 10px;
}
.gfg {
background-color: green;
text-align: center;
color: white;
padding: 15px;
border-radius: 10px;
margin-bottom: -20px;
}
.list {
margin-top: -50px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.vehicle-image {
width: 100%;
border-radius: 10px;
transition: transform 0.3s ease-in-out;
}
.vehicle-image:hover {
transform: scale(1.1);
}
.button-container {
display: flex;
justify-content: space-evenly;
margin-top: 15px;
}
.main-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
// src/App.js
import React, { useState, useEffect } from "react";
import VehicleList from "./components/VehicleList";
import AddVehicle from "./components/AddVehicle";
import axios from "axios";
import "./App.css";
const App = () => {
const [vehicles, setVehicles] = useState([]);
const [showForm, setShowForm] = useState(false);
useEffect(() => {
axios
.get("http://localhost:3001/api/cars")
.then((response) => setVehicles(response.data))
.catch((error) => console.error(error));
}, []);
const handleAddVehicle = (newVehicle) => {
console.log("new vehicle", newVehicle);
setVehicles((prevVehicles) => [...prevVehicles, newVehicle]);
};
const handleContactOwner = (email) => {
alert(`Contacting the owner of the vehicle at ${email}`);
};
const handleDeleteVehicle = (vehicleId) => {
console.log(`Deleting ${vehicleId}`);
axios
.delete(`http://localhost:3001/api/cars/$%7BvehicleId%7D%60)
.then((response) => {
// Filter out the deleted vehicle from the state
setVehicles((prevVehicles) =>
prevVehicles.filter((vehicle) => vehicle._id !== vehicleId)
);
})
.catch((error) => console.error(error));
};
const handleUpdateVehicle = async (updatedVehicle) => {
try {
const response = await axios.put(
`
http://localhost:3001/api/cars/$%7BupdatedVehicle._id%7D%60,
updatedVehicle
);
// Handle the response
console.log("Vehicle updated successfully:", response.data);
// Update the vehicles array with the updated vehicle
setVehicles((prevVehicles) =>
prevVehicles.map((vehicle) =>
vehicle._id === updatedVehicle._id ? response.data : vehicle
)
);
} catch (error) {
// Handle errors, e.g., show an error message to the user
console.error("Error updating vehicle:", error);
}
};
return (
<div className="main-container">
<h1 className="gfg">GFG</h1>
<h1>Vehicle Tracking System</h1>
<button onClick={() => setShowForm(!showForm)}>
{showForm ? "Close" : "Add New Vehicle"}
</button>
<div className="">
{showForm && <AddVehicle onAddVehicle={handleAddVehicle} />}
{vehicles && (
<VehicleList
onDeleteVehicle={handleDeleteVehicle}
onUpdateVehicle={handleUpdateVehicle}
vehicles={vehicles}
onContactOwner={handleContactOwner}
/>
)}
</div>
</div>
);
};
export default App;
//src/components/AddVehicle.js
import axios from "axios";
import React, { useState } from "react";
const AddVehicle = ({ onAddVehicle }) => {
const [newVehicle, setNewVehicle] = useState({
companyName: "",
distanceCovered: "",
mileage: "",
serviceDates: "",
owner: {
name: "",
email: "",
},
image: "",
});
const handleAddVehicle = () => {
// Submit a new vehicle
axios
.post("http://localhost:3001/api/cars", newVehicle)
.then((response) => {
// Notify the parent component about the new vehicle
onAddVehicle(response.data);
// Clear the newVehicle state for the next entry
setNewVehicle({
companyName: "",
distanceCovered: "",
mileage: "",
serviceDates: [],
owner: {
name: "",
email: "",
},
image: "", // Reset image URL field
});
})
.catch((error) => console.error(error));
};
return (
<div className="form-container">
<h2 style={{ color: "#007BFF", textAlign: "center" }}>
Add a New Vehicle
</h2>
<form
onSubmit={(e) => {
e.preventDefault();
handleAddVehicle();
}}
>
<div className="form-row">
<label>
Company Name:
<input
type="text"
value={newVehicle.companyName}
onChange={(e) =>
setNewVehicle({
...newVehicle,
companyName: e.target.value,
})
}
required
className="form-input"
/>
</label>
<label>
Distance Covered:
<input
type="number"
value={newVehicle.distanceCovered}
onChange={(e) =>
setNewVehicle({
...newVehicle,
distanceCovered: e.target.value,
})
}
required
className="form-input"
/>
</label>
</div>
<div className="form-row">
<label>
Mileage:
<input
type="number"
value={newVehicle.mileage}
onChange={(e) =>
setNewVehicle({
...newVehicle,
mileage: e.target.value,
})
}
required
className="form-input"
/>
</label>
<label>
Service Dates (comma-separated):
<input
type="text"
value={newVehicle.serviceDates}
onChange={(e) =>
setNewVehicle({
...newVehicle,
serviceDates: e.target.value,
})
}
required
className="form-input"
/>
</label>
</div>
<div className="form-row">
<label>
Owner Name:
<input
type="text"
value={newVehicle.owner.name}
onChange={(e) =>
setNewVehicle({
...newVehicle,
owner: {
...newVehicle.owner,
name: e.target.value,
},
})
}
required
className="form-input"
/>
</label>
<label>
Owner Email:
<input
type="email"
value={newVehicle.owner.email}
onChange={(e) =>
setNewVehicle({
...newVehicle,
owner: {
...newVehicle.owner,
email: e.target.value,
},
})
}
required
className="form-input"
/>
</label>
</div>
<div className="form-row">
<label>
Image URL:
<input
type="text"
value={newVehicle.image}
onChange={(e) =>
setNewVehicle({
...newVehicle,
image: e.target.value,
})
}
className="form-input"
/>
</label>
</div>
<button type="submit" className="form-button">
Add Vehicle
</button>
</form>
</div>
);
};
export default AddVehicle;
// src/components/VehicleList.js
import React, { useState, useMemo } from "react";
import VehicleCard from "./VehicleCard";
const VehicleList = ({
vehicles,
onContactOwner,
onDeleteVehicle,
onUpdateVehicle,
}) => {
const [companyFilter, setCompanyFilter] = useState("");
const [sortBy, setSortBy] = useState("distanceCovered"); // Default sorting by distanceCovered
const filteredAndSortedVehicles = useMemo(() => {
return vehicles
.filter((vehicle) =>
vehicle.companyName
.toLowerCase()
.includes(companyFilter.toLowerCase())
)
.sort((a, b) => (a[sortBy] > b[sortBy] ? 1 : -1));
}, [vehicles, companyFilter, sortBy]);
return (
<div className="list" style={{ marginTop: "20px" }}>
<h2 style={{ color: "#007BFF" }}>Vehicle List</h2>
{/* Filter and Sort Controls */}
<div style={{ marginBottom: "10px" }}>
<label>
Filter by Company Name:
<input
type="text"
value={companyFilter}
onChange={(e) => setCompanyFilter(e.target.value)}
/>
</label>
<label style={{ marginLeft: "10px" }}>
Sort by:
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
>
<option value="distanceCovered">
Distance Covered
</option>
<option value="mileage">Mileage</option>
</select>
</label>
</div>
<div className="list-container">
{filteredAndSortedVehicles.map((vehicle) => (
<VehicleCard
key={vehicle._id}
vehicle={vehicle}
onContactOwner={onContactOwner}
onDeleteVehicle={onDeleteVehicle}
onUpdateVehicle={onUpdateVehicle}
/>
))}
</div>
</div>
);
};
export default VehicleList;
// src/components/VehicleCard.js
import React, { useState } from "react";
import moment from "moment";
const VehicleCard = ({
vehicle,
onContactOwner,
onDeleteVehicle,
onUpdateVehicle,
}) => {
const [isEditing, setIsEditing] = useState(false);
const [updatedVehicle, setUpdatedVehicle] = useState(vehicle);
const handleUpdateClick = () => {
setIsEditing(true);
};
const handleCancelClick = () => {
setIsEditing(false);
setUpdatedVehicle(vehicle); // Reset to original values
};
const handleSaveClick = () => {
// Implement the logic to save the updated details
onUpdateVehicle(updatedVehicle);
setIsEditing(false);
};
const handleInputChange = (fieldName, value) => {
const [field, subField] = fieldName.split(".");
setUpdatedVehicle((prevVehicle) => ({
...prevVehicle,
[field]: subField
? { ...prevVehicle[field], [subField]: value }
: value,
}));
};
return (
<div className="vehicle-card">
{isEditing ? (
// Render editable fields for updating details
<div>
<label>
Company Name:
<input
type="text"
value={updatedVehicle.companyName}
onChange={(e) =>
handleInputChange("companyName", e.target.value)
}
required
/>
</label>
<label>
Distance Covered:
<input
type="number"
value={updatedVehicle.distanceCovered}
onChange={(e) =>
handleInputChange(
"distanceCovered",
e.target.value
)
}
required
/>
</label>
<label>
Mileage:
<input
type="number"
value={updatedVehicle.mileage}
onChange={(e) =>
handleInputChange("mileage", e.target.value)
}
required
/>
</label>
<label>
Owner Name:
<input
type="text"
value={updatedVehicle.owner.name}
onChange={(e) =>
handleInputChange("owner.name", e.target.value)
}
required
/>
</label>
<label>
Owner Email:
<input
type="email"
value={updatedVehicle.owner.email}
onChange={(e) =>
handleInputChange("owner.email", e.target.value)
}
required
/>
</label>
{/* Add input fields for other properties like
owner name, owner email, and image */}
<button onClick={handleSaveClick}>Save</button>
<button onClick={handleCancelClick}>Cancel</button>
</div>
) : (
// Display details
<div>
<h3 style={{ fontWeight: "bold" }}>
{vehicle.companyName}
</h3>
<p>
<span style={{ fontWeight: "bold" }}>
Distance Covered:
</span>{" "}
{vehicle.distanceCovered}
</p>
<p>
<span style={{ fontWeight: "bold" }}>Mileage:
</span>{" "}
{vehicle.mileage}
</p>
{vehicle.owner && (
<div>
<p style={{ fontWeight: "bold" }}>Owner:</p>
<ul style={{ listStyle: "none", padding: 0 }}>
<li>{vehicle.owner.name}</li>
<li>{vehicle.owner.email}</li>
</ul>
</div>
)}
{vehicle.image && (
<div className="image-container">
<img
src={vehicle.image}
alt={vehicle.companyName}
className="vehicle-image"
/>
</div>
)}
<p>Service Dates:</p>
<ul>
{vehicle.serviceDates.map((item, i) => (
<li key={i}>
{moment(item).format("MMMM D, YYYY")}
</li>
))}
</ul>
<div className="button-container">
<button
onClick={() =>
onContactOwner(
vehicle.owner ? vehicle.owner.email : ""
)
}
>
Contact Owner
</button>
<button onClick={() => onDeleteVehicle(vehicle._id)}>
Delete Vehicle
</button>
<button onClick={handleUpdateClick}>Update</button>
</div>
</div>
)}
</div>
);
};
export default VehicleCard;
Step 6: To start the frontend run the following command.
npm start
Output: