/how-to-build-replit

How to Build a Ride hailing platform with Replit

Learn how to build a ride-hailing platform using Replit. Follow our step-by-step guide to create and launch your own successful app easily.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

How to Build a Ride hailing platform with Replit

You can absolutely build a ride-hailing prototype on Replit by combining a backend (Node.js + Express) and a frontend (React or simple HTML/JS). You’ll use Replit’s built-in web server as the backend host, store your credentials in Replit Secrets, and optionally connect MongoDB Atlas for persistent data. The main idea: the driver and passenger apps talk to the same backend through REST APIs — drivers send their location, passengers request rides, and the server matches them. Replit is great for getting this MVP working end-to-end, but not meant for handling real-time scale like Uber. Still, for a working demo with authentication, ride requests, and a map integration (via Google Maps API), it works beautifully when you build it step by step.

 

Project Structure in Replit

 

Start a new Node.js Repl (not HTML/CSS/JS, since you need a backend). Once created, you’ll have a main file named index.js. We’ll add folders for clarity:

  • backend/ → For API routes and database logic.
  • frontend/ → For your web UI (either React or plain HTML).
  • .replit → To specify how to run the project if needed.
  • package.json → Auto-generated – manages dependencies.

 

// Inside the Replit shell, run this to create folders
mkdir backend frontend

 

Setup Express Server

 

Open index.js (the main file) and replace the content with this code:

// index.js
import express from "express"
import cors from "cors"

const app = express()
const port = process.env.PORT || 3000

app.use(cors()) // Allow frontend to call backend
app.use(express.json()) // Parse JSON bodies

// Simple test route
app.get("/", (req, res) => {
  res.send("Ride-hailing backend is running!")
})

// Import routes for riders and drivers
import "./backend/rides.js"

app.listen(port, () => {
  console.log(`Server is listening on port ${port}`)
})

 

This starts your backend. Replit will automatically expose it through a public URL shown at the top ("Open in a new tab").

 

Add Ride Logic

 

Create a new file backend/rides.js. This file tracks ride requests temporarily in memory (in real usage, you’d connect a database).

// backend/rides.js
import express from "express"
import { Router } from "express"

const router = Router()

// Temporary store for demo purposes
let rides = []
let drivers = []

// Passenger requests a ride
router.post("/request", (req, res) => {
  const { passengerName, pickup } = req.body
  const ride = { id: Date.now(), passengerName, pickup, status: "waiting" }
  rides.push(ride)
  res.json({ message: "Ride requested", ride })
})

// Driver updates location or marks availability
router.post("/driver/update", (req, res) => {
  const { driverName, location } = req.body
  const existing = drivers.find(d => d.name === driverName)
  if (existing) {
    existing.location = location
  } else {
    drivers.push({ name: driverName, location })
  }
  res.json({ message: "Driver location updated" })
})

// List available rides
router.get("/rides", (req, res) => {
  res.json(rides)
})

export default router

 

Make sure in index.js, you import this route file after setting up Express. In Replit, ES modules are allowed, so your type in package.json should be "module":

{
  "name": "ride-hailing",
  "type": "module",
  "dependencies": {
    "express": "^4.18.2",
    "cors": "^2.8.5"
  }
}

 

Frontend (for passengers and drivers)

 

If you don’t want React, a simple frontend/index.html works fine to test the API:

<!-- frontend/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Ride App</title>
  </head>
  <body>
    <h1>Request a Ride</h1>
    <input id="name" placeholder="Your Name"/>
    <input id="pickup" placeholder="Pickup Location"/>
    <button onclick="requestRide()">Request</button>
    <pre id="output"></pre>

    <script>
      async function requestRide() {
        const name = document.getElementById("name").value
        const pickup = document.getElementById("pickup").value
        const res = await fetch("/request", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ passengerName: name, pickup })
        })
        const data = await res.json()
        document.getElementById("output").textContent = JSON.stringify(data, null, 2)
      }
    </script>
  </body>
</html>

 

In Replit, you can serve this static file easily by adding another route inside index.js before app.listen():

app.use(express.static("frontend"))

 

Using Replit Secrets

 

