# Integrate Neon Postgres with Clerk

**Before you start**

- [Set up a Clerk application](https://clerk.com/docs/getting-started/quickstart/setup-clerk.md)
- [Set up a Neon project in the Neon console](https://console.neon.tech)

This tutorial demonstrates how to integrate Neon Postgres with Clerk in a Next.js application, using `drizzle-orm` and `drizzle-kit` to interact with the database. The tutorial guides you through setting up a simple application that enables users to add, view, and delete messages using Server Actions and Middleware with Clerk.

1. ## Create a new Next.js project

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

      filename: terminal
      ```sh
      npx create-next-app clerk-neon-example --typescript --eslint --tailwind --use-npm --no-src-dir --app --import-alias "@/*"
      ```
   2. Navigate to the project directory and install the required dependencies:

      filename: terminal
      ```sh
      cd clerk-neon-example
      npm install @neondatabase/serverless
      npm install drizzle-orm --legacy-peer-deps
      npm install -D drizzle-kit
      ```
2. ## Integrate the Next.js Clerk SDK

   Follow the [Next.js quickstart](https://clerk.com/docs/nextjs/getting-started/quickstart.md) to integrate the Clerk Next.js SDK into your application.
3. ## Protect your application routes

   To ensure that only authenticated users can access your application, modify [clerkMiddleware](https://clerk.com/docs/reference/nextjs/clerk-middleware.md) to require authentication for every route.

   > If you're using Next.js ≤15, name your file `middleware.ts` instead of `proxy.ts`. The code itself remains the same; only the filename changes.

   filename: proxy.ts

   ```typescript
   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)(.*)',
       // Always run for Clerk-specific frontend API routes
       '/__clerk/(.*)',
     ],
   }
   ```
4. ## Set your neon connection string

   Add the Neon connection string to your project's environment variables. You can find the Neon connection string in the [Neon console](https://console.neon.tech/) - see the [Neon docs](https://neon.tech/docs/connect/connect-from-any-app) for more information.

   Your environment variable file should have the following values:

   filename: .env

   ```env
   DATABASE_URL=NEON_DB_CONNECTION_STRING
   NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY={{pub_key}}
   CLERK_SECRET_KEY={{secret}}
   ```
5. ## Set up the application schema and database connection

   1. Inside the `app/`, create a `db/` directory.

   2. Create a `schema.ts` file in the `db/` directory that defines the database schema. The schema will include a table called `user_messages` with the columns `user_id`, `create_ts`, and `message`.The `user_id` column will be used to store the user's Clerk ID.

      filename: app/db/schema.ts

      ```typescript
      import { pgTable, text, timestamp } from 'drizzle-orm/pg-core'

      export const UserMessages = pgTable('user_messages', {
        user_id: text('user_id').primaryKey().notNull(),
        createTs: timestamp('create_ts').defaultNow().notNull(),
        message: text('message').notNull(),
      })
      ```

   3. Create an `index.ts` file in the `db` directory to set up the database connection.

      filename: app/db/index.ts

      ```typescript
      import { loadEnvConfig } from '@next/env'
      import { neon } from '@neondatabase/serverless'
      import { drizzle } from 'drizzle-orm/neon-http'
      import { UserMessages } from './schema'

      loadEnvConfig(process.cwd())

      if (!process.env.DATABASE_URL) {
        throw new Error('DATABASE_URL must be a Neon postgres connection string')
      }

      const sql = neon(process.env.DATABASE_URL)
      export const db = drizzle(sql, {
        schema: { UserMessages },
      })
      ```
6. ## Push the schema to the database

   1. To load the schema into the database, create a `drizzle.config.ts` file at the root of your project and add the following configuration:

      filename: drizzle.config.ts

      ```typescript
      import { defineConfig } from 'drizzle-kit'
      import { loadEnvConfig } from '@next/env'

      loadEnvConfig(process.cwd())

      if (!process.env.DATABASE_URL) {
        throw new Error('DATABASE_URL must be a Neon postgres connection string')
      }

      export default defineConfig({
        dialect: 'postgresql',
        dbCredentials: {
          url: process.env.DATABASE_URL,
        },
        schema: './app/db/schema.ts',
      })
      ```

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

      filename: terminal

      ```sh
      npx drizzle-kit push
      ```
7. ## 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 [auth() helper](https://clerk.com/docs/reference/nextjs/app-router/auth.md) to obtain the user ID, which will be used to interact with the database.

   filename: app/actions.ts

   ```typescript
   'use server'

   import { auth } from '@clerk/nextjs/server'
   import { UserMessages } from './db/schema'
   import { db } from './db'
   import { eq } from 'drizzle-orm'

   export async function createUserMessage(formData: FormData) {
     const { isAuthenticated, userId } = await auth()
     if (!isAuthenticated) throw new Error('User not found')

     const message = formData.get('message') as string
     await db.insert(UserMessages).values({
       user_id: userId,
       message,
     })
   }

   export async function deleteUserMessage() {
     const { isAuthenticated, userId } = await auth()
     if (!isAuthenticated) throw new Error('User not found')

     await db.delete(UserMessages).where(eq(UserMessages.user_id, userId))
   }
   ```
8. ## 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 [auth() helper](https://clerk.com/docs/reference/nextjs/app-router/auth.md) 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.

   filename: app/page.tsx

   ```tsx
   import { createUserMessage, deleteUserMessage } from './actions'
   import { db } from './db'
   import { auth } from '@clerk/nextjs/server'

   export default async function Home() {
     const { isAuthenticated, userId } = await auth()
     if (!isAuthenticated) throw new Error('User not found')
     const existingMessage = await db.query.UserMessages.findFirst({
       where: (messages, { eq }) => eq(messages.user_id, userId),
     })

     return (
       <main>
         <h1>Neon + Clerk Example</h1>
         {existingMessage ? (
           <div>
             <p>{existingMessage.message}</p>
             <form action={deleteUserMessage}>
               <button>Delete Message</button>
             </form>
           </div>
         ) : (
           <form action={createUserMessage}>
             <input type="text" name="message" placeholder="Enter a message" />
             <button>Save Message</button>
           </form>
         )}
       </main>
     )
   }
   ```
9. ## 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.

---

## Sitemap

[Overview of all docs pages](https://clerk.com/docs/llms.txt)
