DEV Community

Alex
Alex

Posted on

How I Built a SaaS for Vacation Rental Hosts: Stack & Key Decisions

I manage vacation rentals inSpain. After a bad experience as a guest — 20 WhatsApp videos the night before a trip — I decided to build a tool to fix the problem. That tool became Itineramio.

Here's the stack I chose and why.

The Stack

  • Framework: Next.js 15 (App Router) — Server Components for fast mobile loading
  • Database: PostgreSQL + Prisma — Type-safe ORM, great migrations
  • Auth: JWT with jose (Edge) — Lightweight, works in Edge Runtime
  • Payments: Stripe — Industry standard, great docs
  • Email: Resend — Simple API, good deliverability
  • Storage: Vercel Blob — Zero config, works with Next.js
  • UI: Tailwind + Radix UI — Fast to build, accessible by default
  • Rate limiting: Upstash Redis — Edge-compatible, pay-per-use

Key Architecture Decisions

App Router over Pages Router

Guest-facing guides need to load fast — guests are often on roaming data or hotel WiFi. Server Components let me ship minimal JavaScript to the client while keeping the dashboard interactive where needed.

JWT at the Edge

I needed auth in middleware without cold starts. jsonwebtoken doesn't work in Edge Runtime, so I use jose for middleware verification and jsonwebtoken in API routes. Not ideal, but it works.

Building VeriFactu Compliance Early

Spain is introducing VeriFactu in 2027 — invoicing software must maintain a SHA-256 hash chain, report to the tax authority, and generate QR codes. I built this from day one instead of retrofitting it later. Each invoice's hash includes the previous one, creating an immutable chain.

AI Chatbot with Scoped Context

The chatbot only knows what's in the property's manual. This keeps responses accurate — it won't hallucinate restaurant recommendations or make up appliance instructions. Scoped context beats general knowledge for this use case.

Mistakes I Made

  1. Skipping rate limiting — Bots found my public endpoints fast. Add rate limiting from the start, not after.
  2. Silent form failures — React Hook Form's valueAsNumber returns NaN for empty fields. Zod accepts it as a number but fails validation silently.Always preprocess numeric inputs.
  3. Over-engineering the loading state — I built a full-page spinner that ended up blocking all clicks across the app. Sometimes less is more.

Current Scale

  • ~460 API routes
  • 128 database models
  • 100+ components
  • 6 cron jobs running on Vercel

The Lesson

The best tools come from experiencing the problem yourself. I'd been a host for years but never understood the guest's frustration until I was one.

If you're curious: itineramio.com

Top comments (0)