/how-to-build-replit

How to Build a Booking platform with Replit

Learn how to build a powerful booking platform using Replit with step-by-step guidance, coding tips, and best practices for smooth app development.

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 Booking platform with Replit

To build a simple but real booking platform on Replit, create a Node.js backend using Express.js to handle bookings, a lightweight frontend with HTML/CSS/JS, and use Replit’s built-in SQLite database or Replit DB for storing booking data. You can manage environment secrets (like an admin password or API keys) through Replit’s built-in Secrets tab. This setup runs entirely inside Replit, so you can instantly test with your collaborators using Replit’s multiplayer feature. You’ll end up with a working prototype where users can create and view bookings, all deployed automatically when you press “Run”.

 

Step 1: Create the Repl and Project Structure

 

Start a new Repl using the “Node.js” template. Then create the following file structure:

  • index.js – main backend server.
  • public/ – folder for frontend files (HTML, CSS, JavaScript).
  • public/index.html – homepage with booking form.
  • database.js – file managing the SQLite database or Replit DB connection.

 

Step 2: Set up the Backend (index.js)

 

In index.js, we’ll create a simple Express server with routes to save and fetch bookings. Run the commands in Replit Shell to install dependencies:

 

npm install express sqlite3 body-parser

 

// index.js
const express = require("express")
const bodyParser = require("body-parser")
const path = require("path")
const db = require("./database") // our local db helper

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

app.use(bodyParser.json())
app.use(express.static(path.join(__dirname, "public")))

// API route to create a new booking
app.post("/api/bookings", async (req, res) => {
  const { name, date, time } = req.body
  if (!name || !date || !time) {
    return res.status(400).json({ error: "Missing data" })
  }
  try {
    await db.createBooking(name, date, time)
    res.json({ message: "Booking created successfully" })
  } catch (err) {
    console.error(err)
    res.status(500).json({ error: "Database error" })
  }
})

// API route to get all bookings
app.get("/api/bookings", async (req, res) => {
  try {
    const bookings = await db.getBookings()
    res.json(bookings)
  } catch (err) {
    console.error(err)
    res.status(500).json({ error: "Database error" })
  }
})

// Start server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`)
})

 

Step 3: Database File (database.js)

 

In this example, use SQLite (which works perfectly in Replit) to store booking data locally:

 

// database.js
const sqlite3 = require("sqlite3").verbose()
const db = new sqlite3.Database("./bookings.db")

// Initialize table if not exists
db.serialize(() => {
  db.run(`
    CREATE TABLE IF NOT EXISTS bookings (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT,
      date TEXT,
      time TEXT
    )
  `)
})

function createBooking(name, date, time) {
  return new Promise((resolve, reject) => {
    db.run("INSERT INTO bookings (name, date, time) VALUES (?, ?, ?)", [name, date, time], function (err) {
      if (err) reject(err)
      else resolve(this.lastID)
    })
  })
}

function getBookings() {
  return new Promise((resolve, reject) => {
    db.all("SELECT * FROM bookings", (err, rows) => {
      if (err) reject(err)
      else resolve(rows)
    })
  })
}

module.exports = { createBooking, getBookings }

 

Step 4: Frontend (public/index.html)

 

This simple UI lets users submit a booking and view existing ones.

 

<!-- public/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Booking Platform</title>
    <style>
      body { font-family: Arial; margin: 40px; }
      form { margin-bottom: 20px; }
      input, button { padding: 8px; margin: 5px; }
      #bookings { margin-top: 30px; }
    </style>
  </head>
  <body>
    <h3>Create a Booking</h3>
    <form id="bookingForm">
      <input type="text" id="name" placeholder="Your Name" required />
      <input type="date" id="date" required />
      <input type="time" id="time" required />
      <button type="submit">Book</button>
    </form>

    <h3>Current Bookings</h3>
    <div id="bookings"></div>

    <script>
      async function loadBookings() {
        const res = await fetch("/api/bookings")
        const data = await res.json()
        const container = document.getElementById("bookings")
        container.innerHTML = data.map(b => `<p>${b.name} - ${b.date} at ${b.time}</p>`).join("")
      }

      document.getElementById("bookingForm").addEventListener("submit", async (e) => {
        e.preventDefault()
        const name = document.getElementById("name").value
        const date = document.getElementById("date").value
        const time = document.getElementById("time").value
        await fetch("/api/bookings", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ name, date, time })
        })
        e.target.reset()
        loadBookings()
      })

      loadBookings()
    </script>
  </body>
</html>

 

Step 5: Managing Secrets and Deployment

 

