diff --git a/content/800-guides/090-prisma-orm-with-nextjs.mdx b/content/800-guides/090-prisma-orm-with-nextjs.mdx new file mode 100644 index 0000000000..d57d52d086 --- /dev/null +++ b/content/800-guides/090-prisma-orm-with-nextjs.mdx @@ -0,0 +1,653 @@ +--- +title: 'How to use Prisma ORM with Next.js' +metaTitle: 'How to use Prisma ORM with Next.js 15 and Vercel' +description: 'Learn how to use Prisma ORM in a Next.js app and deploy it to Vercel' +sidebar_label: 'Use Prisma ORM with Next.js' +image: '/img/guides/prisma-nextjs-setup-cover.png' +--- + +## Introduction + +This guide shows you how to use Prisma ORM with Next.js 15, a fullstack React framework. You'll learn how to set up Prisma ORM with Next.js, handle migrations, and deploy your application to Vercel. You can find a [deployment-ready example on GitHub](https://github.com/prisma/prisma-examples/blob/latest/deployment-platforms/edge/cloudflare-workers/with-d1). + +## Prerequisites + +Before starting this guide, make sure you have: + +- Node.js installed (version 18 or higher) +- A [Prisma Postgres](https://www.prisma.io/postgres) database (or any PostgreSQL database) +- A Vercel account (if you want to deploy your application) + +## 1. Set up your project + +From the directory where you want to create your project, run `create-next-app` to create a new Next.js app that we will be using for this guide. + +```terminal +npx create-next-app@latest my-app --yes +``` + +Then, navigate to the project directory: + +```terminal +cd my-app +``` + +## 2. Set up Prisma ORM + +### 2.1 Install Prisma ORM and create your first models + +First, we need to install a few dependencies. At the root of your project in your terminal, run: + +```terminal +npm install prisma --save-dev +npm install tsx --save-dev +npm install @prisma/extension-accelerate +``` + +:::info + +If you're not using a Prisma Postgres database, you won't need the `@prisma/extension-accelerate` package. + +::: + +Then, run `prisma init` to initialize Prisma ORM in your project. + +```terminal +npx prisma init +``` + +This will create a new `prisma` directory in your project, with a `schema.prisma` file inside of it. The `schema.prisma` file is where you will define your database models. + +The `prisma init` command also creates a `.env` file in your project root. This file is used to store your database connection string. + +Next, let's add two models to your `schema.prisma` file. A `User` model and a `Post` model. + +```prisma file=schema.prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +//add-start +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int + author User @relation(fields: [authorId], references: [id]) +} +//add-end +``` + +This represents a simple blog with users and posts. Each `Post` can have a `User` as an author and each `User` can have many `Post`s. + +Now that we have a Prisma Schema and a model, let's connect to our Prisma Postgres database. + +### 2.2 Save your database connection string + +Now that you have your Prisma schema, you will need a database to apply your schema to. + +:::note[I don't have a database yet] + +If you don't have a database yet, you can create a new one through the [Prisma Console](https://console.prisma.io/). Step-by-step instructions are available in our [getting started guide](/getting-started/quickstart-prismaPostgres#1-set-up-a-prisma-postgres-database-in-the-platform-console). + +::: + +When you have created your Prisma Postgres project, you'll get a `DATABASE_URL` and `PULSE_API_KEY`. Store these in your `.env` file. + +```env file=.env +DATABASE_URL=[Your Database URL Here] +PULSE_API_KEY=[Your Pulse API Key Here] +``` + +:::info + +If you're not using Prisma Postgres you won't need the `PULSE_API_KEY` here. + +::: + +### 2.3 Update your database schema + +::warning + +If you're connecting to an existing database, skip this step and instead use the `prisma db pull` command to update your local schema. + +::: + +Now that you've saved your database connection string, we can apply your schema to your database using the `prisma migrate dev` command. + +```terminal +npx prisma migrate dev --name init +``` + +This creates an initial migration creating the `User` and `Post` tables and applies that migration to your database. + +Now, let's add some initial data to our database. + +### 2.4 Seed your database + +Prisma ORM has built-in support for seeding your database with initial data. To do this, you can create a new file called `seed.ts` in the `prisma` directory. + +```ts file=prisma/seed.ts +import { PrismaClient, Prisma } from '@prisma/client' + +const prisma = new PrismaClient() + +const userData: Prisma.UserCreateInput[] = [ + { + name: 'Alice', + email: 'alice@prisma.io', + posts: { + create: [ + { + title: 'Join the Prisma Discord', + content: 'https://pris.ly/discord', + published: true, + }, + { + title: 'Prisma on YouTube', + content: 'https://pris.ly/youtube', + }, + ], + }, + }, + { + name: 'Bob', + email: 'bob@prisma.io', + posts: { + create: [ + { + title: 'Follow Prisma on Twitter', + content: 'https://www.twitter.com/prisma', + published: true, + }, + ], + }, + } +] + +export async function main() { + for (const u of userData) { + await prisma.user.create({ data: u }) + } +} + +main() +``` + +Now, add the `prisma.seed` configuration to your `package.json` file. + +```json file=package.json +{ + "name": "my-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + //add-start + "prisma": { + "seed": "tsx prisma/seed.ts" + }, + //add-end + "dependencies": { + "@prisma/client": "^6.2.1", + "@prisma/extension-accelerate": "^1.2.1", + "next": "15.1.4", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "15.1.4", + "postcss": "^8", + "prisma": "^6.2.1", + "tailwindcss": "^3.4.1", + "tsx": "^4.19.2", + "typescript": "^5" + } +} +``` + +Finally, run `prisma db seed` to seed your database with the initial data we defined in the `seed.ts` file. + +```terminal +npx prisma db seed +``` + +We now have a database with some initial data! You can check out the data in your database by running `prisma studio`. + +```terminal +npx prisma studio +``` + +### 2.5 Set up Prisma Client + +Now that we have a database with some initial data, we can set up Prisma Client and connect it to our database. + +At the root of your project, create a new `lib` directory and add a `prisma.ts` file to it. + +```terminal +mkdir -p lib && touch lib/prisma.ts +``` + +Now, add the following code to your `lib/prisma.ts` file: + +```ts file=lib/prisma.ts +import { PrismaClient } from '@prisma/client' +import { withAccelerate } from '@prisma/extension-accelerate' + +const prisma = new PrismaClient().$extends(withAccelerate()) + +const globalForPrisma = global as unknown as { prisma: typeof prisma } + +if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma + +export default prisma +``` + +This file creates a Prisma Client and attaches it to the global object so that only one instance of the client is created in your application. This helps resolve issues with hot reloading that can occur when using Prisma ORM with Next.js in development mode. + +:::note + +If you're not using Prisma Postgres, replace the line `const prisma = new PrismaClient().$extends(withAccelerate())` with `const prisma = new PrismaClient()` and remove the second line `import { withAccelerate } from '@prisma/extension-accelerate'`. + +::: + +We'll use this client in the next section to run your first queries. + +## 3. Query your database with Prisma ORM + +Now that we have an initialized Prisma Client, a connection to our database, and some initial data, we can start querying our data with Prisma ORM. + +In our example, we'll be making the "home" page of our application display all of our users. + +Open the `app/page.tsx` file and replace the existing code with the following: + +```tsx file=app/page.tsx +export default async function Home() { + return ( +
+

