Last month, I deployed a booking system for a luxury limo service in Dubai. We used Next.js for the dashboard and Prisma ORM to handle everything from driver shifts to customer history. On launch day, everything broke when a client tried to double-book a Rolls-Royce Phantom during Dubai World Expo week. The error logs pointed directly at my Prisma queries — turns out transactions in PostgreSQL don’t auto-commit like I’d assumed. Spent six hours sweating through the root cause while the client’s ops team screamed on a Zoom call. Welcome to production.
Prisma Isn’t Magic (But It’s Close)
I’ll be real: I switched to Prisma full-time three years ago because writing raw SQL for every table join in a 20-model Next.js app was killing me. It’s not perfect — more on that later — but the type safety and auto-generated types save at least 10 hours per project.
For a UAE real estate platform I built (think Reach Home Properties but with twice the listings), I connected Prisma to PlanetScale because the client insisted on MySQL. Schema structure looked like this:
model Property {
id Int @id @unique
title String
price Float
agent User @relation(fields: [agentId], references: [id])
agentId Int
createdAt DateTime @default(now())
}But in production, you’ll hit edge cases fast. Like when Arabic characters in property titles started showing up as ???? in the DB. Quick fix: force the MySQL connection to use utf8mb4 in the Prisma schema.
Mutations That Don’t Suck (Mostly)
Next.js API routes with Prisma mutations? They work until they don’t. In the limo app’s case, overlapping bookings happened because I used two separate .create() calls instead of wrapping them in a $transaction. Prisma supports transactions, but the syntax is easy to mess up:
// This fails silently if the second operation throws
await prisma.booking.create({...})
await prisma.vehicle.update({...})
// This actually rolls back on failure
await prisma.$transaction([
prisma.booking.create({...}),
prisma.vehicle.update({...})
])But here’s the kicker: if you’re using MongoDB, transactions are scoped to a single collection. Wasted half a day figuring that out the hard way when a client insisted on swapping PostgreSQL for MongoDB mid-project.
When Prisma Fights You
Last year I tried using the new Prisma Accelerate beta to cut latency for a React Native plant-care app (Greeny Corner for UAE users). It added 150ms to response times in Abu Dhabi data centers. Rolled back to plain-old prisma-client.
My current setup is simpler: cache the Prisma client in Next.js API routes to avoid cold-start delays. This gotcha bit me during the first week of the limo project:
// utils/prisma.ts
import { PrismaClient } from '@prisma/client'
declare global {
var prisma: PrismaClient | undefined
}
export const prisma = global.prisma || new PrismaClient()
if (process.env.NODE_ENV !== 'production') global.prisma = prismaOptimize What Matters
Most of my clients don’t care about ORM benchmarks. They care about paying for 500 concurrent users without crashing. So I audit queries like an over-caffeinated hawk. Example from the real estate site:
A homepage endpoint originally fetched full property objects via include:
await prisma.property.findMany({
include: {
features: true,
images: true
}
})But when property counts hit 10k rows, latency spiked to 2.3s. Switched to select with specific fields and cut that to 400ms.
await prisma.property.findMany({
select: {
title: true,
price: true,
images: { take: 1, select: { url: true } }
}
})Also added Redis caching for listing filters (area, bedroom count, etc.) since most users apply the same few options.
Real World Quirks: The UAE Context
Arabic sorting and search behave differently than English in Prisma. For a construction company in Abu Dhabi, searches for “فندق” (hotel) failed because MySQL’s default collation wasn’t set to utf8mb4_unicode_ci. Took forever to trace that back from the Next.js layer to the DB config.
Now I standardize all projects on PostgreSQL via Neon unless a client has a weird legacy requirement. Honestly, Prisma handles Postgres enums, full-text search, and JSON columns way better.
What’s Not Worth It
The Prisma Studio GUI? Skip it. I tried showing it to a client once — they kept modifying records then asking why the app didn’t reflect their changes. Turns out prisma generate wasn’t part of their workflow. Now I use it strictly for schema diffs.
Also avoid using the upsert method in high-concurrency environments. Two users creating the same record format caused unique constraint errors during the limo app’s peak load. Switched to manual checks:
const existing = await prisma.booking.findUnique({...})
if (existing) return update(...)
else return create(...)Not as pretty, but it works.
Final Word
If you’re starting a Next.js project in the UAE and need to ship something solid, Prisma is still the best ORM choice even with its flaws. Watch out for:
- Connection pooling (use
onBeforeInitin Next.js config if you hit max connections) - Arabic encoding gotchas in MongoDB/MySQL
- Transactions in multi-model operations
Need help debugging a specific setup? My DMs are open on Twitter or just reach out through sarahprofile.com/contact. I’ll send you code samples from my GCC projects — including the cursed MongoDB migration we survived last year.