Skip to content

Commit 3b64640

Browse files
aidankmcalisteralexisintechSarahSoutoul
authored
Added Prisma Postgres guide to docs/integrations/databases/ (#2465)
Co-authored-by: Alexis Aguilar <[email protected]> Co-authored-by: Sarah Soutoul <[email protected]>
1 parent 5167fbb commit 3b64640

File tree

3 files changed

+305
-0
lines changed

3 files changed

+305
-0
lines changed
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
---
2+
title: Integrate Prisma Postgres with Clerk
3+
description: Learn how to integrate Clerk into your Prisma Postgres application.
4+
---
5+
6+
<TutorialHero
7+
beforeYouStart={[
8+
{
9+
title: "Follow the Next.js quickstart",
10+
link: "/docs/nextjs/getting-started/quickstart",
11+
icon: "nextjs",
12+
}
13+
]}
14+
/>
15+
16+
Integrating Prisma Postgres with Clerk gives you the benefits of using a Prisma Postgres database while leveraging Clerk's authentication, prebuilt components, and webhooks. This guide will show you how to create a simple blog application that allows users to create and read posts using Prisma Postgres and Clerk. This guide uses Next.js App Router but the same principles can be applied to other SDKs.
17+
18+
<Steps>
19+
## Install Prisma
20+
21+
Run the following command to install Prisma:
22+
23+
```sh {{ filename: 'terminal' }}
24+
npm install prisma tsx --save-dev
25+
```
26+
27+
## Initialize Prisma
28+
29+
Run the following command to initialize Prisma:
30+
31+
```sh {{ filename: 'terminal' }}
32+
npx prisma init --output ../app/generated/prisma
33+
```
34+
35+
This will:
36+
37+
- 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.
38+
- Create a `prisma.config.ts` file in the root of your project.
39+
- Update your `.env` file to include a `DATABASE_URL` environment variable, which is used to store your database connection string for your Prisma Postgres database.
40+
41+
## Update the Prisma configuration
42+
43+
Inside the `prisma.config.ts` file, add `import "dotenv/config";` so that it can load the `DATABASE_URL` environment variable from your `.env` file.
44+
45+
```ts {{ filename: 'prisma.config.ts', mark: [1] }}
46+
import 'dotenv/config'
47+
import { defineConfig, env } from 'prisma/config'
48+
49+
export default defineConfig({
50+
schema: 'prisma/schema.prisma',
51+
migrations: {
52+
path: 'prisma/migrations',
53+
},
54+
engine: 'classic',
55+
datasource: {
56+
url: env('DATABASE_URL'),
57+
},
58+
})
59+
```
60+
61+
## Update the database schema
62+
63+
In the `prisma/schema.prisma` file, update the schema to include a new model called `Post` with the columns `id`, `title`, `content`, `published`, and `authorId`. The `id` column will be used as the post's primary key, and the `authorId` column will be used to store the user's Clerk ID.
64+
65+
```prisma {{ filename: 'prisma/schema.prisma', mark: [[11, 17]] }}
66+
generator client {
67+
provider = "prisma-client"
68+
output = "../app/generated/prisma"
69+
}
70+
71+
datasource db {
72+
provider = "postgresql"
73+
url = env("DATABASE_URL")
74+
}
75+
76+
model Post {
77+
id Int @id @default(autoincrement())
78+
title String
79+
content String?
80+
published Boolean @default(false)
81+
authorId String
82+
}
83+
```
84+
85+
## Push the schema to the database
86+
87+
Run the following command to push the updated schema to the database:
88+
89+
```sh {{ filename: 'terminal' }}
90+
npx prisma db push
91+
```
92+
93+
## Create a reusable Prisma Client instance
94+
95+
At the root of your project, create a folder called `lib`. Inside of it, create a new file called `prisma.ts` and add the following code to it. This will set up a single Prisma Client instance, which is used to interact with your database, and bind 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 with Next.js in development mode.
96+
97+
```typescript {{ filename: 'lib/prisma.ts' }}
98+
import { PrismaClient } from '@prisma/client'
99+
100+
const prisma = new PrismaClient()
101+
102+
const globalForPrisma = global as unknown as { prisma: typeof prisma }
103+
104+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
105+
106+
export default prisma
107+
```
108+
109+
## Create the UI for the homepage
110+
111+
Now that all of the set up is complete, it's time to start building out your app.
112+
113+
Replace the contents of `app/page.tsx` with the following code. This page fetches all posts from your database and displays them on the homepage, showing the title and author ID for each post. It uses the [`prisma.post.findMany()`](https://www.prisma.io/docs/orm/reference/prisma-client-reference?utm_source=docs#findmany) method, which is a Prisma Client method that retrieves all records from the database.
114+
115+
```tsx {{ filename: 'app/page.tsx' }}
116+
import Link from 'next/link'
117+
import prisma from '@/lib/prisma'
118+
119+
export default async function Page() {
120+
const posts = await prisma.post.findMany() // Query the `Post` model for all posts
121+
122+
return (
123+
<div className="mx-auto mt-8 flex min-h-screen max-w-2xl flex-col">
124+
<h1 className="mb-8 text-4xl font-bold">Posts</h1>
125+
126+
<div className="mb-8 flex max-w-2xl flex-col space-y-4">
127+
{posts.map((post) => (
128+
<Link
129+
key={post.id}
130+
href={`/posts/${post.id}`}
131+
className="hover:bg-neutral-100 dark:hover:bg-neutral-800 flex flex-col rounded-lg px-2 py-4 transition-all hover:underline"
132+
>
133+
<span className="text-lg font-semibold">{post.title}</span>
134+
<span className="text-sm">by {post.authorId}</span>
135+
</Link>
136+
))}
137+
</div>
138+
139+
<Link
140+
href="/posts/create"
141+
className="inline-block rounded-lg border-2 border-current px-4 py-2 text-current transition-all hover:scale-[0.98]"
142+
>
143+
Create New Post
144+
</Link>
145+
</div>
146+
)
147+
}
148+
```
149+
150+
## Create a new post
151+
152+
Create a new file called `app/posts/create/page.tsx` and add the following code to it. This page allows users to create new posts. It uses Clerk's [`auth`](/docs/reference/backend/types/auth-object) object to get the user's ID.
153+
154+
- If there is no user ID, the user is not signed in, so a sign in button is displayed.
155+
- If the user is signed in, the "Create New Post" form is displayed. When the form is submitted, the `createPost()` function is called. This function creates a new post in the database using the [`prisma.post.create()`](https://www.prisma.io/docs/orm/reference/prisma-client-reference?utm_source=docs#create) method, which is a Prisma Client method that creates a new record in the database.
156+
157+
```tsx {{ filename: 'app/posts/create/page.tsx' }}
158+
import Form from 'next/form'
159+
import prisma from '@/lib/prisma'
160+
import { redirect } from 'next/navigation'
161+
import { SignInButton, useAuth } from '@clerk/nextjs'
162+
import { revalidatePath } from 'next/cache'
163+
import { auth } from '@clerk/nextjs/server'
164+
165+
export default async function NewPost() {
166+
// The `Auth` object gives you access to properties like `userId`
167+
// Accessing the `Auth` object differs depending on the SDK you're using
168+
// https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object
169+
const { userId } = await auth()
170+
171+
// Protect this page from unauthenticated users
172+
if (!userId) {
173+
return (
174+
<div className="flex h-[calc(100vh-4rem)] flex-col items-center justify-center space-y-4">
175+
<p>You must be signed in to create a post.</p>
176+
<SignInButton>
177+
<button
178+
type="submit"
179+
className="inline-block cursor-pointer rounded-lg border-2 border-current px-4 py-2 text-current transition-all hover:scale-[0.98]"
180+
>
181+
Sign in
182+
</button>
183+
</SignInButton>
184+
</div>
185+
)
186+
}
187+
188+
async function createPost(formData: FormData) {
189+
'use server'
190+
191+
// Type check
192+
if (!userId) return
193+
194+
const title = formData.get('title') as string
195+
const content = formData.get('content') as string
196+
197+
await prisma.post.create({
198+
data: {
199+
title,
200+
content,
201+
authorId: userId,
202+
},
203+
})
204+
205+
revalidatePath('/')
206+
redirect('/')
207+
}
208+
209+
return (
210+
<div className="mx-auto max-w-2xl p-4">
211+
<h1 className="mb-6 text-2xl font-bold">Create New Post</h1>
212+
<Form action={createPost} className="space-y-6">
213+
<div>
214+
<label htmlFor="title" className="mb-2 block text-lg">
215+
Title
216+
</label>
217+
<input
218+
type="text"
219+
id="title"
220+
name="title"
221+
placeholder="Enter your post title"
222+
className="w-full rounded-lg border px-4 py-2"
223+
/>
224+
</div>
225+
<div>
226+
<label htmlFor="content" className="mb-2 block text-lg">
227+
Content
228+
</label>
229+
<textarea
230+
id="content"
231+
name="content"
232+
placeholder="Write your post content here..."
233+
rows={6}
234+
className="w-full rounded-lg border px-4 py-2"
235+
/>
236+
</div>
237+
<button
238+
type="submit"
239+
className="inline-block w-full rounded-lg border-2 border-current px-4 py-2 text-current transition-all hover:scale-[0.98]"
240+
>
241+
Create Post
242+
</button>
243+
</Form>
244+
</div>
245+
)
246+
}
247+
```
248+
249+
## Query a single record
250+
251+
Create a new file called `app/posts/[id]/page.tsx` and add the following code to it. This page uses the URL parameters to get the post's ID, and then fetches it from your database and displays it on the page, showing the title, author ID, and content. It uses the [`prisma.post.findUnique()`](https://www.prisma.io/docs/orm/reference/prisma-client-reference?utm_source=docs#findunique) method, which is a Prisma Client method that retrieves a single record from the database.
252+
253+
```tsx {{ filename: 'app/posts/[id]/page.tsx' }}
254+
import prisma from '@/lib/prisma'
255+
256+
export default async function Post({ params }: { params: Promise<{ id: string }> }) {
257+
const { id } = await params
258+
const post = await prisma.post.findUnique({
259+
where: { id: parseInt(id) },
260+
})
261+
262+
if (!post) {
263+
return (
264+
<div className="mx-auto mt-8 flex min-h-screen max-w-2xl flex-col">
265+
<div>No post found.</div>
266+
</div>
267+
)
268+
}
269+
270+
return (
271+
<div className="mx-auto mt-8 flex min-h-screen max-w-2xl flex-col">
272+
{post && (
273+
<article className="w-full max-w-2xl">
274+
<h1 className="mb-2 text-2xl font-bold sm:text-3xl md:text-4xl">{post.title}</h1>
275+
<p className="text-sm sm:text-base">by {post.authorId}</p>
276+
<div className="prose prose-gray prose-sm sm:prose-base lg:prose-lg mt-4 sm:mt-8">
277+
{post.content || 'No content available.'}
278+
</div>
279+
</article>
280+
)}
281+
</div>
282+
)
283+
}
284+
```
285+
286+
## Test your app
287+
288+
Run your application with the following command:
289+
290+
```sh {{ filename: 'terminal' }}
291+
npm run dev
292+
```
293+
294+
Visit your application at `http://localhost:3000`. Sign in with Clerk and interact with the application to create and read posts. You should be able to see the posts you created on the homepage, and select a single post to view it.
295+
</Steps>

docs/guides/development/integrations/overview.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ description: Learn about the available integrations with Clerk.
7272

7373
---
7474

75+
- [Prisma Postgres](/docs/guides/development/integrations/databases/prisma-postgres)
76+
- Build applications using Prisma Postgres with Clerk as your authentication provider.
77+
- {<svg width="58" height="72" viewBox="0 0 58 72" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0.522473 45.0933C-0.184191 46.246 -0.173254 47.7004 0.550665 48.8423L13.6534 69.5114C14.5038 70.8529 16.1429 71.4646 17.6642 71.0082L55.4756 59.6648C57.539 59.0457 58.5772 56.7439 57.6753 54.7874L33.3684 2.06007C32.183 -0.511323 28.6095 -0.722394 27.1296 1.69157L0.522473 45.0933ZM32.7225 14.1141C32.2059 12.9187 30.4565 13.1028 30.2001 14.3796L20.842 60.9749C20.6447 61.9574 21.5646 62.7964 22.5248 62.5098L48.6494 54.7114C49.4119 54.4838 49.8047 53.6415 49.4891 52.9111L32.7225 14.1141Z" fill="white"/></svg>}
78+
79+
---
80+
7581
- [Shopify](/docs/guides/development/integrations/platforms/shopify)
7682
- Use Clerk as your preferred Auth solution for your Shopify store.
7783
- {<svg width="32" height="32" viewBox="0 0 292 292" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M223.774 57.34c-.201-1.46-1.48-2.268-2.537-2.357-1.055-.088-23.383-1.743-23.383-1.743s-15.507-15.395-17.209-17.099c-1.703-1.703-5.029-1.185-6.32-.805-.19.056-3.388 1.043-8.678 2.68-5.18-14.906-14.322-28.604-30.405-28.604-.444 0-.901.018-1.358.044C129.31 3.407 123.644.779 118.75.779c-37.465 0-55.364 46.835-60.976 70.635-14.558 4.511-24.9 7.718-26.221 8.133-8.126 2.549-8.383 2.805-9.45 10.462C21.3 95.806.038 260.235.038 260.235l165.678 31.042 89.77-19.42S223.973 58.8 223.775 57.34zM156.49 40.848l-14.019 4.339c.005-.988.01-1.96.01-3.023 0-9.264-1.286-16.723-3.349-22.636 8.287 1.04 13.806 10.469 17.358 21.32zm-27.638-19.483c2.304 5.773 3.802 14.058 3.802 25.238 0 .572-.005 1.095-.01 1.624-9.117 2.824-19.024 5.89-28.953 8.966 5.575-21.516 16.025-31.908 25.161-35.828zm-11.131-10.537c1.617 0 3.246.549 4.805 1.622-12.007 5.65-24.877 19.88-30.312 48.297l-22.886 7.088C75.694 46.16 90.81 10.828 117.72 10.828z" fill="#95BF46"/><path d="M221.237 54.983c-1.055-.088-23.383-1.743-23.383-1.743s-15.507-15.395-17.209-17.099c-.637-.634-1.496-.959-2.394-1.099l-12.527 256.233 89.762-19.418S223.972 58.8 223.774 57.34c-.201-1.46-1.48-2.268-2.537-2.357" fill="#5E8E3E"/><path d="M135.242 104.585l-11.069 32.926s-9.698-5.176-21.586-5.176c-17.428 0-18.305 10.937-18.305 13.693 0 15.038 39.2 20.8 39.2 56.024 0 27.713-17.577 45.558-41.277 45.558-28.44 0-42.984-17.7-42.984-17.7l7.615-25.16s14.95 12.835 27.565 12.835c8.243 0 11.596-6.49 11.596-11.232 0-19.616-32.16-20.491-32.16-52.724 0-27.129 19.472-53.382 58.778-53.382 15.145 0 22.627 4.338 22.627 4.338" fill="#FFF"/></svg>}

docs/manifest.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,10 @@
886886
"title": "Nhost",
887887
"href": "/docs/guides/development/integrations/databases/nhost"
888888
},
889+
{
890+
"title": "Prisma Postgres",
891+
"href": "/docs/guides/development/integrations/databases/prisma-postgres"
892+
},
889893
{
890894
"title": "Supabase",
891895
"href": "/docs/guides/development/integrations/databases/supabase"

0 commit comments

Comments
 (0)