Skip to main content
Chapter 6 Database Integration with Prisma

CRUD Operations with Prisma

24 min read Lesson 22 / 28

Type-Safe Database Operations

Prisma generates TypeScript types directly from your schema. Every query is type-checked at compile time — if your schema changes, TypeScript catches mismatches immediately.

Create

// Create a post with relations
const post = await db.post.create({
    data: {
        title: 'Getting Started with Prisma',
        slug: 'getting-started-with-prisma',
        content: 'Prisma is amazing...',
        author: {
            connect: { id: userId },
        },
        category: {
            connectOrCreate: {
                where: { slug: 'tutorials' },
                create: { name: 'Tutorials', slug: 'tutorials' },
            },
        },
        tags: {
            connectOrCreate: [
                { where: { name: 'prisma' }, create: { name: 'prisma' } },
                { where: { name: 'typescript' }, create: { name: 'typescript' } },
            ],
        },
    },
    include: {
        author: { select: { name: true, email: true } },
        category: true,
        tags: true,
    },
});

Read

// Find with filters, ordering, and pagination
const posts = await db.post.findMany({
    where: {
        published: true,
        category: { slug: 'tutorials' },
        tags: { some: { name: 'typescript' } },
    },
    orderBy: { publishedAt: 'desc' },
    skip: (page - 1) * perPage,
    take: perPage,
    include: {
        author: { select: { id: true, name: true, image: true } },
        category: true,
        _count: { select: { comments: true } },
    },
});

// Find one or throw
const post = await db.post.findUniqueOrThrow({
    where: { slug },
    include: {
        author: true,
        tags: true,
        comments: {
            include: { author: true },
            orderBy: { createdAt: 'desc' },
        },
    },
});

Update

// Update with optimistic concurrency
const updatedPost = await db.post.update({
    where: { id: postId },
    data: {
        title: newTitle,
        content: newContent,
        updatedAt: new Date(),
        tags: {
            set: [],  // Clear existing tags
            connectOrCreate: newTags.map(tag => ({
                where: { name: tag },
                create: { name: tag },
            })),
        },
    },
});

Delete

// Soft delete pattern
const archived = await db.post.update({
    where: { id: postId },
    data: { published: false, deletedAt: new Date() },
});

// Hard delete (cascades to comments via schema onDelete: Cascade)
await db.post.delete({ where: { id: postId } });

Aggregations

const stats = await db.post.aggregate({
    where: { authorId: userId },
    _count: { id: true },
    _avg: { viewCount: true },
});

const postsByCategory = await db.category.findMany({
    include: {
        _count: { select: { posts: true } },
    },
    orderBy: { posts: { _count: 'desc' } },
});

Prisma's type safety means you get autocomplete for every field and relation, and TypeScript errors catch invalid queries before they reach production.