Taxi Booking Backend (Uber/Rapido Clone)

 Perfect 🚖 — let’s design a Taxi Booking App Backend (like Uber, Rapido) for practice.

This kind of project is real-world and advanced: it involves authentication, geolocation, ride booking, role-based access, and real-time status updates.


🚀 Example 4: Taxi Booking Backend (Uber/Rapido Clone)


📂 Project Structure

taxi-app-backend/
│── src/
│   ├── config/
│   │   └── db.js
│   ├── controllers/
│   │   ├── authController.js
│   │   ├── rideController.js
│   │   └── driverController.js
│   ├── middleware/
│   │   ├── authMiddleware.js
│   │   └── errorMiddleware.js
│   ├── models/
│   │   ├── User.js
│   │   ├── Driver.js
│   │   └── Ride.js
│   ├── routes/
│   │   ├── authRoutes.js
│   │   ├── rideRoutes.js
│   │   └── driverRoutes.js
│   ├── app.js
│   └── server.js
│── .env
│── package.json

📦 Install Dependencies

npm init -y
npm install express bcryptjs jsonwebtoken dotenv mongoose
npm install joi helmet cors morgan socket.io
npm install nodemon --save-dev

⚙️ src/config/db.js

const mongoose = require("mongoose");

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGO_URI);
    console.log("MongoDB connected");
  } catch (err) {
    console.error(err.message);
    process.exit(1);
  }
};

module.exports = connectDB;

👤 src/models/User.js

const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, unique: true, required: true },
  password: { type: String, required: true },
  role: { type: String, enum: ["Rider", "Driver", "Admin"], default: "Rider" },
}, { timestamps: true });

module.exports = mongoose.model("User", userSchema);

🚖 src/models/Driver.js

const mongoose = require("mongoose");

const driverSchema = new mongoose.Schema({
  user: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
  vehicleNumber: { type: String, required: true },
  isAvailable: { type: Boolean, default: true },
  currentLocation: {
    lat: { type: Number },
    lng: { type: Number }
  }
}, { timestamps: true });

module.exports = mongoose.model("Driver", driverSchema);

🏁 src/models/Ride.js

const mongoose = require("mongoose");

const rideSchema = new mongoose.Schema({
  rider: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
  driver: { type: mongoose.Schema.Types.ObjectId, ref: "Driver" },
  pickup: {
    lat: { type: Number, required: true },
    lng: { type: Number, required: true }
  },
  drop: {
    lat: { type: Number, required: true },
    lng: { type: Number, required: true }
  },
  status: { 
    type: String, 
    enum: ["Requested", "Accepted", "Ongoing", "Completed", "Cancelled"], 
    default: "Requested" 
  },
  fare: { type: Number }
}, { timestamps: true });

module.exports = mongoose.model("Ride", rideSchema);

🔑 src/controllers/authController.js

const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../models/User");

exports.register = async (req, res, next) => {
  try {
    const { name, email, password, role } = req.body;
    const hashed = await bcrypt.hash(password, 10);
    const user = await User.create({ name, email, password: hashed, role });
    res.status(201).json({ message: "User registered", user });
  } catch (err) {
    next(err);
  }
};

exports.login = async (req, res, next) => {
  try {
    const { email, password } = req.body;
    const user = await User.findOne({ email });
    if (!user) return res.status(401).json({ message: "Invalid credentials" });

    const valid = await bcrypt.compare(password, user.password);
    if (!valid) return res.status(401).json({ message: "Invalid credentials" });

    const token = jwt.sign({ id: user._id, role: user.role }, process.env.JWT_SECRET, {
      expiresIn: "1h",
    });

    res.json({ token });
  } catch (err) {
    next(err);
  }
};

📲 src/controllers/rideController.js

const Ride = require("../models/Ride");
const Driver = require("../models/Driver");

exports.requestRide = async (req, res, next) => {
  try {
    const { pickup, drop } = req.body;
    const ride = await Ride.create({ rider: req.user.id, pickup, drop });
    res.status(201).json(ride);
  } catch (err) {
    next(err);
  }
};

exports.assignDriver = async (req, res, next) => {
  try {
    const { rideId, driverId } = req.body;
    const ride = await Ride.findById(rideId);
    const driver = await Driver.findById(driverId);

    if (!ride || !driver) return res.status(404).json({ message: "Ride/Driver not found" });

    ride.driver = driver._id;
    ride.status = "Accepted";
    await ride.save();

    res.json(ride);
  } catch (err) {
    next(err);
  }
};

exports.updateRideStatus = async (req, res, next) => {
  try {
    const { rideId, status } = req.body;
    const ride = await Ride.findById(rideId);
    if (!ride) return res.status(404).json({ message: "Ride not found" });

    ride.status = status;
    if (status === "Completed") {
      // Dummy fare calculation
      ride.fare = Math.floor(Math.random() * 200) + 50;
    }
    await ride.save();

    res.json(ride);
  } catch (err) {
    next(err);
  }
};

🔐 src/middleware/authMiddleware.js

const jwt = require("jsonwebtoken");

exports.authMiddleware = (req, res, next) => {
  const token = req.headers["authorization"];
  if (!token) return res.status(401).json({ message: "No token provided" });

  try {
    const decoded = jwt.verify(token.split(" ")[1], process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ message: "Invalid token" });
  }
};

🚦 Routes

src/routes/authRoutes.js

const express = require("express");
const { register, login } = require("../controllers/authController");
const router = express.Router();

router.post("/register", register);
router.post("/login", login);

module.exports = router;

src/routes/rideRoutes.js

const express = require("express");
const { requestRide, assignDriver, updateRideStatus } = require("../controllers/rideController");
const { authMiddleware } = require("../middleware/authMiddleware");
const router = express.Router();

router.post("/request", authMiddleware, requestRide);
router.post("/assign", authMiddleware, assignDriver);
router.post("/status", authMiddleware, updateRideStatus);

module.exports = router;

🌍 src/app.js

const express = require("express");
const dotenv = require("dotenv");
const helmet = require("helmet");
const morgan = require("morgan");

const authRoutes = require("./routes/authRoutes");
const rideRoutes = require("./routes/rideRoutes");
const { errorHandler } = require("./middleware/errorMiddleware");

dotenv.config();
const app = express();

app.use(express.json());
app.use(helmet());
app.use(morgan("dev"));

app.use("/api/auth", authRoutes);
app.use("/api/rides", rideRoutes);

app.use(errorHandler);

module.exports = app;

🖥️ src/server.js

const app = require("./app");
const connectDB = require("./config/db");

const PORT = process.env.PORT || 5000;

connectDB();
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

🔑 .env

PORT=5000
MONGO_URI=mongodb://localhost:27017/taxiapp
JWT_SECRET=supersecretkey

✅ Features Covered

  1. User Auth (Rider/Driver/Admin with JWT).

  2. Ride Requests (pickup & drop points stored).

  3. Driver Assignment (admin/logic assigns driver).

  4. Ride Status Flow (Requested → Accepted → Ongoing → Completed).

  5. Fare Calculation (dummy logic, can expand with distance/time APIs).

  6. Role-based Access (drivers & riders have different roles).

  7. Real-Time Ready (can extend with Socket.io for live ride updates).


Extra Challenge (if time left in exam):

  • Integrate Google Maps API for distance & fare calculation.

  • Use Socket.io for live driver location updates.

  • Add payment gateway integration (Stripe/PayPal).


Would you like me to prepare a 3-hour exam roadmap for this Taxi App (step-by-step order: setup → auth → rides → drivers → extras) so you can simulate the real test?

Previous Post Next Post