For sensitive info like Google Maps API keys or MongoDB credentials, never hardcode them. Instead, in Replit, click the Lock icon (Secrets tab), add keys like MAPS_API_KEY, and read them from process.env.MAPS_API_KEY in your code.

 

Database Integration (Optional)

 

To make rides persist beyond server restart, use MongoDB Atlas. In Replit Shell, install the package:

npm install mongoose

 

Then in backend/rides.js you can connect at top of file:

import mongoose from "mongoose"

mongoose.connect(process.env.MONGO_URI)
const RideSchema = new mongoose.Schema({
  passengerName: String,
  pickup: String,
  status: String
})
const Ride = mongoose.model("Ride", RideSchema)

 

Deploy & Collaborate

 

Replit automatically deploys your app: as soon as it runs, it’s live on your Replit URL. You can share it with teammates by inviting them using the “Invite” or “Collaborate” button — they can code simultaneously in real time. Keep an eye on Replit’s Shell logs for debugging; remember that sleeping Repls may stop after inactivity unless you’re on a paid plan.

 

In Practice

 

A ride-hailing platform built like this on Replit can handle small-scale demos, internal tests, or prototypes. It’s excellent for learning full-stack interactions and quickly iterating on functionality. For production-level scaling, you’d later migrate the backend to a proper hosting service and link mobile apps. But for now, with these steps, you’ll have a working, interactive ride-hailing demo that actually runs from within Replit end to end.

 

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Contact Us

How to Build a Simple Ride-Matching API with Express on Replit


const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const { v4: uuid } = require('uuid');

app.use(bodyParser.json());

// Simple in-memory data store for demo
let drivers = {};
let rideRequests = {};

// Register driver with realtime location
app.post('/drivers/register', (req, res) => {
  const { name, lat, lng } = req.body;
  const id = uuid();
  drivers[id] = { id, name, lat, lng, available: true };
  res.json({ driverId: id });
});

// Update driver location
app.post('/drivers/:id/location', (req, res) => {
  const { id } = req.params;
  const { lat, lng } = req.body;
  if (!drivers[id]) return res.status(404).json({ error: 'Driver not found' });
  drivers[id].lat = lat;
  drivers[id].lng = lng;
  res.json({ status: 'Location updated' });
});

// Customer requests a ride
app.post('/rides/request', (req, res) => {
  const { customerName, pickup, destination } = req.body;
  const id = uuid();

  // Find nearest available driver (simple distance check)
  let nearestDriver = null;
  let minDistance = Infinity;
  for (const d of Object.values(drivers)) {
    if (!d.available) continue;
    const dist = Math.hypot(d.lat - pickup.lat, d.lng - pickup.lng);
    if (dist < minDistance) {
      minDistance = dist;
      nearestDriver = d;
    }
  }

  if (!nearestDriver) return res.status(404).json({ error: 'No drivers available' });

  nearestDriver.available = false;
  rideRequests[id] = {
    id,
    customerName,
    pickup,
    destination,
    driver: nearestDriver,
    status: 'assigned'
  };

  res.json({ rideId: id, driver: nearestDriver });
});

// Complete ride
app.post('/rides/:id/complete', (req, res) => {
  const { id } = req.params;
  const ride = rideRequests[id];
  if (!ride) return res.status(404).json({ error: 'Ride not found' });
  ride.status = 'completed';
  drivers[ride.driver.id].available = true;
  res.json({ message: 'Ride completed', ride });
});

app.listen(3000, () => {
  console.log('Ride backend API running on port 3000');
});

How to Build a Route Info API for Your Ride-Hailing App


import express from "express";
import fetch from "node-fetch";

const app = express();
app.use(express.json());

// Example: calculate distance & ETA using Google Maps Distance Matrix API
app.post("/api/route-info", async (req, res) => {
  const { pickup, destination } = req.body; 
  if (!pickup || !destination)
    return res.status(400).json({ error: "Missing pickup or destination." });

  try {
    const apiKey = process.env.MAPS_API_KEY; // store this in Replit Secrets
    const params = new URLSearchParams({
      origins: `${pickup.lat},${pickup.lng}`,
      destinations: `${destination.lat},${destination.lng}`,
      key: apiKey,
    });

    const response = await fetch(`https://maps.googleapis.com/maps/api/distancematrix/json?${params}`);
    const data = await response.json();

    if (data.rows?.[0]?.elements?.[0]?.status !== "OK") {
      return res.status(400).json({ error: "Unable to fetch route data." });
    }

    const info = data.rows[0].elements[0];
    res.json({
      distance: info.distance.text,
      duration: info.duration.text,
      valueMeters: info.distance.value,
      valueSeconds: info.duration.value,
    });
  } catch (err) {
    console.error("Route Info Error:", err);
    res.status(500).json({ error: "Failed to fetch route info." });
  }
});

