> Full Neon documentation index: https://neon.com/docs/llms.txt # Getting started with Neon Auth and Next.js Learn how to setup Neon Auth in a Next.js application This guide walks you through building a demo todo application with **Next.js**, [Neon Auth](https://neon.com/docs/auth/overview), and **Drizzle ORM**. By following along, you'll learn how to integrate Neon Auth into your Next.js projects and manage database interactions with Drizzle ORM. The guide primarily focuses on using **Server actions** to securely handle authentication and database operations. [Optional steps](https://neon.com/guides/neon-auth-nextjs#optional-accessing-user-data-elsewhere) are included at the end of the guide to demonstrate additional ways of retrieving user information in a Next.js app (e.g., server components, client components, API routes). By the end, you'll have a fully functional todo application where users can sign up, log in, and manage their todos. Authentication and session management are powered by Neon Auth, while Drizzle ORM handles database interactions. ## Prerequisites Before you begin, ensure you have the following: - **Node.js:** Version `18` or later installed on your machine. You can download it from [nodejs.org](https://nodejs.org/). - **Neon account:** A free Neon account. If you don't have one, sign up at [Neon](https://console.neon.tech/signup). ## Create a Neon project with Neon Auth You'll need to create a Neon project and enable Neon Auth. 1. **Create a Neon project:** Navigate to [pg.new](https://pg.new) to create a new Neon project. Give your project a name, such as `next-neon-todo`. 2. **Enable Neon Auth:** - In your project's dashboard, go to the **Neon Auth** tab. - Click on the **Enable Neon Auth** button to set up authentication for your project. 3. **Copy your credentials:** - **Auth URL:** Found on the **Auth** page (e.g., `https://ep-xxx.neon.tech/neondb/auth`).  - **Database Connection String:** Found on the **Dashboard** (select "Pooled connection").  ## Set up the Next.js project Create a new Next.js project and install dependencies. 1. **Initialize the app:** ```bash npx create-next-app@latest next-neon-todo --yes cd next-neon-todo ``` 2. **Install dependencies:** ```bash npm install @neondatabase/neon-js @neondatabase/serverless drizzle-orm npm install -D drizzle-kit dotenv @types/node ``` ## Configure environment variables Create a `.env` file in the root of your project. ```env DATABASE_URL="postgresql://alex:AbC123dEf@ep-cool-darkness-a1b2c3d4-pooler.us-east-2.aws.neon.tech/dbname?sslmode=require&channel_binding=require" NEON_AUTH_BASE_URL="https://ep-xxx.neon.tech/neondb/auth" ``` ## Set up Drizzle ORM Drizzle ORM helps manage your database schema and queries. Alternatively, you can use any Postgres client of your choice. The core logic is to filter data based on the authenticated user provided by Neon Auth while performing database operations. ### Create Drizzle config Create `drizzle.config.ts` in the root of your project: ```typescript import 'dotenv/config'; import type { Config } from 'drizzle-kit'; export default { schema: './app/db/schema.ts', out: './drizzle', dialect: 'postgresql', schemaFilter: ['public', 'neon_auth'], dbCredentials: { url: process.env.DATABASE_URL!, }, } satisfies Config; ``` This config tells Drizzle Kit where to find your database schema and where to output migration files. The `schemaFilter` is configured to look at both the `public` and `neon_auth` schemas. The `neon_auth` schema is where Neon Auth stores its user data. ### Pull Neon Auth schema A key feature of Neon Auth is the automatic creation and maintenance of the Better Auth tables within the `neon_auth` schema. Since these tables reside in your Neon database, you can work with them directly using SQL queries or any Postgresβcompatible ORM, including defining foreign key relationships. To integrate Neon Auth tables into your Drizzle ORM setup, you need to introspect the existing `neon_auth` schema and generate the corresponding Drizzle schema definitions. This step is crucial because it makes Drizzle aware of the Neon Auth tables, allowing you to create relationships between your application data (like the `todos` table) and the user data managed by Neon Auth. 1. **Introspect the database:** Run the Drizzle Kit `pull` command to generate a schema file based on your existing Neon database tables. ```bash npx drizzle-kit pull ``` This command connects to your Neon database, inspects its structure, and creates `schema.ts` and `relations.ts` files inside a new `drizzle` folder. This file will contain the Drizzle schema definition for the Neon Auth tables. 2. **Organize schema files:** Create a new directory `app/db`. Move the generated `schema.ts` and `relations.ts` files from the `drizzle` directory to `app/db/schema.ts` and `app/db/relations.ts` respectively. ``` β π drizzle β β π meta β β π migration.sql β β π relations.ts βββββββββ β β π schema.ts ββββββββββββ€ β π app β β β π db β β β β π relations.ts <ββββββ€ β β β π schema.ts <βββββββββ β β π App.tsx β β¦ ``` 3. **Add the Todos table to your schema** Open `app/db/schema.ts` to view the `neon_auth` tables that Drizzle generated from your existing Neon database schema. At the bottom of the file, append the `todos` table definition as shown below: ```typescript {9,39-49} import { pgTable, pgSchema, uuid, text, timestamp, unique, boolean, bigint, } from 'drizzle-orm/pg-core'; import { sql } from 'drizzle-orm'; export const neonAuth = pgSchema('neon_auth'); // .. other Neon Auth table definitions .. export const userInNeonAuth = neonAuth.table( 'user', { id: uuid().defaultRandom().primaryKey().notNull(), name: text().notNull(), email: text().notNull(), emailVerified: boolean().notNull(), image: text(), createdAt: timestamp({ withTimezone: true, mode: 'string' }) .default(sql`CURRENT_TIMESTAMP`) .notNull(), updatedAt: timestamp({ withTimezone: true, mode: 'string' }) .default(sql`CURRENT_TIMESTAMP`) .notNull(), role: text(), banned: boolean(), banReason: text(), banExpires: timestamp({ withTimezone: true, mode: 'string' }), }, (table) => [unique('user_email_key').on(table.email)] ); export const todos = pgTable('todos', { id: bigint('id', { mode: 'number' }).primaryKey().generatedByDefaultAsIdentity(), text: text('text').notNull(), completed: boolean('completed').notNull().default(false), userId: uuid('user_id') .notNull() .references(() => userInNeonAuth.id), createdAt: timestamp('created_at').defaultNow(), }); export type Todo = typeof todos.$inferSelect; ``` The `todos` table contains the following columns: `id`, `text`, `completed`, and `user_id`. It is linked to the `user` table in the `neon_auth` schema via a foreign key relationship on the `user_id` column. ### Generate and apply migrations Now, generate the SQL migration file to create the `todos` table. ```bash npx drizzle-kit generate ``` This creates a new SQL file in the `drizzle` directory. Apply this migration to your Neon database by running: **Important: Issue with commented migrations** This is a [known issue](https://github.com/drizzle-team/drizzle-orm/issues/4851) in Drizzle. If `drizzle-kit pull` generated an initial migration file (e.g., `0000_...sql`) wrapped in block comments (`/* ... */`), `drizzle-kit migrate` may fail with an `unterminated /* comment` error. To resolve this, manually delete the contents of the `0000_...sql` file or replace the block comments with line comments (`--`). ```bash npx drizzle-kit migrate ``` Your `todos` table now exists in your Neon database. You can verify this in the **Tables** section of your Neon project dashboard. ### Initialize database client Create `app/db/index.ts` to initialize the Drizzle ORM client. ```typescript import { neon } from '@neondatabase/serverless'; import { drizzle } from 'drizzle-orm/neon-http'; const sql = neon(process.env.DATABASE_URL!); export const db = drizzle(sql); ``` Now you have Drizzle ORM set up with Neon Auth and a `todos` table ready for use in your Next.js application. ## Set up Neon Auth Integrate Neon Auth into your Next.js application for authentication and session management. ### Create Auth client Create a file `lib/auth/client.ts` at the root of your project to initialize the Neon Auth client. ```typescript 'use client'; import { createAuthClient } from '@neondatabase/neon-js/auth/next'; export const authClient = createAuthClient(); ``` ### Create API route Create `app/api/auth/[...path]/route.ts`. This file will handle authentication API requests on the server side. ```typescript import { authApiHandler } from '@neondatabase/neon-js/auth/next/server'; export const { GET, POST } = authApiHandler(); ``` ### Add Neon Auth UI provider Update `app/layout.tsx` to wrap your application with the `NeonAuthUIProvider`, which supplies authentication context and UI components. This setup also adds a global header containing a `UserButton` from [Neon Auth UI components](https://neon.com/docs/auth/reference/ui-components) for account management, ensuring the header is visible across all pages. ```tsx import { authClient } from '@/lib/auth/client'; import { NeonAuthUIProvider, UserButton } from '@neondatabase/neon-js/auth/react/ui'; import './globals.css'; export default function RootLayout({ children }: { children: React.ReactNode }) { return (
No tasks yet.
}Status: {session ? 'β Authenticated' : 'β Guest'}
{user && (User ID: {user.id}
)} {user && (Email: {user.email}
)}
{JSON.stringify({ session, user }, null, 2)}
Status: {data?.session ? 'β Authenticated' : 'β Guest'}
{data?.user && (User ID: {data.user.id}
)}
{JSON.stringify(data, null, 2)}