Aurelia: What It Is, What It Can Do, and What It's Made Of
Aurelia is a shop for private palaces, villas and residences. A catalogue, a rupee quote, Razorpay checkout, and a folio you can cancel — built with Next.js, Prisma, PostgreSQL and TypeScript.
Aurelia is a booking product dressed as a house, and built as a shop. The live site is called Aurelia House. It sells stays in private residences the same way a store sells goods: you pick a chamber, you see the full price, you pay, and you keep an order. In Aurelia that order is called a folio.
This note is the plain version of the build. What the product is, what a guest can actually do on it, and the stack that holds it up.
What it is
It is a catalogue of ten private houses across ten cities: Udaipur, Jaisalmer, Dubai, Gstaad, Kyoto, Malé, Oia, Paris, Positano and Ubud. Palaces, villas, a chalet, a ryokan, an estate, an atelier. Prices are per chamber, per night, in Indian rupees. The writing treats the stay as something you buy, and the confirmation as a receipt you can open later.
Around the shop there is a destinations index, an experiences page, a concierge desk, and a Circle membership enquiry. Legal pages cover how a stay is bought, the terms of sale, refunds, privacy and a merchant contact. The public site is the storefront. The database is the inventory and the ledger of orders.
Choose a chamber, see the full price, and pay at checkout. The folio is your order.
What it can do
- Shop the collection. Filter by destination, dates, guests, house type (palace, villa, chalet, ryokan, estate, atelier) and a nightly price ceiling. Chambers already sold for those dates stay off the page.
- Open a residence. Each house has its own page: story, images, amenities, reviews, and the chambers that are free.
- Quote the stay before anyone pays. The quote is the room total, chosen extras, and 18% tax, shown together.
- Pay at checkout with Razorpay — card, UPI, net banking or wallet. The reservation is confirmed only after the payment signature checks out. Aurelia stores the folio, never the card number.
- Sign in with a magic link sent to the same email, then open the folio, read the confirmation, or cancel.
- Cancel against a printed policy. Fourteen days or more returns the full stay. Seven to thirteen days returns 70%. Three to six days returns 40%. Under three days the room is non-refundable, and taxes plus unused extras still come back. The refund is calculated in front of the guest.
- Write the concierge. A letter with a name, an email, an optional folio number, and the request, saved for the desk.
- Ask to join the Circle. A short enquiry with a name, an email and a tier.
A chamber is shown only when it can still be reserved. The catalogue does not invent urgency, and it does not list dates that are already spoken for.
How a stay is held
Availability is a count, not a flag on the hotel. Each chamber has an inventory. A confirmed reservation that overlaps the requested nights counts against that inventory. If the taken count is already at the inventory, those dates are refused with a simple line: those dates have already been spoken for.
const taken = await overlappingCount(room.id, checkIn, checkOut);
const free = taken < room.inventory;The same check runs again inside a serializable transaction when the folio is written, so two guests cannot both take the last chamber for the same nights. The price the guest pays is recomputed on the server: nights times the chamber rate, plus extras, plus tax at 18%. The browser can ask for a stay. It cannot decide the total.
What it's made of
Aurelia is one Next.js application. The pages and the API live in the same TypeScript codebase, and the whole thing deploys to Vercel. There is no separate Express server.
| Layer | Choice | Why |
|---|---|---|
| App | Next.js 16, React 19, TypeScript | App Router pages, route handlers, one deploy |
| Interface | Tailwind CSS v4, shadcn/ui, Base UI | The house look: Playfair Display, Jost, gold on near-black |
| Data | PostgreSQL + Prisma | Houses, chambers, extras, reviews, folios and payments as real relations |
| Validation | Zod | Quotes, guests, payments and forms checked before they touch the database |
| Auth | jose, magic links, Resend | A signed session cookie. A 15-minute link by email. No password to forget |
| Payments | Razorpay | Order, signature check, and a webhook before a folio is confirmed |
| Limits | Upstash Redis | Rate limits on the routes that take money and mail |
| Delivery | Vercel | The live shop is hotel-booked-frontend.vercel.app |
The models that matter
The schema is small on purpose. A Hotel has rooms, extras, reviews and reservations. A Room knows its occupancy, its nightly price and how many of that chamber exist. A Reservation is the folio: dates, guests, nights, room total, extras, tax, status, and the payment row beside it. Concierge letters and Circle enquiries are their own tables, so a message is a record and not an email that disappeared.
Sessions are HS256 JWTs in an httpOnly cookie, good for seven days, with a guest role and an admin role. The magic link is a separate token that expires in fifteen minutes and can only be used to sign the session. When Resend is configured, the link is emailed. When it is not, the link is logged, which keeps local development honest.
Payments that agree with the folio
Checkout creates a Razorpay order for the quoted amount in paise, in INR. The client never marks a stay as paid. The verify route checks the Razorpay signature, reads the payment method back from Razorpay, and only then writes the reservation. A webhook covers the same fulfillment path, so a closed tab does not leave a paid order without a folio. If the signature fails, the stay stays unconfirmed.
What I would keep
- Sell the stay as an order. A folio is easier to explain than a reservation state machine buried in a dashboard.
- Show the tax before the button. Eighteen percent, printed, is calmer than a surprise on the last screen.
- Re-check inventory in the same transaction that writes the folio.
- Trust the payment only after the signature, and keep the card number on Razorpay's side.
Aurelia is the project where a luxury page and a real checkout had to be the same product. The type is quiet. The inventory, the quote and the payment are not.
Keep reading