Supabase and Next.js for Bangkok Booking Systems
26 April 2026 · by Yunmin Shin
What Is Supabase and Why Bangkok Clinics and Restaurants Use It
Supabase is an open-source backend-as-a-service built on top of PostgreSQL. It provides a hosted database, authentication, file storage, and real-time subscriptions — all accessible via a JavaScript client or REST API.
For the kind of small-to-mid-size Bangkok businesses Bluewich builds for — aesthetic clinics that need an appointment booking system, restaurants that need a live order dashboard, a real estate site that needs a listings database — Supabase is an excellent choice. You get a production-grade PostgreSQL database, row-level security, and auth out of the box, without hiring a dedicated backend engineer or standing up your own server infrastructure. It replaces what used to require a custom PHP backend or a bloated WordPress plugin stack trying to fake booking functionality it was never built for.
How Does Supabase Work with Next.js?
Supabase provides a first-class Next.js integration via the @supabase/ssr package. This package handles the cookie-based session management required for server-side rendering — something the older @supabase/auth-helpers-nextjs package did not handle cleanly.
Install both packages:
npm install @supabase/supabase-js @supabase/ssr
Create two Supabase client utilities:
- A server client (
lib/supabase/server.ts) for use in server components, route handlers, and server actions — reads cookies from the request. - A browser client (
lib/supabase/client.ts) for use in client components — persists the session in browser storage.
Store your Supabase URL and anon key in environment variables. The anon key is safe to expose to the browser — Supabase's row-level security policies enforce access control at the database level, which matters a great deal once you have a clinic staff dashboard and a public booking form both talking to the same database.
How Do You Build a Clinic Booking System with Row-Level Security?
Row-Level Security (RLS) is Supabase's most important feature. It lets you define access policies directly on your database tables, enforced by PostgreSQL itself rather than by application code. Take a clinic's appointments table as an example:
alter table appointments enable row level security;
-- Patients can read only their own appointments
create policy "patients read own appointments"
on appointments for select
using (auth.uid() = patient_id);
-- Clinic staff can read and update all appointments
create policy "staff read all appointments"
on appointments for select
using (auth.jwt() ->> 'role' = 'staff');
create policy "staff update all appointments"
on appointments for update
using (auth.jwt() ->> 'role' = 'staff');
Enable RLS on every table and write explicit policies. By default, a table with RLS enabled and no policies denies all access — which is the safe default. This means a patient booking through the public site can never accidentally see another patient's appointment, and if a bug in the front-end code forgets to filter by patient ID, the database still refuses the request. For a clinic handling medical treatment records, that database-level guarantee is not a nice-to-have.
How Do You Handle Real-Time Updates for Restaurant Orders?
Supabase wraps PostgreSQL's LISTEN/NOTIFY and logical replication to deliver real-time updates to connected clients. This is a natural fit for a restaurant's kitchen order dashboard: an order placed through a LINE-linked ordering page inserts a row into the orders table, and the kitchen screen updates instantly without polling.
const channel = supabase
.channel("kitchen-orders")
.on(
"postgres_changes",
{ event: "INSERT", schema: "public", table: "orders" },
(payload) => {
addOrderToQueue(payload.new);
}
)
.subscribe();
The same pattern works for a clinic's front-desk dashboard showing new bookings as they come in, or a delivery tracking view for a sourcing or e-commerce business. Anywhere staff need to see new activity the moment it happens, without refreshing a page, real-time subscriptions remove the need to build that infrastructure yourself.
How Do You Store Treatment Photos and Menu Images?
Supabase Storage handles file uploads with the same RLS model as the database. For a clinic, before/after treatment photos should live in a private bucket, with policies that only allow the patient and clinic staff to read a given file — these are sensitive medical images and should never be publicly listed:
const { data, error } = await supabase.storage
.from("treatment-photos")
.upload(`${patientId}/${appointmentId}-after.jpg`, file, {
contentType: "image/jpeg",
});
A restaurant's menu photos, by contrast, belong in a public bucket, since they need to load directly on the public menu page without an auth check. Deciding bucket visibility per use case — and never defaulting everything to public out of convenience — is one of the easiest ways to avoid an embarrassing data exposure later.
Is Supabase Right for Production in Thailand?
Supabase is production-ready and runs on AWS infrastructure with region options including Singapore, which provides good latency for Thailand users — noticeably better than defaulting to a US region for a site whose visitors are almost entirely in Bangkok. The free tier supports small projects and early prototypes, and the Pro plan at $25/month covers most early-stage clinics, restaurants, and startups comfortably.
For larger applications with complex business logic — multi-location clinic chains, or an e-commerce platform with custom pricing rules — pairing Supabase's database and auth with a custom Next.js backend (API routes or server actions handling business logic, Supabase handling storage and RLS) gives you the best of both worlds.
Ready to Build Something Fast?
Get a free quote. We reply within 24 hours.
Ready to build something fast and scalable?
Get a free project quote. We reply within 24 hours.
Get a Free Quote →