DEV Community

Akın Coşkun
Akın Coşkun

Posted on

I Built a Multi-Tenant SaaS Where Customers Never Need an Account

TL;DR

I built Arac Saglik Karnesi (Vehicle Health Card), a free digital service-history tool for auto repair shops in Turkey. Every vehicle gets an 8-character code. Customers type that code into a page and see the full maintenance history: no signup, no app, no password. The interesting engineering problem wasn't the lookup. It was making one shared Next.js app safely serve dozens of independent auto shops without any of them ever seeing another shop's data.

Two audiences, two access models

A shop owner needs a normal authenticated dashboard: add vehicles, log service visits, upload photos, manage staff. The shop's customers need zero-friction access. They got a code printed on a receipt, they type it in, they see their car's history. No account, no login screen, no "create a password to view your own car" moment that makes people abandon the page.

Those are genuinely different security models living in the same app, and mixing them up is exactly how multi-tenant systems leak data.

Multi-tenancy without accidental leaks

Every table that holds shop-owned data carries a tenant identifier, and every query is scoped by that identifier at the query layer, not just filtered in the UI afterward. That distinction matters: a bug in a screen's rendering logic can't accidentally show one shop's customers on another shop's dashboard if the database query itself was never allowed to fetch cross-tenant rows in the first place.

Auth.js v5 handles the owner-side session and ties every authenticated request to exactly one shop. The customer-facing lookup page is intentionally unauthenticated, and instead scoped entirely by the 8-character code, which functions as a capability token: whoever holds the code can view that one vehicle's record, and nobody can browse their way into someone else's car by guessing, since the code space and rate limiting make brute force impractical.

Why a code and not a login

Requiring the customer to create an account just to see their own service history kills the point of the product. The entire value proposition is a shop being able to say "we did the work, here's the record," and any friction between the receipt and that proof cancels out the trust it was supposed to build. An 8-character alphanumeric code is short enough to print on a receipt or text to a customer, and long enough that guessing your way to a specific vehicle isn't practical.

Compressing photos before they leave the browser

Shops attach before and after photos of the work, which is a strong trust signal for customers. Uploading full-resolution phone photos straight to Vercel Blob would be slow on shop wifi and adds up in storage and egress at scale. Photos are compressed client-side, in the browser, before the upload even starts, so what actually crosses the network is already the size it needs to be.

The stack

  • Next.js 16, React 19, TypeScript
  • Prisma 6 and PostgreSQL on Neon, tenant-scoped at the query layer
  • Auth.js v5 for shop-owner authentication
  • Upstash Redis
  • Vercel Blob for photos, compressed client-side before upload
  • Tailwind CSS

Try it

Arac Saglik Karnesi is free for auto repair shops, no monthly fee. Live at arac-saglik-karnesi.vercel.app, source on GitHub: akincskn/arac-saglik-karnesi.

I'm Akın Coşkun, a full-stack developer from Turkey building production SaaS tools with zero-cost infrastructure. More projects on my portfolio: akin-coskun.web.app.

Top comments (0)