+ Superblog +

+
    +
  1. Alice
  2. +
  3. Bob
  4. +
+
+ ); +} +``` + +This gives us a basic page with a title and a list of users. However, that list is static and doesn't change. Let's update the page to fetch the users from our database and make it dynamic. + +```tsx file=app/page.tsx +//add-start +import prisma from '@/lib/prisma' +//add-end + +export default async function Home() { + //add-start + const users = await prisma.user.findMany(); + //add-end + return ( +
+

+ Superblog +

+
    + //add-start + {users.map((user) => ( +
  1. + {user.name} +
  2. + ))} + //add-end +
+
+ ); +} + +``` + +We are now importing our client, querying the `User` model for all users, and then displaying them in a list. + +Now your home page is dynamic and will display the users from your database. + +### 3.1 Update your data (optional) + +If you want to see what happens when data is updated, you could: + +- update your `User` table via a SQL browser of your choice +- change your `seed.ts` file to add more users +- change the call to `prisma.user.findMany` to re-order the users, filter the users, or similar. + +Just reload the page and you'll see the changes. + +## 4. Add a new "posts" list page + +We have our home page working, but we want to add a new page that displays all of our posts. + +First create a new `posts` directory in the `app` directory and create a new `page.tsx` file inside of it. + +```terminal +mkdir -p app/posts && touch app/posts/page.tsx +``` + +Now, replace the existing code in the `app/posts/page.tsx` file with the following: + +```tsx file=app/posts/page.tsx +import prisma from "@/lib/prisma"; + +export default async function Posts() { + return ( +
+

+ Posts +

+ +
+ ); +} +``` + +Now `localhost:3000/posts` will load, but the content is static. Let's update it to be dynamic, similarly to the home page: + +```tsx file=app/posts/page.tsx +import prisma from "@/lib/prisma"; + +export default async function Posts() { + //add-start + const posts = await prisma.post.findMany({ + include: { + author: true, + }, + }); + //add-end + + return ( +
+

+ Posts +

+ +
+ ); +} +``` + +This works similarly to the home page, but instead of displaying users, it displays posts. You can also see that we've used `include` in our Prisma Client query to fetch the author of each post so we can display the author's name. + +This "list view" is one of the most common patterns in web applications. We're going to add two more pages to our application which you'll also commonly need: a "detail view" and a "create view". + +## 5. Add a new "posts" detail page + +To complement the "posts" list page, we'll add a "posts" detail page. + +In the `posts` directory, create a new `[id]` directory and a new `page.tsx` file inside of that. + +```terminal +mkdir -p app/posts/[id] && touch app/posts/[id]/page.tsx +``` + +This page will display a single post's title, content, and author. Just like our other pages, replace the content of the file with the following: + +```tsx file=app/posts/[id]/page.tsx +import prisma from "@/lib/prisma"; + +export default async function Post({ params }: { params: Promise<{ id: string }> }) { + return ( +
+
+

My first post

+

by Anonymous

+
+ No content available. +
+
+
+ ); +} +``` + +As before, this page is static. Let's update it to be dynamic based on the `params` passed to the page: + +```tsx file=app/posts/[id]/page.tsx +import prisma from "@/lib/prisma"; +//add-start +import { notFound } from "next/navigation"; +//add-end + +export default async function Post({ params }: { params: Promise<{ id: string }> }) { + //add-start + const { id } = await params; + const post = await prisma.post.findUnique({ + where: { id: parseInt(id) }, + include: { + author: true, + }, + }); + + if (!post) { + notFound(); + } + //add-end + + return ( +
+
+ //delete-start +