app.listen(3000, () => console.log("Route API running on port 3000"));

How to Securely Connect Drivers and Riders with Express and Replit Database


import express from "express";
import Database from "@replit/database";
import jwt from "jsonwebtoken";

const app = express();
const db = new Database();
app.use(express.json());

const SECRET = process.env.JWT\_SECRET;

// Middleware for verifying driver tokens
const authDriver = async (req, res, next) => {
  const token = req.headers.authorization?.split(" ")[1];
  if (!token) return res.status(401).json({ error: "No token" });
  try {
    const payload = jwt.verify(token, SECRET);
    req.driver = payload;
    next();
  } catch {
    res.status(403).json({ error: "Invalid token" });
  }
};

// Secure endpoint for drivers to update current status and location
app.post("/driver/update", authDriver, async (req, res) => {
  const { lat, lng, available } = req.body;
  const id = req.driver.id;
  if (typeof lat !== "number" || typeof lng !== "number")
    return res.status(400).json({ error: "Invalid coordinates" });

  await db.set(`driver_${id}`, { lat, lng, available });
  res.json({ message: "Driver status updated" });
});

// Secure customer endpoint to find nearby available drivers from Replit DB
app.post("/customer/search", async (req, res) => {
  const { pickup } = req.body;
  if (!pickup || typeof pickup.lat !== "number" || typeof pickup.lng !== "number")
    return res.status(400).json({ error: "Invalid pickup" });

  const allKeys = await db.list("driver\_");
  const drivers = await Promise.all(allKeys.map((k) => db.get(k)));
  const available = drivers.filter((d) => d?.available);

  const nearest = available.sort(
    (a, b) =>
      Math.hypot(a.lat - pickup.lat, a.lng - pickup.lng) -
      Math.hypot(b.lat - pickup.lat, b.lng - pickup.lng)
  )[0];

  if (!nearest)
    return res.status(404).json({ error: "No nearby drivers" });
  res.json({ driver: nearest });
});

app.listen(3000, () => console.log("Secure driver/customer API running on Replit"));

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Contact Us
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Best Practices for Building a Ride hailing platform with Replit

On Replit, the best way to build a ride-hailing platform is to separate your front-end (for users and drivers) and back-end (for routes, payments, real-time updates) inside one Repl using a clear folder structure. Use Node.js with Express for your server-side logic and a simple React or HTML/CSS/JS front-end. Replit provides a single running container, so you must design it efficiently: avoid running multiple servers, store credentials in Secrets, and rely on persistent storage through a database like PostgreSQL (via an external provider, e.g. Supabase). WebSockets (using Socket.IO) help you show live driver location updates. Use Replit’s built-in web server preview during development, and once stable, connect it to your custom domain through Replit Deployments or an external reverse proxy.

 

Project Setup

 

In Replit, create a new Node.js Repl. This gives you an index.js file and a package.json file. Organize your folders like this:

  • server/ — contains your Express API and Socket.IO setup (backend)
  • client/ — your front-end app (React or static HTML/JS)
  • .replit — runs your server startup command
  • replit.nix — defines the environment; leave default unless you add dependencies

 

// Sample folder structure
.
├── client/
│   └── index.html
│   └── main.js
│
├── server/
│   └── server.js
│   └── routes.js
│
├── package.json
└── .replit

 

Server Setup (Express + Socket.IO)

 

Inside server/server.js, create your main server that handles requests from riders and drivers. This server also broadcasts live updates like driver locations.

 

// server/server.js
import express from "express"
import { createServer } from "http"
import { Server } from "socket.io"
import cors from "cors"

const app = express()
app.use(cors())
app.use(express.json())

// Basic Rider API Example
app.post("/api/ride-request", (req, res) => {
  // req.body contains pickup location, destination etc.
  console.log("Ride requested:", req.body)
  res.json({ status: "Ride request received" })
})

