Build an online test booking system with Lovable: step-by-step guide to add scheduling, payments and notifications to increase bookings, save time

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
You can build an online test-booking app in Lovable without a terminal by using Chat Mode to create React pages/components, adding the Supabase client, storing keys in Lovable Cloud Secrets, and testing in Preview. Create the UI and client-side calls to Supabase (no server code required), set up tables in the Supabase dashboard (outside Lovable via SQL editor), then Publish from Lovable after confirming Secrets are set. This works well in Lovable because Preview will build the app and use the Secrets you configure; no CLI needed.
Implement a simple online test booking flow: a booking page that lists available test slots (from Supabase), allows a user to reserve a slot (creates a booking row), and shows confirmations. Data lives in Supabase; Lovable hosts the frontend and reads credentials from Lovable Cloud Secrets.
Use Chat Mode edits to create/modify files (React components and a Supabase client), set Supabase keys in Lovable Cloud Secrets, use Preview to test the flow, and Publish from the Lovable UI. Schema creation in Supabase uses the Supabase dashboard SQL editor (outside Lovable). If you need server functions or migrations that require CLI, export to GitHub and run them locally (labelled below).
Prompt 1 — scaffold client and Supabase helper
// create src/lib/supabaseClient.ts
import { createClient } from '@supabase/supabase-js'
// Supabase public keys must be NEXT_PUBLIC_* so the frontend can read them at build/runtime
const url = process.env.NEXT_PUBLIC_SUPABASE_URL
const anon = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
export const supabase = createClient(url!, anon!)
Prompt 2 — create booking page and basic UI
// create src/pages/booking.tsx
import React, { useEffect, useState } from 'react'
import { supabase } from '../lib/supabaseClient'
// simple client-only UI: fetch slots and insert a booking on click
export default function Booking() {
const [slots, setSlots] = useState<any[]>([])
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [message, setMessage] = useState('')
useEffect(() => {
async function load() {
// fetch available slots (assumes table "slots")
const { data } = await supabase.from('slots').select('*').order('start', { ascending: true })
setSlots(data || [])
}
load()
}, [])
async function book(slotId: string) {
const { error } = await supabase.from('bookings').insert([{ slot_id: slotId, name, email }])
if (error) setMessage('Booking failed: ' + error.message)
else setMessage('Booked successfully!')
}
return (
<div>
<h1>Book a Test</h1>
<input placeholder="Name" value={name} onChange={e => setName(e.target.value)} />
<input placeholder="Email" value={email} onChange={e => setEmail(e.target.value)} />
<ul>
{slots.map(s => (
<li key={s.id}>
{new Date(s.start).toLocaleString()} - <button onClick={() => book(s.id)}>Book</button>
</li>
))}
</ul>
<div>{message}</div>
</div>
)
}
Prompt 3 — Supabase schema (outside Lovable)
-- run in Supabase SQL editor
create table if not exists slots (
id uuid primary key default gen_random_uuid(),
start timestamp with time zone not null
);
create table if not exists bookings (
id uuid primary key default gen_random_uuid(),
slot_id uuid references slots(id),
name text not null,
email text not null,
created_at timestamptz default now()
);
All instructions use Lovable-native features: Chat Mode file edits, Preview, Publish, and Lovable Cloud Secrets. Schema creation uses the Supabase dashboard (Supabase SQL editor). No fake Lovable menus or terminal commands are suggested. If you need server-side code requiring CLI, export to GitHub and run migrations locally — that step is explicitly outside Lovable.
This prompt helps an AI assistant understand your setup and guide to build the feature
This prompt helps an AI assistant understand your setup and guide to build the feature
This prompt helps an AI assistant understand your setup and guide to build the feature

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
You should design the booking app to keep business-critical logic in the backend (atomic booking via DB function), use Supabase for storage/auth, keep secrets in Lovable Cloud Secrets, use Lovable’s Chat edits/Preview/Publish for iterative development, and treat AI code generators as accelerators — always review, test, and wrap generated code in reviewed RPCs/transactions before trusting it for bookings.
Keep the source of truth in the database (Supabase/Postgres). Implement atomic booking in the DB (a Postgres RPC/function) so concurrent requests can’t double-book. Use Lovable’s Secrets UI to store service\_role keys, and use server-side RPCs called from a protected API route. Use Lovable Preview to exercise flows and GitHub sync to export when you need CI/deploy control.
-- // Create tables and a transactional RPC function in Supabase SQL editor
create table slots (
id uuid primary key default gen_random_uuid(),
start timestamptz not null,
capacity int not null
);
create table bookings (
id uuid primary key default gen_random_uuid(),
slot_id uuid references slots(id),
user_id uuid not null,
created_at timestamptz default now()
);
create or replace function create_booking(p_slot uuid, p_user uuid)
returns uuid as $$
declare
v_capacity int;
v_booked int;
v_booking_id uuid;
begin
select capacity into v_capacity from slots where id = p_slot for update;
if not found then
raise exception 'slot not found';
end if;
select count(*) into v_booked from bookings where slot_id = p_slot;
if v_booked >= v_capacity then
raise exception 'no_capacity';
end if;
insert into bookings(slot_id, user_id) values (p_slot, p_user) returning id into v_booking_id;
return v_booking_id;
end;
$$ language plpgsql security definer;
// // Next.js (app/api/book/route.js) server route calls the RPC
import { createClient } from '@supabase/supabase-js';
// // Use Lovable Secrets to set SUPABASE_URL & SUPABASE_SERVICE_ROLE
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE);
export async function POST(req) {
// // parse body for slot_id; ensure user is authenticated server-side
const { slot_id, user_id } = await req.json();
const { data, error } = await supabase.rpc('create_booking', { p_slot: slot_id, p_user: user_id });
if (error) return new Response(JSON.stringify({ error: error.message }), { status: 400 });
return new Response(JSON.stringify({ booking_id: data }), { status: 200 });
}
Follow this pattern: move sensitive/atomic logic into DB RPCs, keep keys in Lovable Secrets, iterate with Chat Mode + Preview, and sync to GitHub for production CI/deploy. Use AI generators for speed, but always validate and test the generated code before publishing.
From startups to enterprises and everything in between, see for yourself our incredible impact.
Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.