My first post

+

by Anonymous

+
+ No content available. +
+ //delete-end + //add-start +

{post.title}

+

by {post.author.name}

+
+ {post.content || "No content available."} +
+ //add-end +
+
+ ); +} +``` + +There's a lot of changes here, so let's break it down: + +- We're using Prisma Client to fetch the post by its `id`, which we get from the `params` object. +- In case the post doesn't exist (maybe it was deleted or maybe you typed a wrong ID), we call `notFound()` to display a 404 page. +- We then display the post's title, content, and author. If the post doesn't have content, we display a placeholder message. + +It's not the prettiest page, but it's a good start. Try it out by navigating to `localhost:3000/posts/1` and `localhost:3000/posts/2`. You can also test the 404 page by navigating to `localhost:3000/posts/999`. + +## 6. Add a new "posts" create page + +To round out our application, we'll add a "create" page for posts. This will let you write your own posts and save them to the database. + +As with the other pages, we'll start with a static page and then update it to be dynamic. + +```terminal +mkdir -p app/posts/new && touch app/posts/new/page.tsx +``` + +Now, add the following code to the `app/posts/new/page.tsx` file: + +```tsx file=app/posts/new/page.tsx +import Form from "next/form"; + +export default function NewPost() { + async function createPost(formData: FormData) { + "use server"; + + const title = formData.get("title") as string; + const content = formData.get("content") as string; + } + + return ( +
+

Create New Post

+
+
+ + +
+
+ +