Especially this is noticeable when working with PostgreSQL — the stack seems the same, but familiar solutions no longer work, especially if you’re using JavaScript.
I ran into this myself: you do everything “as before”, but instead you get errors that are not immediately obvious how to fix. And the most unpleasant part is that the problem is not in your code, but in the changed internal logic of Prisma itself.
So if you’re currently trying to figure out what the Prisma 7 PostgreSQL JavaScript setup should look like, you’re not alone — almost everyone goes through this after the upgrade.
In this article, I’ll show a working approach: without unnecessary theory, but with a clear explanation of what exactly has changed and how to use it in a real JavaScript project.
STEP 1: Project structure for Prisma 7 PostgreSQL JavaScript
When we talk about Prisma 7 PostgreSQL JavaScript, it’s important to understand one simple thing right away — setup doesn’t start with code or database connection, but with the project structure.
In Prisma 7 this became especially noticeable: the logic is split into several layers, and if you try to “dump everything into one place”, strange errors start appearing very quickly, and you get the feeling that something is wrong even though everything is formally correct.
So the first step is simply to separate the project by responsibilities.
At minimum, it looks like this:
back/
├── prisma/
│ └── schema.prisma
├── src/config/
│ └── prisma.js
├── prisma.config.ts
├── .env
At this stage there is nothing complicated or unnecessary here. But the key point is different — now each file is responsible for its own specific part of the system, and this directly affects how predictable Prisma behaves in a JavaScript project.
And this separation is what actually marks the beginning of a proper setup.
STEP 2: Installing Prisma 7 PostgreSQL JavaScript dependencies
Next we move to the basic step — installing everything required for the project to work.
cd back
npm install prisma @prisma/client @prisma/adapter-pg dotenv
At first glance there’s nothing unusual here. A standard set of dependencies you’ve probably seen in other projects.
Prisma 7 requires using an adapter when working with PostgreSQL in JavaScript projects.
And this is where the difference from older versions begins.
Now Prisma does not work directly with the database “out of the box” the way it used to. It requires an adapter that explicitly defines how it connects to PostgreSQL and how it manages the connection.
And if it’s missing — Prisma simply won’t be able to work properly with the database. This is no longer an optional tweak or optimization, but a required part of the architecture.
STEP 3: Initializing a Prisma project
At this stage everything looks familiar. We simply run:
npx prisma init
After that Prisma creates the basic project files — .env and a prisma folder with the schema.
Visually, this is the same starting point many developers are used to. And that is exactly where the small trap is: everything looks identical, but Prisma 7’s internal logic is already different, and this becomes noticeable later when you start configuring the connection and client.
STEP 4: Setting up schema.prisma in Prisma 7 PostgreSQL JavaScript
This is one of the key parts of the whole setup.
If раньше the database connection was written directly in schema.prisma, in Prisma 7 this no longer works — this configuration is simply not supported.
Now the schema looks simpler:
generator client {
provider = "prisma-client-js"
}datasource db {
provider = "postgresql"
}
In Prisma 7, in our case, there is no longer a url string here, and this is not a mistake or omission, but a deliberate architectural change. The idea is that the new version allows generating the client directly in the project under src/generated/prisma. In that scenario, all generated files will use TypeScript syntax. If you are writing a pure JavaScript server, this will most likely not fit your use case. That is why in this article I decided to skip that scenario.
To generate the client inside node_modules, we use provider = "prisma-client-js". This means the generated client will work smoothly with JavaScript and will be available in the code like this:
import { PrismaClient } from '@prisma/client'
The idea is that Prisma now separates responsibilities. The schema only defines the data structure, while everything related to the database connection is moved elsewhere and no longer mixed with models.
At first this may feel like unnecessary complexity, because previously everything lived in one file. But in practice, it removes confusion between what you define and how the connection actually happens.
STEP 5: PostgreSQL connection via DATABASE_URL
After the schema is ready, a logical question arises: where do we now define the database connection?
Previously it was simple — you just put the URL into schema.prisma and moved on. In Prisma 7 this no longer works, and the connection has moved into .env.
The file looks very simple:
DATABASE_URL="postgresql://user:password@localhost:5432/your_db"
Nothing unusual here. It’s a standard PostgreSQL connection string.
But it’s important to understand that the schema no longer knows anything about the database. It only defines structure, while everything related to the connection lives separately.
STEP 6: prisma.config.js for Prisma CLI
And here we get a new file that didn’t exist in older Prisma projects — prisma.config.ts.
If you are writing a JavaScript server, you can safely name it prisma.config.js — functionally there is no difference; what matters is how it is used.
At first it may look unnecessary or like duplication of .env, but in reality it has a very specific purpose — it tells the Prisma CLI how to work with the database and project schema.
It looks like this:
import { defineConfig } from 'prisma/config'
import 'dotenv/config'export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: "prisma/migrations",
},
datasource: {
url: process.env.DATABASE_URL,
},
})
What matters here is not so much the content itself, but the role of this file.
It is used exclusively by Prisma tools, for example:
- migrations (
prisma migrate) - database GUI (
prisma studio)
Your server application does not use this file at all. It exists only so that the CLI understands the project context and can properly interact with the database and schema.
There is a clear separation:
prisma.config.ts— used only for CLI tools- server application — uses a separate Prisma Client in code
And if you try to use this config inside the server directly, nothing will work. It is not intended for runtime logic.
STEP 7: Prisma Client with PostgreSQL adapter in JavaScript
Now we reach the most important part of the entire Prisma 7 setup — creating the client and how the PostgreSQL connection actually works. This is where it becomes clear that Prisma 7 behaves differently from previous versions.
If раньше everything boiled down to a simple line:
const prisma = new PrismaClient()
and that was enough, now this approach no longer works.
The reason is that Prisma 7 introduces a required layer — the adapter. It defines how the client connects to PostgreSQL and manages the connection. And now this is no longer hidden internal logic, but an explicit part of the configuration.
How the client is created now
Usually, a separate file is created:
src/config/prisma.js
And inside it the client is built:
import { PrismaClient } from '@prisma/client'
import { PrismaPg } from '@prisma/adapter-pg'
import 'dotenv/config'const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL,
})export const prisma = new PrismaClient({ adapter })
At first it may seem like just another extra layer. But in reality this is a key architectural change: Prisma no longer “guesses” how to connect — it relies strictly on an explicitly defined adapter.
Why this is not enough
If you stop here, in a simple scenario everything will work. But in a real project another problem quickly appears.
In development mode, the application restarts frequently. And each restart creates a new database connection.
At first this is not noticeable, but over time the number of connections grows, and the database may start throwing errors due to overload.
And this is where the next part of the solution appears.
STEP 8: Singleton Prisma Client (preventing extra connections)
To avoid multiple connections, the same Prisma client instance is reused across the entire application.
The idea is simple: if the client already exists — reuse it; if not — create it once and store it.
import { PrismaClient } from '@prisma/client'
import { PrismaPg } from '@prisma/adapter-pg'
import 'dotenv/config'const globalForPrisma = globalThis// Create PostgreSQL adapter
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL,
})// Create client with adapter
const createPrismaClient = () => {
return new PrismaClient({ adapter })
}// Singleton: reuse existing or create new
export const prisma = globalForPrisma.prisma || createPrismaClient()// In dev mode store it globally to avoid multiple connections
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = prisma
}
The idea is simple: instead of constantly creating new connections, you work with one stable client instance. And this is what makes Prisma behavior in real projects predictable and safe.
STEP 9: Migrations and Prisma Client generation
At this stage you already have the full project foundation: Prisma schema, database connection, and configured client. Now the final step is to tie everything into a working system and verify that it actually works.
First, generate the Prisma Client:
npx prisma generate
This creates an up-to-date client based on your schema. This is what you use in code for all database queries.
Next, apply migrations:
npx prisma migrate dev --name init
At this point Prisma synchronizes your schema with PostgreSQL and creates the required tables. It also stores migration history so you can track structural changes later. If everything runs without errors — the database and Prisma setup are correct, and the project is ready.
STEP 10: Checking Prisma 7 PostgreSQL JavaScript connection
Now the simplest and most important step remains — making sure everything is actually working.
Let’s run a small test in code:
import { prisma } from './config/prisma.js'const users = await prisma.user.findMany()
console.log(users)
If you see an array in the console (even an empty one) — Prisma has successfully connected to the database and everything is configured correctly. This is the simplest way to verify that the client works and PostgreSQL responds without errors.
STEP 11: Running a Node.js project
After that, you can start the server with the usual command:
npm run dev
If all previous steps were done correctly, the project will start without surprises, and the database connection will work stably from the first launch.
What you actually need to understand
The whole Prisma 7 + PostgreSQL setup comes down to a few key ideas.
Not commands — those can be googled. But understanding:
- the schema no longer stores the connection
- Prisma Client is created through an adapter
- database connection lives in
.env - the client must be a singleton
Once this clicks, everything else becomes obvious.
Conclusion
Prisma 7 has indeed slightly changed the usual startup process, especially if you come from previous versions and expect the old “out of the box” behavior. But this is not complexity for the sake of complexity — it’s more of a shift toward a more explicit and controlled configuration.
In practice, this setup works equally well for small pet projects and for more serious applications with growing load and logic.
And most importantly — it removes random errors that previously appeared due to hidden magic inside the configuration.