Prisma is a modern TypeScript ORM that drastically simplifies database work with a declarative schema, automatic migrations, and a fully typed client. Once you try it, there's no going back.
Hey! Ever found yourself wrestling with raw SQL queries, wishing your ORM just understood what you want? Let me introduce you to Prisma - the ORM that feels like it was built by someone who knows your pain. π
What Is Prisma, Exactly? π€
Prisma is an open-source ORM for Node.js and TypeScript that takes a completely different approach than traditional ORMs. Instead of classes and decorators, Prisma uses its own schema format (schema.prisma), from which a type-safe client is automatically generated. The result: you get autocomplete and type checking for every single database query.
Prisma consists of three main components:
- Prisma Client - The auto-generated, type-safe query builder
- Prisma Migrate - The declarative migration system
- Prisma Studio - A visual database editor
The Schema: Your Single Source of Truth π
Everything starts with the schema.prisma file. This is where you define your database connection and your models. The beauty: it's super readable and intuitive.
// schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
profile Profile?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Profile {
id Int @id @default(autoincrement())
bio String?
userId Int @unique
user User @relation(fields: [userId], references: [id])
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
tags Tag[]
createdAt DateTime @default(now())
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}Even from this example you can see: relations are directly visible in the schema. A User has many Posts, a Post belongs to a User, and Tags have a many-to-many relationship with Posts. Everything is clear and declarative.
Migrations: No Fear of Schema Changes π
When you change your schema, Prisma Migrate automatically creates SQL migrations for you:
# Create and run migration
npx prisma migrate dev --name add-user-model
# Regenerate client
npx prisma generatePrisma analyzes the difference between your current schema and the database and generates the appropriate SQL statements. You never have to manually write ALTER TABLE again (unless you want to).
Prisma Client: Type-Safe Queries That Are Actually Fun π―
Now for the heart of it all. After generating, you have a client that knows exactly your schema. Here are some examples:
CRUD Operations
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// Create - Add a new user
const user = await prisma.user.create({
data: {
email: '[email protected]',
name: 'Max Mustermann',
},
})
// Read - Find users
const allUsers = await prisma.user.findMany()
const singleUser = await prisma.user.findUnique({
where: { email: '[email protected]' },
})
// Update - Modify a user
const updatedUser = await prisma.user.update({
where: { id: 1 },
data: { name: 'Max M.' },
})
// Delete - Remove a user
const deletedUser = await prisma.user.delete({
where: { id: 1 },
})The genius part: for each of these calls you get full autocomplete. When you type prisma.user.create({ data: { , your editor shows you exactly which fields you can set - including which are optional and which are required.
Relations and Nested Writes β¨
This is where it gets really cool. You can create nested data in a single operation:
// Create user WITH post and profile in one go
const userWithRelations = await prisma.user.create({
data: {
email: '[email protected]',
name: 'Lisa',
profile: {
create: {
bio: 'Full-stack developer from Berlin',
},
},
posts: {
create: [
{
title: 'My First Post',
content: 'Hello World!',
published: true,
},
{
title: 'Prisma is awesome',
content: 'Seriously, try it out.',
},
],
},
},
include: {
profile: true,
posts: true,
},
})And when querying, you can easily load relations with include or select:
// Load all users with their posts
const usersWithPosts = await prisma.user.findMany({
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
},
profile: true,
},
})
// Select only specific fields
const userNames = await prisma.user.findMany({
select: {
name: true,
email: true,
_count: {
select: { posts: true },
},
},
})Raw Queries: When You Need to Get Specific π§
Sometimes you just need raw SQL. No problem, Prisma has you covered:
// Type-safe raw queries
const result = await prisma.$queryRaw`
SELECT u.name, COUNT(p.id) as post_count
FROM "User" u
LEFT JOIN "Post" p ON p."authorId" = u.id
GROUP BY u.name
HAVING COUNT(p.id) > ${minPosts}
`
// Or for mutations
await prisma.$executeRaw`
UPDATE "User"
SET name = ${newName}
WHERE id = ${userId}
`Tagged template literals automatically protect you from SQL injection. Security out of the box! π‘οΈ
Prisma Studio: Keep an Eye on Your Data π
With a simple command you can start a visual editor for your database:
npx prisma studioThis opens a web interface where you can browse, filter, edit, and even create new entries in your data. Super handy for development and debugging.
Prisma vs. TypeORM vs. Sequelize βοΈ
| Feature | Prisma | TypeORM | Sequelize |
|---|---|---|---|
| Schema Definition | Own schema format | TypeScript decorators | JavaScript objects |
| Type Safety | β Fully generated | Good, but manual | Limited |
| Migrations | Auto from schema | Auto-generate possible | Manual/CLI |
| Learning Curve | Flat | Medium | Medium |
| Raw SQL | Tagged templates | Query builder | Sequelize.literal |
| Visual Editor | Prisma Studio | No | No |
| DB Support | PostgreSQL, MySQL, SQLite, MongoDB, CockroachDB | Many RDBMs | Many RDBMs |
Prisma really shines when it comes to type safety and developer experience. The generated client is unbeatable for autocomplete and compile-time error detection.
TypeORM is a solid choice if you come from the Java/C# world and prefer the Active Record or Data Mapper pattern. Sequelize is the classic option, but falls behind significantly when it comes to TypeScript support.
When Should You Use Prisma? π―
Prisma is ideal when you:
- Work with TypeScript (the biggest advantage!)
- Value developer experience
- Are starting a new project or want to migrate an existing one
- Use PostgreSQL, MySQL, SQLite, or MongoDB
- Work in a team where schema changes need to be clearly traceable
It's less suited when you need very complex SQL queries that are hard to translate into the Prisma Client - but that's what raw queries are for.
Conclusion: Just Try It! π
Prisma has completely changed the way I work with databases in TypeScript projects. The combination of a declarative schema, automatic migrations, and the brilliant typed client is genuinely fun. It feels like the database is finally working with you instead of against you.
Just give it a try - you won't regret it!
# Start a new project
npm init -y
npm install prisma @prisma/client
npx prisma init