If you have used Prisma 7 for the first time and seen something like this:
const adapter = new PrismaBetterSqlite3({ url: databaseUrl });
const prisma = new PrismaClient({ adapter });
you might be wondering: what is this adapter thing, why do I need it, and what happens when I want to use PostgreSQL or MySQL in production? Let us walk through it.
The real world version of an adapter
Imagine you bring your US laptop charger to Europe. Your charger has flat US prongs. The wall has round EU sockets. They do not match. So you buy a small plug adapter. One side accepts your US prongs, the other side fits the EU wall.
You did not change your laptop. You did not change the wall. The adapter sits in the middle and translates between two things that were never designed to fit together.
That is exactly what a software adapter does. It connects two pieces that speak slightly different "languages" without forcing either side to change.
What that means in Prisma
When you write code like this:
prisma.user.findMany();
you are talking to Prisma Client. Prisma Client speaks one generic language for working with data. But your database speaks its own language. SQLite has its own dialect. PostgreSQL has its own dialect. MySQL has its own dialect. They all do similar things, but the details are different.
Something has to translate between Prisma Client and your actual database. In Prisma 7, that something is the adapter.
your code -> Prisma Client -> adapter -> driver -> database
The adapter is a small package that knows two things:
- How to receive instructions from Prisma Client.
- How to forward those instructions to a real database driver.
For SQLite, the adapter is @prisma/adapter-better-sqlite3, which talks to the better-sqlite3 library. For PostgreSQL, it is @prisma/adapter-pg, which talks to the pg library. Same idea, different translator.
Why Prisma changed to adapters in version 7
Old Prisma versions had a single built in "query engine" written in Rust that handled every database type internally. It worked, but it was large, and it did not run in places like Cloudflare Workers or other edge environments where you cannot ship Rust binaries.
By moving to pluggable adapters, Prisma got smaller, faster to start, and able to run in more places. You only install the adapter for the database you actually use, instead of carrying support for all of them.
"But what about other environments? My production uses PostgreSQL."
This is the question that worries a lot of people the first time they set up Prisma with SQLite locally. It feels like you are locking yourself in. Let us be honest about how this actually plays out.
The short answer
Your code is mostly portable. Your schema and queries may not be.
Switching adapters is a one line change in the file where you create your PrismaClient. The rest of your app code, the prisma.user.findMany() calls, do not change at all.
// Local: SQLite
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
const adapter = new PrismaBetterSqlite3({ url: databaseUrl });
// Production: PostgreSQL
import { PrismaPg } from "@prisma/adapter-pg";
const adapter = new PrismaPg({ connectionString: databaseUrl });
The catch is what happens around that line.
The real issues
The
providerinschema.prismais fixed. When you runprisma generate, the generated client is shaped for the database you set as the provider. SQLite and PostgreSQL support different column types. PostgreSQL has things like arrays, enums, JSON columns, anduuidtypes that SQLite does not have. If you generate againstsqlite, you do not get those types in your client even if you flip the adapter at runtime.Migrations are written in raw SQL. A migration file generated for SQLite uses SQLite syntax. PostgreSQL will not understand it. So the migrations folder is database specific too.
Subtle behavior differences. SQLite is forgiving in ways that PostgreSQL is strict about. Date handling, casing, transaction isolation, all slightly different. A query that passes locally might break in production for reasons that are not obvious.
What teams actually do
Almost everyone picks one database for all environments and uses that in development too. If production is PostgreSQL, you run PostgreSQL locally. The usual way is Docker:
docker run -d --name postgres -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:16
Then your .env has a PostgreSQL DATABASE_URL, your schema uses provider = "postgresql", and dev matches prod. Bugs surface in development, not at 2 AM in production.
So the answer to "should I refactor my code to swap databases per environment?" is usually no. The cost is high, the payoff is small, and the risk of dialect bugs is real. Pick the database you will use in production and use it everywhere.
When using SQLite locally is fine
If you are doing a tutorial, a tiny side project, or learning Prisma for the first time, SQLite is great. There is nothing to install, the database is just a file, and you can focus on learning. Just know that this is a learning shortcut, not a production pattern.
Recap in one paragraph
An adapter is a translator that sits between Prisma Client and a real database. Prisma 7 made this an explicit part of the setup so the framework can be smaller and run in more environments. Switching the adapter is technically a one line change, but the rest of your setup, the schema, the migrations, and small SQL behavior, is usually tied to one specific database. The standard practice is to pick one database for all environments and run it locally with Docker, rather than mixing SQLite for dev and something else for prod.
That is the whole picture. Once you see the adapter as just a translator, the new Prisma 7 setup feels less like extra ceremony and more like an honest acknowledgment of what was always going on under the hood.
Top comments (0)