Prisma + NextAuth

I have been building Toolgebra primarily as a client side utilities website. But recently I have also started building features where the utilities can be chained together into workflows. Now these workflows need to be saved of course, and that is where the need for authentication and database persistence comes in. In my previous projects I have used django + postgres + Auth0 for this. But since I am more serious about Toolgebra, I want a cost friendly solution to host and acquire users. A separate django service would incur separate hosting costs. Reusing the same NextJS app for the backend as well seemed like a better choice. So I did some research and decided to use
Though each of these seem to be fairly popular choices in the NextJS ecosystem, combining them all together took me nearly a weekend full of work as the documentation was all over the place. I am recording it as a blog post here for posterity. This setup works as of Nov 2025, but if you are reading this later, it is best to refer the latest docs of these services once again.
Installing Prisma
Installing Prisma itself is quite straightforward. You can just follow the guide for Nextjs. By default it shows the instructions for configuring Prisma Postgres - which is their hosted postgres solution. If you are not looking to use it, you can skip the installation of @prisma/extension-accelerate
To install just the packages necessary for using prisma with any postgres installation, just run these
npm install prisma tsx --save-dev
npm install @prisma/client dotenv
After installing it, you can run
npx prisma init
The installation mentions a --db argument which you can skip if you are not using their hosted postgres solution.
Once you have initiated, you can start the local database server automatically created by prisma by running
npx prisma dev
The connection to this DB server should have already configured been by the preceding init script which would have setup the DATABASE_URL var in your .env file. (If you are running your own local postgres db already, you can of course replace this DATABASE_URL with a connection string to that local db)
After this, the rest of their guide is about how to setup a functioning NextJS app using Prisma. If you already have an app into which you are integrating Prisma, you can skip it and jump to the NextAuth part. For the purposes of this tutorial, let’s continue to follow the guide and setup the example Posts app they have described.
Setting up the example app
First, add the models to the schema.prisma file as mentioned here and then add dotenv as mentioned here
Then seed the database like mentioned here, but you will need to change the first import line in the seed script to use import from prisma/client instead of just prisma
import { PrismaClient, Prisma } from "../app/generated/prisma/client";
Note that you can also use @/app instead of ../app.
Then setup lib/prisma.ts as mentioned here. But here as well you will need to change the first import line to
import { PrismaClient } from '../app/generated/prisma/client'
For some reason the prisma guide there mixes between src/app convention and app/ convention alternatingly. If you have chosen to use src/ folder as the root in nextjs repo, then use the ../src/app/generated path. If not it will be ../app/generated.
Then add the different pages mentioned in the guide - home, posts listing, post detail, new post form
Setting up NextAuth
If prisma setup is done, we can move on to configuring NextAuth. For NextAuth there are 2 sets of docs available. At NextAuth website and in Auth JS website. The AuthJS one is the newer one and we can follow that.
Let’s follow the steps there. You can first install the package
npm install next-auth@beta
Then run npx auth secret. This adds AUTH_SECRET var in .env.local. Since we are already using .env, we can just continue using that. Copy over this variable from .env.local to .env and delete the .env.local file.
Then you can just follow the steps here.
Add auth.ts
import NextAuth from "next-auth"
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [],
})
And then ./app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth" // Referring to the auth.ts we just created
export const { GET, POST } = handlers
But skip ./middleware.ts since it is no longer supported by nextjs
Configure Google oauth
Go to Google Cloud Console and setup a Web Client. Set the origin to localhost:3000 and callback url to localhost:3000/api/auth/callback/google
Then follow the steps for Google auth here. Add components/sign-in.tsx as mentioned there.
import { signIn } from "@/auth"
export function SignIn() {
return (
<form
action={async () => {
"use server"
await signIn()
}}
>
<button type="submit">Sign in</button>
</form>
)
}
We can also add components/sign-out.tsx as mentioned here
import { signOut } from "@/auth"
export function SignOut() {
return (
<form
action={async () => {
"use server"
await signOut()
}}
>
<button type="submit">Sign Out</button>
</form>
)
}
The guide doesn’t mention anything about where to add these signin and sign out components. So let’s add a navigation component
import Link from "next/link";
import { auth } from "../auth"
import SignIn from "./sign-in";
import SignOut from "./sign-out";
export default async function Navigation() {
const session = await auth();
return (
<>
<nav className="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-200">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center h-16">
{/* Logo */}
<Link href="/" className="flex items-center space-x-2">
<span className="text-xl font-semibold text-[#369b6f]">
Prisma + NextAuth + Neon
</span>
</Link>
{/* Spacer */}
<div className="flex-1"></div>
{/* Navigation Links */}
<div className="hidden md:flex items-center mr-8">
<Link
href="/posts"
className="text-gray-700 font-medium hover:text-[#369b6f] transition-all duration-200"
>
Posts
</Link>
</div>
{/* Auth Buttons */}
<div className="flex items-center space-x-2 sm:space-x-4">
{session?.user ? (
<SignOut/>
) : (
<SignIn/>
)}
</div>
</div>
</div>
</nav>
</>
);
}
And then include it in app/layout.tsx
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<Navigation/>
<main>
{children}
</main>
</body>
</html>
);
}
The app should now show a navigation bar at the top with the Sign in / Sign out buttons. If you are adding NextAuth to your own app, you can similarly add it somewhere in your navigation like this. But note that the sign in and sign out components we have coded now are server side components. They cannot be used in your client components. We will explore how to do that in a future post.
Integrate Prisma with NextAuth
Now that we have the auth working, we need to start persisting the authentication. For that we are going to use prisma adapter. Let’s first install it
npm install @auth/prisma-adapter
Then modify auth.ts to include prisma adapter
import NextAuth from "next-auth"
import Google from "next-auth/providers/google"
import { PrismaAdapter } from "@auth/prisma-adapter";
import prisma from "@/lib/prisma";
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [Google],
})
Now we need to add the schema needed by NextAuth. We can copy it from here. Note that we had already created an User table while creating the example app earlier. Now that will need to change as NextAuth uses a string column for user id and adds a few more columns to the user table. Since the schema will change for the User table, it is simpler to drop the current database and recreate it. (But if you already have a functioning app with an User table, you will need to research more on how to integrate NextAuth safely into your schema. That is outside the scope of this tutorial. )
To do this, let’s first remove the seed script so that it doesn’t see data again. Remove this line from prisma.config.ts
seed: `tsx prisma/seed.ts`
Then run this to drop the current tables.
npx prisma migrate reset
and then run this to create the new table structure
npx prisma migrate dev
After this, if you restart the dev server with npm run dev and do a signin, you should see a new entry created in the User table. You can verify that by viewing the table contents using prisma studio by running npx prisma studio
Now that we have a functioning setup for NextAuth + Prisma, let’s complete one final step in our example app just to complete this tutorial. Let’s configure the Post creation page to send session.userId as authorId instead of the hard-coded authorId we had copied from the Prisma tutorial.
export default async function NewPost() {
const session = await auth();
async function createPost(formData: FormData) {
"use server";
if(!session?.user?.id){
return;
}
const title = formData.get("title") as string;
const content = formData.get("content") as string;
await prisma.post.create({
data: {
title,
content,
authorId: session.user.id,
},
});
revalidatePath("/posts");
redirect("/posts");
}
if(!session?.user){
return <div>You need to sign in to create a post</div>
}
...
}
Conclusion
That concludes the tutorial. We mostly followed the already available official guides. But they don’t smoothly work together out of the box and we have to make various adjustments - which I figured out with a lot of trial and error. Hope this post saves you a couple of days of effort.
At the start of the post, I had mentioned that I used Neon as the hosted postgres solution to deploy Toolgebra on the cloud. I have published a separate post on how to integrate Prisma & Neon. Please check that out as well.