// Create HTTP + WebSocket Server
const server = createServer(app)
const io = new Server(server, {
  cors: { origin: "*" }
})

// Listen for live location updates
io.on("connection", (socket) => {
  console.log("User connected:", socket.id)

  socket.on("driver-location", (data) => {
    // Broadcast driver location to waiting rider clients
    io.emit("update-location", data)
  })

  socket.on("disconnect", () => {
    console.log("User disconnected")
  })
})

const PORT = process.env.PORT || 3000
server.listen(PORT, () => console.log(`Server running on port ${PORT}`))

 

Where to put this: Create server/server.js and paste the code above. It runs both Express and Socket.IO within one process — ideal for Replit since one Repl gets one main port.

 

Front-End (Connecting a Simple Map and WebSocket)

 

Inside client/index.html and client/main.js, connect to your backend using Socket.IO to show real-time driver updates. You can serve static files directly from your Express app too.

 

<!-- client/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Ride Hailing Demo</title>
  </head>
  <body>
    <h1>Rider Screen</h1>
    <div id="map"></div>
    <script src="https://cdn.socket.io/4.6.0/socket.io.min.js"></script>
    <script src="./main.js"></script>
  </body>
</html>

 

// client/main.js
const socket = io(window.location.origin)

socket.on("update-location", (loc) => {
  console.log("Driver location update:", loc)
})

// Example: simulate driver location updates every few seconds
setInterval(() => {
  socket.emit("driver-location", {
    lat: (Math.random() * 90).toFixed(4),
    lng: (Math.random() * 180).toFixed(4)
  })
}, 3000)

 

Where to put this: In your client folder. You can also use React by initializing a React app inside client and ensuring Express serves it via app.use(express.static("client/build")) after build.

 

Secrets and Environment Variables

 

All sensitive data (like database connection strings or API tokens for payment gateways such as Stripe) should go into the Secrets tab in Replit (lock icon on left sidebar). Access them inside your server via process.env.VAR\_NAME.

 

// Example usage in server/server.js
const db_url = process.env.DB_URL

 

Database Integration (Example with External PostgreSQL)

 

Replit storage isn’t for production-grade databases. So, connect to an external provider like Supabase or Neon.tech. Add your connection string in Secrets as DB\_URL and then use it with a driver like `pg`.

 

// server/db.js
import pkg from "pg"
const { Pool } = pkg

const pool = new Pool({
  connectionString: process.env.DB_URL,
  ssl: { rejectUnauthorized: false }
})

export default pool

 

Then use it in your route file:

 

// server/routes.js
import express from "express"
import pool from "./db.js"

const router = express.Router()

router.get("/drivers", async (req, res) => {
  const { rows } = await pool.query("SELECT * FROM drivers")
  res.json(rows)
})

export default router

 

Running and Testing

 

In your .replit file, make sure you start your server from the correct file path:

 

run = "node server/server.js"

 

Then click “Run” in Replit — it will host your app and give you a web URL to preview. When testing Socket.IO or APIs, open the Replit web view link directly in your browser.

 

Common Pitfalls & Replit-Specific Advice

 

  • Don’t spawn multiple servers on Replit — you only get one main port (usually 3000). Keep Express + Socket.IO on the same instance.
  • Store all secrets in Replit's Secrets tab — never commit keys in public Repls.
  • Persistent storage like Replit’s default file system resets sometimes. Use external databases.
  • Use package versions explicitly in package.json, since Replit sometimes updates dependencies automatically.
  • Autosave is continuous — use Git integration (on left sidebar) to commit working states, especially before big changes.
  • Test collaboration carefully — only one live run instance per Repl; share editing but not simultaneous serving.

 

Deploying Beyond Development

 

For production, either use Replit Deployments (Always On) or export your code to an external host (Render, Fly.io, etc.). Replit Deployments keep your server running, but for scale, external hosting is often cheaper per connection if you expect many drivers and riders.

 

Following this structure, you’ll get a clean, maintainable ride-hailing platform prototype: an Express API, Socket.IO-powered live updates, and a front-end all running nicely inside Replit’s limitations while still keeping realistic room for scaling later.

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022