How to Use Prisma: The Beginner's Guide (First Schema, First Migration, Type-Safe Queries, and Deploy)
CodexFuse Team · 2026-10-31T03:57:27.257Z · 15 min read
How do you use Prisma?
You use Prisma by installing the CLI, describing your tables as models in a schema.prisma file, turning those models into real database tables with migrate dev, generating a type-safe Prisma Client, and then querying your data with TypeScript instead of hand-written SQL. That is how to use Prisma end to end, and this guide is a Prisma-for-beginners walkthrough of that exact path, updated for Prisma 7 in 2026. If you have only ever seen older tutorials, read carefully: Prisma 7 changed the setup flow, and copying v6-era commands is the single most common reason beginners get stuck.
- Install. Add
prisma@7as a dev dependency and@prisma/client@7plus a driver adapter as runtime dependencies. - Initialize. Run
npx prisma@7 initwith your database provider to createschema.prisma,prisma.config.ts, and.env. - Model. Define your tables as models in
prisma/schema.prisma— fields, types, and relations in one readable file. - Migrate. Run
npx prisma@7 migrate dev --name initto create a SQL migration and apply it to your development database. - Generate. Run
npx prisma@7 generateexplicitly — Prisma 7 no longer runs this for you after a migration. - Query. Use the generated client:
prisma.user.findMany(),prisma.user.create(),update(),delete()— all fully typed. - Relate. Add relations between models, open Prisma Studio to inspect your data visually, and seed starter data with
prisma db seed. - Deploy. Commit your migrations, run
npx prisma@7 migrate deployin your pipeline, and keepDATABASE_URLin platform secrets — never in the repo.
The eight steps above are the whole beginner path. The rest of this guide explains what each one is actually doing, so you learn Prisma as a system rather than a set of incantations. The generated client gives you fully typed query results — the same idea as our JSON to TypeScript converter, which turns raw JSON into TypeScript types, except Prisma derives the types directly from your database schema.
Is Prisma hard for beginners?
No — Prisma is one of the friendlier ways for a beginner to work with a database, because you never have to write SQL by hand to get started. If you can write basic TypeScript and run npm commands, you can follow this guide. The schema file reads almost like a description of your data ("a User has an id, an email, and many Posts"), and the client turns that description into autocomplete-friendly functions.
That said, Prisma is not magic, and beginners hit the same handful of walls. The 2026 wall is version confusion: most blog posts still teach the Prisma 6 flow, while new installs default to Prisma 8 and its new orm init getting-started path (Flavio Copes, "How to use Prisma," updated Sep 10, 2026). This guide pins Prisma 7 explicitly, which is the stable, fully documented path as of this writing. The other walls — connecting to a database for the first time, understanding what a migration actually does, and knowing the difference between migrate dev and migrate deploy — are all covered below, each in its own section.
How does Prisma actually work?
Prisma has three moving parts, and everything else is a detail of one of them. The schema is a single file where you declare your data models, the database connection, and the client generator. Migrate reads that schema and produces versioned SQL migration files that create or alter your actual tables. Prisma Client is a query builder generated from the schema, with TypeScript types for every model, so prisma.post.findMany() is checked by your editor before it ever touches the database.
The flow is always schema → migrate → generate → query. You change the schema, you migrate, you regenerate the client, you write queries. Once that loop is in your fingers, Prisma stops feeling like a framework and starts feeling like a natural extension of TypeScript.
What do you need before you start learning Prisma?
You need Node.js installed and a database to point Prisma at. This guide uses PostgreSQL, which is the most common Prisma pairing and the database Flavio Copes' Prisma 7 walkthrough is written against. If you do not have Postgres running yet, the fastest local setup is a container — our Docker beginner guide walks through installing Docker and running your first container, and a Postgres image takes one command to start.
You do not need a frontend, a framework, or any cloud account. Everything in this guide runs from a terminal against a local database, which is exactly how you should learn it: the fewer moving parts, the faster the schema → migrate → query loop clicks.
How do you install Prisma 7?
You install Prisma 7 by pinning the @7 tag on both packages, because prisma@latest now resolves to Prisma 8 with a different getting-started flow (Flavio Copes, "How to use Prisma," updated Sep 10, 2026). Run these from your project directory:
npm install --save-dev prisma@7 @types/pg
npm install @prisma/client@7 @prisma/adapter-pg@7 pg dotenv
The first line is the CLI and the PostgreSQL type definitions. The second line is the runtime: Prisma Client 7, the PostgreSQL driver adapter, the pg driver itself, and dotenv for loading your connection string. Prisma 7 connects through driver adapters — one adapter package per database — rather than its old built-in driver, which is why @prisma/adapter-pg is a separate install. Using MySQL instead? The pattern is identical; you would install that database's adapter package and pass --datasource-provider mysql at init.
How do you write your first Prisma schema?
You write your first Prisma schema by running the init command and then editing the model block it creates. Initialize with your provider and an explicit client output directory:
npx prisma@7 init --datasource-provider postgresql --output ../generated/prisma
Initialization creates four things: prisma/schema.prisma (your models), prisma.config.ts (Prisma's own configuration — new in v7), .env (your connection string), and supporting ignore rules so generated files and secrets stay out of version control (Flavio Copes, updated Sep 10, 2026). The config file wires the connection URL from the environment:
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations'
},
datasource: {
url: env('DATABASE_URL')
}
})
Now open prisma/schema.prisma. It has three block types: the generator block (tells Prisma to build the client, and where), the datasource block (which database you are talking to), and your model blocks (your tables). A first model looks like this:
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
}
model Car {
id Int @id @default(autoincrement())
brand String
model String
createdAt DateTime @default(now())
bought Boolean @default(false)
}
Each model becomes a database table: Car becomes a Car table, each field becomes a column, and the attributes do the work — @id marks the primary key, @default(autoincrement()) auto-numbers it, and @default(now()) timestamps new rows (Flavio Copes, updated Sep 10, 2026). Put your real connection string in .env as DATABASE_URL, and do not commit that file when it contains real credentials — use a separate database and separate credentials for local development, and keep production credentials in your deployment platform's secret settings.
How does Prisma migrate work?
Prisma Migrate turns schema changes into versioned SQL files and applies them to your database — and the critical lesson is that the development command and the production command are different tools with different safety rules. In development you run:
npx prisma@7 migrate dev --name init
This creates a migration folder under prisma/migrations/ containing the SQL, and applies it to your development database using a shadow database to validate the steps (Flavio Copes, updated Sep 10, 2026). Commit those migration files to Git — they are the history of your database, and your deployment pipeline replays them.
Then, and this is the v7 break every old tutorial gets wrong, you generate the client explicitly:
npx prisma@7 generate
Prisma 7 does not automatically run prisma generate after migrate dev, so keep the two steps separate or you will be querying with a stale client (Flavio Copes, updated Sep 10, 2026). In a deployment pipeline you apply the committed migrations with npx prisma@7 migrate deploy — and you never run migrate dev against production, because the dev command can reset data and is built for iteration, not safety.
| Step | v6-era flow (old tutorials) | Prisma 7 flow (this guide) |
|---|---|---|
| Install | npm install prisma --save-dev + @prisma/client | Pin both: prisma@7, @prisma/client@7, plus a driver adapter (@prisma/adapter-pg) |
| Init | npx prisma init — creates schema.prisma + .env only | npx prisma@7 init --datasource-provider postgresql --output ../generated/prisma — also creates prisma.config.ts |
| Connection URL | Lived in schema.prisma's datasource block | Lives in prisma.config.ts via env('DATABASE_URL') |
After migrate dev | Client regenerated automatically | Run npx prisma@7 generate explicitly |
| Client import | from '@prisma/client' | from '../generated/prisma/client' (your chosen output dir) + driver adapter |
| Seeding | "prisma": { "seed": ... } in package.json | migrations: { seed: "tsx prisma/seed.ts" } in prisma.config.ts |
Original comparison compiled from Flavio Copes' Prisma 7 guide (updated Sep 10, 2026) and the LearnerBits v6-era walkthrough (retrieved Oct 30, 2026).
What is Prisma Client and how do you generate it?
Prisma Client is the type-safe query builder Prisma generates from your schema — it is the object your code actually talks to, and in Prisma 7 it is generated into your own project directory rather than into node_modules (Coddy, "NestJS TypeORM and Prisma Interview Questions," retrieved Oct 30, 2026). You refresh it with npx prisma@7 generate after changing the schema, changing generator settings, or pulling schema changes from another branch — and on platforms that cache dependencies, add "postinstall": "prisma generate" to your package.json scripts so deploys never run with a stale client (Flavio Copes, updated Sep 10, 2026).
Because v7 connects through a driver adapter, the client needs the adapter at construction time. The recommended pattern is one shared client module that reuses a single instance in development (framework hot reloads would otherwise open a new connection pool on every reload):
import { PrismaPg } from '@prisma/adapter-pg'
import { PrismaClient } from '../generated/prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!
})
const prisma =
globalForPrisma.prisma ??
new PrismaClient({ adapter })
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}
export default prisma
Two rules from the official guidance: import this module only in server-side code — never into a browser bundle or client component — and pass the adapter into the constructor rather than relying on any global default (Flavio Copes, updated Sep 10, 2026; Coddy, retrieved Oct 30, 2026).
How do you run CRUD queries with Prisma Client?
You run CRUD queries by calling methods on the model — prisma.car.create(), findMany(), update(), delete() — with plain objects that your editor type-checks against the schema. Creating a record wraps the values in data:
const car = await prisma.car.create({
data: {
brand: 'Ford',
model: 'Fiesta'
}
})
Reading is findMany() for lists and findUnique() for one row by a unique field. Filters, sorting, and field selection are all part of the same call — no query-string building, no string concatenation:
const cars = await prisma.car.findMany({
where: { brand: 'Ford' },
orderBy: { createdAt: 'desc' }
})
const car = await prisma.car.findUnique({
where: { id: 1 }
})
Handle the two sharp edges deliberately: findUnique() returns null when nothing matches, so check the result before using it, and update() throws when the record does not exist — choose updateMany() or catch the error when absence is a normal case (Flavio Copes, updated Sep 10, 2026). Deleting is the same shape: await prisma.car.delete({ where: { id: 1 } }). And remember that a type-safe query is not automatically an authorized one — validate who is allowed to update or delete a row before the query runs.
How do Prisma relations work?
Prisma relations work by declaring the connection between two models in the schema, after which Prisma gives you nested queries across both. The classic one-to-many is a user with many posts: the User model holds posts Post[], the Post model holds user User @relation(fields: [userId], references: [id]) plus the userId foreign-key field (official Prisma seeding docs, updated Aug 26, 2026). One declaration, and the database gets the foreign key while your code gets prisma.user.findMany({ include: { posts: true } }).
One-to-one and many-to-many relations use the same @relation attribute with different field shapes — the schema reference documents all three, along with indexes and database-specific behavior (Flavio Copes, updated Sep 10, 2026). The practical rule: model the relationship once in the schema, and let migrations and the generated client carry it everywhere else.
What is Prisma Studio?
Prisma Studio is a visual database browser that ships with the CLI: run npx prisma@7 studio and it opens a web app where you can view, add, and edit your data without writing a query (dev.to, "Quick introduction to the Prisma ORM," retrieved Oct 30, 2026). It reads your schema, so every table and relation shows up with the right fields and links.
Studio is a development tool, not a deployment target — it is for inspecting what your migrations created and sanity-checking what your seed script wrote. When the data looks right in Studio, your schema and migrations are doing their job.
How do you seed a database with Prisma?
You seed a database with Prisma by declaring a seed command in prisma.config.ts and running prisma db seed whenever you want a fresh, predictable dataset. In Prisma 7 the seed lives in the config's migrations object, not in package.json:
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
seed: "tsx prisma/seed.ts",
},
datasource: {
url: env("DATABASE_URL"),
},
});
The seed script itself is ordinary Prisma Client code — the official guide's example creates users with nested posts via upsert, so re-running the seed never duplicates data (official Prisma "Seeding" docs, updated Aug 26, 2026). Seeding is especially useful with Prisma Migrate, which sometimes requires resetting your development database: one command rebuilds the schema and one command refills the data.
How do you deploy a Prisma app to production?
You deploy a Prisma app by committing your migration files, applying them with migrate deploy in the pipeline, and reading the connection string from the platform's secrets — the production flow has exactly three steps. First, apply the committed migrations: npx prisma@7 migrate deploy. Second, set DATABASE_URL in your deployment platform's environment-variable or secret settings, never in a committed .env (Flavio Copes, updated Sep 10, 2026).
Third, make sure the generated client is fresh on the server: add "postinstall": "prisma generate" to package.json when your platform caches dependencies, and re-run generate after any schema change before you deploy. That is the whole production story — everything else is your framework's normal deploy process.
How do you use Prisma with an existing database?
You use Prisma with an existing database by introspecting it: npx prisma@7 db pull reads your tables and writes them into schema.prisma as models, then npx prisma@7 generate builds the client from that schema (Flavio Copes, updated Sep 10, 2026). This is the add-Prisma-to-a-legacy-project path, and it is how brownfield teams adopt the ORM without rebuilding their database.
Review the generated schema before building on it — database naming conventions, unsupported column types, and relation conventions often need manual cleanup after introspection. Once the schema reads cleanly, the normal loop takes over: change the schema, migrate dev, generate, query.
Prisma vs TypeORM: which should you pick?
Pick Prisma if you want the database layer defined in one schema file with a generated, type-safe client; pick TypeORM if your team prefers entities as decorated TypeScript classes (Coddy, "NestJS TypeORM and Prisma Interview Questions," retrieved Oct 30, 2026). The honest differences are smaller than framework debates suggest:
| Prisma | TypeORM | |
|---|---|---|
| How you define models | schema.prisma file — one declarative source of truth | TypeScript classes with decorators (@Entity, @Column) |
| How you query | Generated client: prisma.user.findMany(), types follow each query's select | Repositories and entity manager: userRepository.find() |
| Migrations | prisma migrate dev / migrate deploy, SQL files committed | Generated migration classes run through the CLI |
| Learning curve | Gentler — one file to learn, autocomplete everywhere | Steeper — decorators, DataSource configuration, more concepts up front |
| Ecosystem fit | Pairs with any framework; the NestJS pattern is a PrismaService extending the client, connected in a lifecycle hook | Deep NestJS integration via the official TypeORM module |
Comparison compiled from the Coddy Prisma/NestJS interview reference (retrieved Oct 30, 2026) and the official Prisma v7 docs. A third option worth knowing is Drizzle, which takes a code-first, SQL-like approach — but if your question is "Prisma vs TypeORM for a beginner in 2026," Prisma's single schema file is the easier first ORM.
What mistakes do Prisma beginners always make?
Beginners make the same five mistakes, and all five are avoidable once you know the loop. First, creating a new PrismaClient in every file during development — use the globalThis singleton pattern from the client section so hot reloads do not exhaust your connection pool. Second, running migrate dev against production — the dev command is allowed to reset data; production gets migrate deploy only.
Third, committing .env with a real DATABASE_URL — rotate the credentials if this ever happens, then add the file to .gitignore. Fourth, forgetting prisma generate after a schema change in v7 — the old auto-generate is gone, and a stale client produces confusing type errors. Fifth, importing the client into browser-side code — Prisma Client is server-only; keep it in API routes, server actions, and backend services.
Where should you go next with Prisma?
Once the local loop works, the natural next step is a real hosted database. If you want Postgres without managing a server, read our Supabase beginner guide — Supabase gives you a managed Postgres plus an API layer, and Prisma is simply the ORM you would point at any database, including a Supabase database. The two are complements, not rivals: Supabase hosts the data, Prisma types the queries.
Then wire Prisma into something real: build a small API (our plain-English guide to what an API actually is covers the concepts if they are new), commit your migration history, and run it in CI with our GitHub beginner guide. That combination — schema, migrations in Git, typed client, deployed pipeline — is the professional Prisma workflow, and you now know every piece of it.
Sources
- Flavio Copes, "How to use Prisma" (flaviocopes.com/prisma/, updated Sep 10, 2026) — Prisma 7 setup: pinning
@7,prisma.config.ts, explicitgenerateaftermigrate dev,migrate deploy, driver adapters, CRUD examples,db pull, and thepostinstallgenerate step. - Prisma official documentation, "Seeding (Prisma ORM v7)" (prisma.io/docs/orm/prisma-migrate/workflows/seeding, updated Aug 26, 2026) —
migrations.seedinprisma.config.tsand theprisma db seedworkflow. - Coddy, "NestJS TypeORM and Prisma Interview Questions" (coddy.tech/interview-questions/nestjs/databases, retrieved Oct 30, 2026) — Prisma 7's generated-in-project client, the driver adapter passed via
super({ adapter }), and the TypeORM entities-as-classes contrast. - LearnerBits, "Prisma ORM: A Complete Guide to Getting Started" (learnerbits.com/prisma-orm-a-complete-guide-to-getting-started/, retrieved Oct 30, 2026) — the v6-era install → init → schema → migrate → generate flow, used as the contrast for what changed in v7.
- dev.to, "Quick introduction to the Prisma ORM" (retrieved Oct 30, 2026) — Prisma Studio as the visual database browser and the relations/CRUD basics.
Frequently asked questions
Do I need to know SQL to use Prisma?
No — that is the point of an ORM. You describe tables as models and query them with TypeScript functions, and Prisma writes the SQL. Learning basic SQL later still pays off when you debug a slow query or read a migration file, but it is not a prerequisite for this guide.
Is Prisma free to use?
Yes. Prisma ORM is a modern, open-source tool (LearnerBits, retrieved Oct 30, 2026) — the CLI, the schema language, migrations, Studio, and the generated client cost nothing. You only pay for whatever database you point it at, such as a managed Postgres provider.
Which databases can I use with Prisma?
This guide uses PostgreSQL with the @prisma/adapter-pg driver adapter (Flavio Copes, updated Sep 10, 2026). MySQL and SQLite follow the same workflow with a different --datasource-provider flag and adapter package — the LearnerBits walkthrough, for example, runs the identical steps against SQLite. Pick the database you already have; the Prisma concepts transfer unchanged.
Can I use Prisma with Supabase?
Yes — Supabase is managed Postgres, and Prisma connects to it like any other Postgres database: put your Supabase connection string in DATABASE_URL and run the normal migrate → generate → query loop. If you have not set up Supabase yet, start with our beginner's guide to Supabase and add Prisma when you want typed queries instead of the auto-generated API.
Should I learn Prisma 7 or Prisma 8?
Learn the one your project uses, but start with 7 if you are following tutorials: Prisma 7 (prisma@7 / prisma@prev) is the stable, fully documented path, while Prisma 8 (prisma@latest) uses a different getting-started flow with orm init (Flavio Copes, updated Sep 10, 2026). The core concepts — schema, migrations, generated client, CRUD — carry over either way.