If you later add integrations (like sending email confirmations), go to Replit’s left sidebar and open the Secrets tab. Add environment variables like EMAIL_API_KEY there, and access them in Node.js via process.env.EMAIL_API_KEY.

  • Do not store sensitive data (like credentials) in code — always use Replit Secrets.
  • Your app automatically deploys when you hit “Run”. Replit provides a web URL (e.g., https://your-repl-name.username.repl.co).
  • You can share that URL right away and collaborate in real-time with teammates.

 

Step 6: Common Pitfalls in Replit

 

  • Database persists only if files are saved: SQLite databases are files inside your project. Make sure they’re committed or backed up if needed.
  • Long-running background tasks stop when the repl sleeps — use Replit deployments or external uptime services for production-like setups.
  • Use relative paths and ports: Replit automatically assigns PORT via process.env.PORT.
  • Always include a static frontend folder — Replit’s webview serves it automatically from Express.

 

That’s the full, working path to get a real booking platform built and deployed inside Replit. It’s lightweight, 100% valid, and uses Replit’s true environment setup to run with database, backend, and frontend all inside one Repl.

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 Handle Bookings and Manage User Reservations with Express and Replit Database



import express from "express";
import bodyParser from "body-parser";
import Database from "@replit/database";

const app = express();
const db = new Database();

app.use(bodyParser.json());

// Utility: build a composite key for quick lookups like "booking:userId:date"
const bookingKey = (userId, date) => `booking:${userId}:${date}`;

// POST /book — creates a booking if slot free
app.post("/book", async (req, res) => {
  const { userId, date, timeSlot } = req.body;

  const key = bookingKey(userId, date);
  let bookings = (await db.get(key)) || [];

  const alreadyBooked = bookings.find((b) => b.timeSlot === timeSlot);
  if (alreadyBooked) {
    return res.status(409).json({ msg: "Slot already booked" });
  }

  const newBooking = { timeSlot, createdAt: Date.now() };
  bookings.push(newBooking);

  await db.set(key, bookings);
  res.json({ msg: "Booking confirmed", booking: newBooking });
});

// GET /bookings/:userId — fetch all bookings for a user
app.get("/bookings/:userId", async (req, res) => {
  const { userId } = req.params;
  const keys = await db.list(`booking:${userId}:`);
  const data = await Promise.all(keys.map((k) => db.get(k)));
  res.json(Object.fromEntries(keys.map((k, i) => [k, data[i]])));
});

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

How to Connect Your Replit Booking Backend to Stripe for Payments



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

const router = express.Router();

// Example: connect Replit booking backend to Stripe for payments
router.post("/create-payment-intent", async (req, res) => {
  const { amount, currency, userId } = req.body;
  const stripeSecret = process.env.STRIPE_SECRET_KEY;

  if (!stripeSecret) {
    return res.status(500).json({ error: "Stripe API key not configured" });
  }

  try {
    const response = await fetch("https://api.stripe.com/v1/payment\_intents", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${stripeSecret}`,
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body: new URLSearchParams({
        amount: amount.toString(),
        currency,
        "metadata[userId]": userId
      })
    });

    const paymentIntent = await response.json();
    if (response.ok) {
      res.json({ clientSecret: paymentIntent.client\_secret });
    } else {
      res.status(400).json(paymentIntent);
    }
  } catch (err) {
    console.error("Stripe error:", err);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

export default router;

// usage in index.js
// import paymentsRouter from "./payments.js"
// app.use("/api/payments", paymentsRouter);

How to Handle Booking Cancellations Safely in Your Replit App



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

const router = express.Router();
const db = new Database();

// cancels a pending booking & safely restores slot availability (atomic handle)
router.post("/cancel", async (req, res) => {
  const { userId, date, timeSlot } = req.body;
  const key = `booking:${userId}:${date}`;

  const currentBookings = (await db.get(key)) || [];
  const updated = currentBookings.filter((b) => b.timeSlot !== timeSlot);

  if (updated.length === currentBookings.length) {
    return res.status(404).json({ msg: "Booking not found or already cancelled" });
  }

  await db.set(key, updated);
  await db.delete(`slotlock:${date}:${timeSlot}`);

  res.json({ msg: "Booking cancelled successfully", date, timeSlot });
});

export default router;

// usage in index.js
// import cancelRouter from "./cancelBooking.js"
// app.use("/api", cancelRouter);

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 Booking platform with Replit

When building a booking platform on Replit, focus on organizing code correctly, protecting secrets, and using Replit’s built-in tools the right way. Use Node.js with Express for your backend API, React for the frontend (Replit templates make this easy), and a hosted database like Replit DB, Supabase, or MongoDB Atlas for reliable data storage. Keep any credentials (like API keys) inside the Replit Secrets panel instead of hardcoding them. Finally, deploy straight from Replit with the "Deployments" tab, and don’t treat it exactly like local development — Replit containers can sleep, and file-system writes are temporary unless saved properly.

 

Basic Folder and File Structure

 

In your Replit project, use a structure like this:

booking-platform/
│
├── backend/
│   ├── server.js
│   ├── routes/
│   │    └── bookings.js
│   └── models/
│        └── bookingModel.js
│
├── frontend/
│   ├── src/
│   │    ├── App.js
│   │    └── components/
│   │          └── BookingForm.js
│   └── package.json
│
├── .replit
├── replit.nix
└── README.md

This keeps backend and frontend concerns clean. Your server.js runs on Replit when you press Run. The frontend can connect to it through a full URL like https://your-repl-name.username.repl.co/api.

 

Setting Up the Backend (server.js)

 

Create a file at backend/server.js. This file will start a small Express server and handle routes for new bookings.

import express from "express"
import cors from "cors"

const app = express()
app.use(cors())
app.use(express.json()) // Allows you to read JSON in requests

// Example in-memory data store (for demo; replace with a real DB later)
let bookings = []

app.post("/api/bookings", (req, res) => {
  const { name, email, date } = req.body
  if (!name || !email || !date) {
    return res.status(400).json({ message: "Missing required fields" })
  }
  const newBooking = { id: bookings.length + 1, name, email, date }
  bookings.push(newBooking)
  res.status(201).json(newBooking)
})

app.get("/api/bookings", (req, res) => {
  res.json(bookings)
})

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

Place this in backend/server.js. In Replit, if your run command runs node backend/server.js, the server will start instantly when you press Run.

 

Connecting to a Real Database

 

If you want data to persist, use a cloud DB. For a small project, Replit DB (key-value store) works fine. You can set it up quickly like this inside server.js:

import Database from "@replit/database"
const db = new Database()

// Save booking
await db.set(`booking_${newBooking.id}`, newBooking)

// Retrieve all bookings
let bookings = []
for await (const key of db.list()) {
  const record = await db.get(key)
  bookings.push(record)
}

Use this DB logic inside your API endpoints, replacing the in-memory bookings array above. Replit DB is automatically bound to your Repl and doesn’t need credentials.

 

Frontend (React Example)

 

Go to frontend/src/components/BookingForm.js and create a simple booking form. It will send data to the backend API hosted on the same Replit app.

import { useState } from "react"

export default function BookingForm() {
  const [name, setName] = useState("")
  const [email, setEmail] = useState("")
  const [date, setDate] = useState("")

  const handleSubmit = async (e) => {
    e.preventDefault()
    const response = await fetch("/api/bookings", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name, email, date })
    })
    const data = await response.json()
    console.log("Booking saved:", data)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input placeholder="Name" value={name} onChange={(e) => setName(e.target.value)} />
      <input placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />
      <input type="date" value={date} onChange={(e) => setDate(e.target.value)} />
      <button type="submit">Book</button>
    </form>
  )
}

This file connects frontend input to your backend using fetch(). Run both the backend and React frontend Repls (or serve React build from Express when exporting to production).

 

Using Secrets in Replit

 

If you connect an external database like MongoDB Atlas or use a payment API (Stripe, for example), never paste credentials into your code. In the Replit editor, click the Secrets icon (the lock symbol on the left sidebar), and add keys like DB_URI or STRIPE_KEY. Then in your backend:

const dbURI = process.env.DB_URI
// Example: connect using mongoose
import mongoose from "mongoose"
mongoose.connect(dbURI)

These variables are secure and won’t be committed to GitHub or shared with collaborators unless you explicitly share Secrets.

 

Deployment and Realistic Tips

 

  • Use the Deployments tab for production hosting. It creates a persistent container so your project stays live even when you close the Repl.
  • Don’t rely on writing files to disk at runtime — files aren’t guaranteed to persist after a container restarts. Use a real database instead.
  • Use Replit’s multiplayer mode cautiously: all collaborators have code access. Use Secrets to hide credentials even from other users if needed.
  • Keep dependencies minimal since build times can grow in Replit’s container. For example, use Express instead of complex frameworks like NestJS unless required.
  • Use Replit’s Git integration to push and pull code, ensuring version safety. You can link your Repl directly to a private GitHub repo.

 

Final Practical Advice

 

Replit makes full-stack building easy, but remember: treat it like a managed cloud sandbox, not a full VPS. For a booking platform, get your backend stable and tested first, then link your React frontend, and only after that, deploy. Use Replit’s simplicity for early MVPs — it’s perfect for startups or client demos, as long as you keep your API keys secret and your data layer external. This approach will give you a stable, real-world deployable booking platform fully manageable inside Replit.

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