Skip to main content
Docs

Integrate Prisma Postgres with Clerk

This tutorial shows you how to build a Next.js application with Clerk authentication and Prisma Postgres. You'll create a simple messaging app where users can store and manage their personal messages using Prisma ORM for database operations.

Create a new Next.js project

Create a new Next.js project using the following command:

terminal
npx create-next-app clerk-prisma-example --typescript --eslint --tailwind --use-npm --no-src-dir --app --import-alias "@/*"

Navigate to the project directory and install the required dependencies:

terminal
cd clerk-prisma-example
npm install prisma --save-dev
npm install tsx --save-dev
npm install @prisma/extension-accelerate

Integrate the Next.js Clerk SDK

Follow the to integrate the Clerk Next.js SDK into your application.

Protect your application routes

To ensure that only authenticated users can access your application, modify to require authentication for every route.

middleware.ts
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware(async (auth) => {
  await auth.protect()
})

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}

Set up the application schema and database connection

Next, you'll need to initialize Prisma and set up a Prisma Postgres database.

Run the following command to start a local Prisma Postgres database:

terminal
npx prisma dev

Initialize Prisma in your project:

terminal
npx prisma init --output ../app/generated/prisma

After initializing Prisma, your environment variable file should have the following values:

.env
DATABASE_URL=PRISMA_POSTGRES_CONNECTION_STRING
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
CLERK_SECRET_KEY=YOUR_SECRET_KEY

You will find the file schema.prisma in the prisma/ directory, this defines the database schema. Add to the schema a new table called UserMessage with the columns user_id, create_ts, and message. The user_id column will be used to store the user's Clerk ID. You will need to change the provider from "prisma-client-js" to "prisma-client".

prisma/schema.prisma
generator client {
  // Change the provider from "prisma-client-js" to "prisma-client"
  provider = "prisma-client"
  output   = "../app/generated/prisma"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model UserMessage {
  user_id   String   @id
  create_ts DateTime @default(now())
  message   String
}

Push the schema to the database

Run the following command to push the schema to the database:

terminal
npx prisma db push

Create a reusable Prisma Client instance

To be able to use the Prisma Client instance globally in your code, create a reusable Prisma Client instance in lib/prisma.ts:

lib/prisma.ts
import { PrismaClient } from '../app/generated/prisma/client'

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined
}

export const prisma = globalForPrisma.prisma ?? new PrismaClient()

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

export default prisma

Create Server Actions to handle user interactions

To handle form submissions for adding and deleting user messages, create two Server Actions in app/actions.ts. Use Clerk's to obtain the user ID, which will be used to interact with the database.

app/actions.ts
'use server'

import { auth } from '@clerk/nextjs/server'
import prisma from '@/lib/prisma'
import { revalidatePath } from 'next/cache'

export async function createUserMessage(formData: FormData) {
  const { userId } = await auth()
  if (!userId) throw new Error('User not found')
  const message = formData.get('message') as string
  await prisma.userMessage.create({
    data: {
      user_id: userId,
      message,
    },
  })

  revalidatePath('/')
}
export async function deleteUserMessage() {
  const { userId } = await auth()
  if (!userId) throw new Error('User not found')
  await prisma.userMessage.deleteMany({
    where: { user_id: userId },
  })

  revalidatePath('/')
}

Create the UI for the Home Page

In your app/page.tsx file, add the following code to create the UI for the home page. If a message exists, the user can view and delete it; otherwise, they can add a new message.

To retrieve the user's messages, use Clerk's to obtain the user's ID. Then, use this ID to query the database for the user's messages.

To enable the user to delete or add a message, use the deleteUserMessage() and createUserMessage() actions created in the previous step.

app/page.tsx
import { createUserMessage, deleteUserMessage } from './actions'
import prisma from '@/lib/prisma'
import { auth } from '@clerk/nextjs/server'

export default async function Home() {
  const { userId } = await auth()
  if (!userId) throw new Error('User not found')
  const existingMessage = await prisma.userMessage.findUnique({
    where: { user_id: userId },
  })

  return (
    <main className="grid justify-center p-4">
      <div className="w-full max-w-md space-y-6">
        <h1 className="text-3xl font-bold text-center">Prisma + Clerk Example</h1>
        {existingMessage ? (
          <div className="space-y-4 text-center">
            <p className="bg-neutral-800 text-white p-3 rounded">{existingMessage.message}</p>
            <form action={deleteUserMessage}>
              <button className="bg-red-500 text-white px-4 py-2 rounded w-full">
                Delete Message
              </button>
            </form>
          </div>
        ) : (
          <form action={createUserMessage} className="space-y-4">
            <input
              type="text"
              name="message"
              placeholder="Enter a message"
              className="w-full p-2 border rounded"
              required
            />
            <button className="bg-blue-500 text-white px-4 py-2 rounded w-full">
              Save Message
            </button>
          </form>
        )}
      </div>
    </main>
  )
}

Run the application

Run your application and open http://localhost:3000 in your browser. Sign in with Clerk and interact with the application to add and delete user messages.

Feedback

What did you think of this content?

Last updated on