DEV Community

Cover image for Week 11: Prisma!
Nikhil Sharma
Nikhil Sharma

Posted on

Week 11: Prisma!

This week I explored the Prisma ORM, which simplifies our SQL journey by a LOT. Let’s get right into it!

Topics Covered✅

Last week, I worked with basic schemas and simple database interactions. But once our data starts to grow and multiple entities are involved, raw SQL queries or loosely typed database access quickly become messy and error-prone.

That’s where Prisma comes in.

This week revolved around:

  • Understanding what an ORM actually solves
  • Defining a database schema using Prisma
  • Generating type-safe database clients
  • Performing CRUD operations cleanly and safely

1. What is Prisma and why do we need it? 🧠

Prisma is an ORM (Object Relational Mapper). In simple terms, it acts as a bridge between our database and our application code.

Without Prisma, we might write raw SQL like this:

SELECT * FROM users WHERE email = 'test@gmail.com';
Enter fullscreen mode Exit fullscreen mode

With Prisma, the same thing looks like:

const user = await prisma.user.findUnique({
  where: { email: "test@gmail.com" }
});
Enter fullscreen mode Exit fullscreen mode

Why this matters

  • We don’t manually write SQL for every operation
  • Queries become type-safe
  • Refactoring database fields doesn’t silently break things
  • Autocomplete + compile-time checks save us from runtime bugs

2. Prisma Schema — the single source of truth 📄

First we initialise Prisma in our project (for which you can refer to the docs here).
At the heart of all generated files lies the schema.prisma file. This file defines:

  • our database connection
  • our models (tables)
  • relationships between models

A basic example:

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
}
Enter fullscreen mode Exit fullscreen mode

This schema:

  • Creates a User table
  • Automatically enforces uniqueness on email
  • Generates types that our editor understands

Once this is written, running:

npx prisma generate
Enter fullscreen mode Exit fullscreen mode

creates a fully typed Prisma Client for us — which we can just import in our DB setup files and use as simply as this -

import { PrismaPg } from '@prisma/adapter-pg'
import { PrismaClient } from "@/app/generated/prisma/client";

const adapter = new PrismaPg({
  connectionString: process.env.DATABASE_URL,
})
const prisma = new PrismaClient({
  adapter,
});
Enter fullscreen mode Exit fullscreen mode

3. Migrations — evolving the database safely 🔁

Databases are never static. Fields get added, removed, or changed. Prisma handles this through migrations.

Example:

npx prisma migrate dev --name add-user-model
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Creates a migration file
  • Applies changes to the database
  • Keeps history of schema evolution

This ensures our local DB, staging DB, and production DB all stay in sync. And the generated migration files are simply the SQL versions of the queries that we can write easily using Prisma.

4. CRUD operations with Prisma ✍️

Prisma makes CRUD operations intuitive and readable.

Create

await prisma.user.create({
  data: {
    email: "nikhil@gmail.com",
    name: "Nikhil"
  }
});
Enter fullscreen mode Exit fullscreen mode

Read

const users = await prisma.user.findMany();
Enter fullscreen mode Exit fullscreen mode

Update

await prisma.user.update({
  where: { id: 1 },
  data: { name: "Updated Name" }
});
Enter fullscreen mode Exit fullscreen mode

Delete

await prisma.user.delete({
  where: { id: 1 }
});
Enter fullscreen mode Exit fullscreen mode

Each of these operations is:

  • Typed
  • Validated at compile time
  • Predictable in behaviour

And to see what parameters these functions expect we can just refer to the docs.

5. Relations and foreign keys made simple 🔗

Defining relations in Prisma is surprisingly clean.

Example:

model User {
  id    Int    @id @default(autoincrement())
  posts Post[]
}

model Post {
  id     Int  @id @default(autoincrement())
  title  String
  user   User @relation(fields: [userId], references: [id])
  userId Int
}
Enter fullscreen mode Exit fullscreen mode

Now Prisma understands:

  • One user → many posts
  • How to join data internally
  • How to fetch nested data safely

Fetching related data:

const user = await prisma.user.findUnique({
  where: { id: 1 },
  include: { posts: true }
});
Enter fullscreen mode Exit fullscreen mode

New things I learnt this week 🔄

  • Prisma acts as an intermediary between our code and database - increasing our ease while also giving type safety, which is an absolute game changer.
  • Schema-first design prevents an entire class of bugs

Wrapping up

While Prisma might feel like “just another tool”, it fundamentally changes how confidently we interact with our database. Clean schemas and safe migrations make production systems far more reliable.

If you have any questions or feedback, make sure to comment and let me know! Also, if you have any project ideas for me, please drop em in the comments!

I’ll be back next week with more. Until then, stay consistent!

Top comments (0)