avatarTomas Svojanovsky

Summary

This context provides a step-by-step tutorial on how to use Prisma in a Next.js app, focusing on fetching and creating data using server components and server actions.

Abstract

The tutorial begins by explaining that Prisma can be used directly in server components, offering advantages such as types and eliminating the need for additional code. The author mentions that they will not delve into APIs or server actions in this series but will demonstrate how to start using Prisma in a few steps. The tutorial covers fetching data using the findMany and findFirstOrThrow methods, as well as creating data using server actions and the revalidatePath function. The author provides code examples and a full code listing for reference. The tutorial concludes by showing the expected result if everything works correctly.

Bullet points

  • Prisma can be used directly in server components for fetching and creating data.
  • Fetching data can be done using the findMany and findFirstOrThrow methods.
  • Creating data requires the use of server actions and the revalidatePath function.
  • Code examples and a full code listing are provided for reference.
  • The tutorial concludes by showing the expected result if everything works correctly.

Elevate Your Next.js App with Prisma: A Step-by-Step Tutorial (Final)

Now we have everything set up. We can write an API, and server actions, or directly use Prisma in server components. I prefer using Prisma directly in server components because I can leverage all the advantages it offers — types, and I don’t need to write anything extra. Of course, I see use cases for other approaches like APIs or server actions, but this is really cool.

In this series, we won’t go deep into API or server actions. I’ll show you how to start using Prisma in your app in a few steps, and we’ll delve deeper into other topics in the next stories.

I will continue where we left off. If you didn't catch the previous step, you can check it here: Part 2.

Fetch data

This one is easy and can be done on the server. Go to the page.tsx file and delete all the content within the main tag. Since we don't have use client at the top, it means this component will be rendered on the server, allowing us to use Prisma safely here. Additionally, server components can be asynchronous, which is beneficial for server-side operations.

Find all

const users = await db.user.findMany();

Find one

const user = await db.user.findFirstOrThrow({
    where: {
        firstName: "John",
    },
});

Create data

This one is a little bit harder because we can’t just write a query, and that’s it. It must be attached to some form or click event. Since we are on the server, we aren’t allowed to use click events due to the absence of the window object. So, let’s try server actions; you can think of them as an API, but you need to write much less code than with a classic endpoint.

Server actions

We need to call one important thing and it is revalidatePath, because without data we won’t get any new data to the user’s list. It’s a little bit different from client-side rendering.

async function createPerson(formData: FormData) {
    "use server";
    const firstName = formData.get("firstName") as string;
    const lastName = formData.get("lastName") as string;

    await db.user.create({
        data: {
            firstName,
            lastName,
        },
    });

    revalidatePath("/");
}

You may wonder how we get data. It will be from a form like this. I’ve added some styles with Tailwind. It’s not very pretty, but at least not ugly.

<form action={createPerson} className={"max-w-xs"}>
    <div className="flex flex-col gap-1">
        <label htmlFor="firstName">First Name:</label>
        <input className="border-zinc-900 border" type="text" name="firstName"/>
    </div>

    <div className="flex flex-col gap-1">
        <label htmlFor="firstName">First Name:</label>
        <input className="border-zinc-900 border" type="text" name="lastName"/>
    </div>

    <button className="bg-zinc-900 text-white w-full mt-2 p-2 rounded-md" type="submit">Create</button>
</form>

Full code

import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";

type UserFormValues = {
  firstName: string;
  lastname: string;
};

export default async function Home() {
    const users = await db.user.findMany();
    const user = await db.user.findFirstOrThrow({
        where: {
            firstName: "John",
        },
    });

    async function createPerson(formData: FormData) {
        "use server";
        const firstName = formData.get("firstName") as string;
        const lastName = formData.get("lastName") as string;

        await db.user.create({
            data: {
                firstName,
                lastName,
            },
        });

        revalidatePath("/");
    }

    return (
        <main className="flex flex-col gap-4 h-screen text-zinc-900 bg-white p-8">
            <section>
                <h2>Users:</h2>

                <ul>
                    {users.map(user => (
                        <li key={user.id}>{user.firstName} {user.lastName}</li>
                    ))}
                </ul>
            </section>

            <section>
                <h2>User:</h2>

                <p>{user.firstName} {user.lastName}</p>
            </section>

            <section>
                <h2>Form:</h2>

                <form action={createPerson} className={"max-w-xs"}>
                    <div className="flex flex-col gap-1">
                        <label htmlFor="firstName">First Name:</label>
                        <input className="border-zinc-900 border" type="text" name="firstName"/>
                    </div>

                    <div className="flex flex-col gap-1">
                        <label htmlFor="firstName">First Name:</label>
                        <input className="border-zinc-900 border" type="text" name="lastName"/>
                    </div>

                    <button className="bg-zinc-900 text-white w-full mt-2 p-2 rounded-md" type="submit">Create</button>
                </form>
            </section>
        </main>
    );
}

Result

This is what you should see if everything works fine.

Server actions with fetching data

Thanks for reading my article!

If you enjoyed the read and want to be part of our growing community, hit the follow button, and let’s embark on a knowledge journey together.

Your feedback and comments are always welcome, so don’t hold back!

Stackademic

Thank you for reading until the end. Before you go:

  • Please consider clapping and following the writer! 👏
  • Follow us on Twitter(X), LinkedIn, and YouTube.
  • Visit Stackademic.com to find out more about how we are democratizing free programming education around the world.
Software Development
Nextjs
Prisma
Recommended from ReadMedium