Skip to main content
Chapter 6 Database Integration with Prisma

Prisma Relations and Advanced Queries

22 min read Lesson 23 / 28

Working with Relations in Prisma

Prisma handles one-to-one, one-to-many, and many-to-many relationships natively. Understanding how to query across relations efficiently is key to avoiding N+1 problems.

Select vs Include

// include — loads the full related model
const post = await db.post.findUnique({
    where: { id },
    include: { author: true }, // All author fields
});

// select — loads only specified fields (more efficient)
const post = await db.post.findUnique({
    where: { id },
    select: {
        id: true,
        title: true,
        author: {
            select: { name: true, image: true },
        },
    },
});

Nested Writes

// Create user with posts in one query
const user = await db.user.create({
    data: {
        name: 'Jane Doe',
        email: 'jane@example.com',
        posts: {
            create: [
                { title: 'First Post', slug: 'first-post', content: '...' },
                { title: 'Second Post', slug: 'second-post', content: '...' },
            ],
        },
    },
    include: { posts: true },
});

Raw Queries for Complex Cases

// When Prisma's query builder is not expressive enough
const topAuthors = await db.$queryRaw<Array<{
    id: string;
    name: string;
    postCount: bigint;
    avgViews: number;
}>>`
    SELECT
        u.id,
        u.name,
        COUNT(p.id) AS "postCount",
        AVG(p.view_count) AS "avgViews"
    FROM users u
    LEFT JOIN posts p ON p.author_id = u.id AND p.published = true
    GROUP BY u.id, u.name
    HAVING COUNT(p.id) > 5
    ORDER BY "postCount" DESC
    LIMIT 10
`;

Transactions

// Atomic operations — all succeed or all fail
const [post, _notification] = await db.$transaction([
    db.post.create({
        data: { title, slug, content, authorId },
    }),
    db.notification.create({
        data: {
            type: 'NEW_POST',
            recipientId: adminId,
            message: `New post: ${title}`,
        },
    }),
]);

// Interactive transaction for complex logic
const result = await db.$transaction(async (tx) => {
    const post = await tx.post.create({ data: { title, slug, content, authorId } });

    await tx.user.update({
        where: { id: authorId },
        data: { postCount: { increment: 1 } },
    });

    return post;
});

Using transactions ensures data consistency — if the notification fails, the post is never created, keeping your database in a valid state.