diff --git a/.github/workflows/lychee.yml b/.github/workflows/lychee.yml index 72cf3fa499..6bae422c2b 100644 --- a/.github/workflows/lychee.yml +++ b/.github/workflows/lychee.yml @@ -34,6 +34,7 @@ jobs: --exclude 'http://localhost.*' --exclude 'https://localhost.*' --exclude 'https://cockroachlabs.com' + --exclude 'https://www.gnu.org' --exclude '^/.*' './**/*.md' './**/*.mdx' workingDirectory: "content" @@ -60,6 +61,7 @@ jobs: --exclude 'http://localhost.*' --exclude 'https://localhost.*' --exclude 'https://cockroachlabs.com' + --exclude 'https://www.gnu.org' --exclude '^/.*' './**/*.md' './**/*.mdx' workingDirectory: "content" @@ -97,7 +99,7 @@ jobs: fi - name: 📝 Comment Broken Links - if: ${{ always() && github.event.pull_request.head.repo.fork == false }} + if: ${{ always() && github.event.pull_request.head.repo.fork == false && (steps.lychee.outputs.exit_code != 0 || (steps.lychee-retry.conclusion != 'skipped' && steps.lychee-retry.outputs.exit_code != 0)) }} uses: peter-evans/create-or-update-comment@v4 with: issue-number: ${{ github.event.pull_request.number }} diff --git a/content/800-guides/230-betterauth-nextjs.mdx b/content/800-guides/230-betterauth-nextjs.mdx index 5a048fb22c..4907a37fa0 100644 --- a/content/800-guides/230-betterauth-nextjs.mdx +++ b/content/800-guides/230-betterauth-nextjs.mdx @@ -1,8 +1,8 @@ --- -title: 'How to use Prisma ORM with Better-Auth and Next.js' -metaTitle: 'How to use Prisma ORM and Prisma Postgres with Better-Auth and Next.js' -description: 'Learn how to use Prisma ORM in a Next.js app with Better-Auth' -sidebar_label: 'Better-Auth (with Next.js)' +title: 'How to use Prisma ORM with Better Auth and Next.js' +metaTitle: 'How to use Prisma ORM and Prisma Postgres with Better Auth and Next.js' +description: 'Learn how to use Prisma ORM in a Next.js app with Better Auth' +sidebar_label: 'Better Auth (with Next.js)' image: '/img/guides/prisma-betterauth-nextjs-cover.png' completion_time: '25 min' community_section: true @@ -10,9 +10,9 @@ community_section: true ## Introduction -[Better-Auth](https://better-auth.com/) is a modern, open-source authentication solution for web applications. It's built with TypeScript, React, and Prisma to provide a simple and extensible auth experience. +[Better Auth](https://better-auth.com/) is a modern, open-source authentication solution for web applications. It's built with TypeScript and provides a simple and extensible auth experience with support for multiple database adapters, including Prisma. -In this guide, you'll wire Better-Auth into a brand-new [Next.js](https://nextjs.org/) app and persist users in a [Prisma Postgres](https://prisma.io/postgres) database. You can find a complete example of this guide on [GitHub](https://github.com/prisma/prisma-examples/tree/latest/orm/betterauth-nextjs). +In this guide, you'll wire Better Auth into a brand-new [Next.js](https://nextjs.org/) app and persist users in a [Prisma Postgres](https://prisma.io/postgres) database. You can find a complete example of this guide on [GitHub](https://github.com/prisma/prisma-examples/tree/latest/orm/betterauth-nextjs). ## Prerequisites @@ -78,15 +78,15 @@ Once installed, initialize Prisma in your project: npx prisma init --db --output ../src/generated/prisma ``` :::info -You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database like "My Better-Auth Project" +You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database like "My Better Auth Project" ::: This will create: -- A `prisma` directory with a `schema.prisma` file. -- A Prisma Postgres database. -- A `.env` file containing the `DATABASE_URL` at the project root. -- An `output` directory for the generated Prisma Client as `better-auth/generated/prisma`. +- A `prisma` directory with a `schema.prisma` file +- A Prisma Postgres database +- A `.env` file containing the `DATABASE_URL` at the project root +- An `output` directory for the generated Prisma Client as `better-auth/generated/prisma` ### 2.2. Configure the Prisma client generator @@ -98,7 +98,7 @@ npx prisma generate ### 2.3. Set up a global Prisma client -Create a `/lib` directory and a `prisma.ts` file inside it. This file will be used to create and export your Prisma Client instance. +In the `src` directory, create a `lib` folder and a `prisma.ts` file inside it. This file will be used to create and export your Prisma Client instance. ```terminal mkdir -p src/lib @@ -149,19 +149,19 @@ We recommend using a connection pooler (like [Prisma Accelerate](https://www.pri If you choose not to use one, **avoid** instantiating `PrismaClient` globally in long-lived environments. Instead, create and dispose of the client per request to prevent exhausting your database connections. ::: -## 3. Set up Better-Auth +## 3. Set up Better Auth -Now it's time to integrate Better-Auth for authentication. +Now it's time to integrate Better Auth for authentication. -### 3.1. Install and configure Better-Auth +### 3.1. Install and configure Better Auth -First, install the Better-Auth core package: +First, install the Better Auth core package: ```terminal npm install better-auth ``` -Next, generate a secure secret that Better-Auth will use to sign authentication tokens. This ensures your tokens cannot be messed with. +Next, generate a secure secret that Better Auth will use to sign authentication tokens. This ensures your tokens cannot be messed with. ```terminal npx @better-auth/cli@latest secret @@ -170,7 +170,7 @@ npx @better-auth/cli@latest secret Copy the generated secret and add it, along with your application's URL, to your `.env` file: ```dotenv file=.env showLineNumbers -# Better-Auth +# Better Auth //add-start BETTER_AUTH_SECRET=your-generated-secret BETTER_AUTH_URL=http://localhost:3000 @@ -180,13 +180,13 @@ BETTER_AUTH_URL=http://localhost:3000 DATABASE_URL="your-database-url" ``` -Now, create a configuration file for Better-Auth at `src/lib/auth.ts`: +Now, create a configuration file for Better Auth. In the `src/lib` directory, create an `auth.ts` file: ```terminal touch src/lib/auth.ts ``` -In this file, you'll configure Better-Auth to use the Prisma adapter, which allows it to persist user and session data in your database. You will also enable email and password authentication. +In this file, you'll configure Better Auth to use the Prisma adapter, which allows it to persist user and session data in your database. You will also enable email and password authentication. ```ts file=src/lib/auth.ts import { betterAuth } from 'better-auth' @@ -200,7 +200,7 @@ export const auth = betterAuth({ }) ``` -Better-Auth also supports other sign-in methods like social logins (Google, GitHub, etc.), which you can explore in their [documentation](https://www.better-auth.com/docs/authentication/email-password). +Better Auth also supports other sign-in methods like social logins (Google, GitHub, etc.), which you can explore in their [documentation](https://www.better-auth.com/docs/authentication/email-password). ```ts file=src/lib/auth.ts import { betterAuth } from 'better-auth' @@ -240,9 +240,9 @@ export const auth = betterAuth({ ``` ::: -### 3.2. Add Better-Auth models to your schema +### 3.2. Add Better Auth models to your schema -Better-Auth provides a CLI command to automatically add the necessary authentication models (`User`, `Session`, `Account`, and `Verification`) to your `schema.prisma` file. +Better Auth provides a CLI command to automatically add the necessary authentication models (`User`, `Session`, `Account`, and `Verification`) to your `schema.prisma` file. Run the following command: @@ -329,16 +329,16 @@ npx prisma migrate dev --name add-auth-models ## 4. Set up the API routes -Better-Auth needs an API endpoint to handle authentication requests like sign-in, sign-up, and sign-out. You'll create a catch-all API route in Next.js to handle all requests sent to `/api/auth/[...all]`. +Better Auth needs an API endpoint to handle authentication requests like sign-in, sign-up, and sign-out. You'll create a catch-all API route in Next.js to handle all requests sent to `/api/auth/[...all]`. -First, create the necessary directory and file: +In the `src/app/api` directory, create an `auth/[...all]` folder structure and a `route.ts` file inside it: ```terminal mkdir -p "src/app/api/auth/[...all]" touch "src/app/api/auth/[...all]/route.ts" ``` -Add the following code to the newly created `route.ts` file. This code uses a helper from Better-Auth to create Next.js-compatible `GET` and `POST` request handlers. +Add the following code to the newly created `route.ts` file. This code uses a helper from Better Auth to create Next.js-compatible `GET` and `POST` request handlers. ```ts import { auth } from "@/lib/auth"; @@ -347,7 +347,7 @@ import { toNextJsHandler } from "better-auth/next-js"; export const { POST, GET } = toNextJsHandler(auth); ``` -Next, you'll need a client-side utility to interact with these endpoints from your React components. Create a new file `src/lib/auth-client.ts`: +Next, you'll need a client-side utility to interact with these endpoints from your React components. In the `src/lib` directory, create an `auth-client.ts` file: ```terminal touch src/lib/auth-client.ts @@ -363,7 +363,11 @@ export const { signIn, signUp, signOut, useSession } = createAuthClient() ## 5. Set up your pages -Now, let's build the user interface for authentication. Create the pages for signing up, signing in, and a protected dashboard: +Now, let's build the user interface for authentication. In the `src/app` directory, create the following folder structure: + +- `sign-up/page.tsx` +- `sign-in/page.tsx` +- `dashboard/page.tsx` ```terminal mkdir -p src/app/{sign-up,sign-in,dashboard} @@ -410,7 +414,7 @@ export default function SignUpPage() { } ``` -Now, import the `signUp` function from your Better-Auth client and add the `handleSubmit` function. This function is triggered on form submission and calls the `signUp.email` method provided by Better-Auth, passing the user's name, email, and password. +Now, import the `signUp` function from your Better Auth client and add the `handleSubmit` function. This function is triggered on form submission and calls the `signUp.email` method provided by Better Auth, passing the user's name, email, and password. ```tsx file=src/app/sign-up/page.tsx "use client"; @@ -611,7 +615,7 @@ export default function SignInPage() { } ``` -Add the `handleSubmit` function, this time importing and using the `signIn.email` method from Better-Auth. +Add the `handleSubmit` function, this time importing and using the `signIn.email` method from Better Auth. ```tsx file=src/app/sign-in/page.tsx "use client"; @@ -778,7 +782,7 @@ export default function DashboardPage() { } ``` -Import the `useSession` hook from your Better-Auth client. This hook is the key to managing authentication state on the client side. It provides the session data and a pending status. +Import the `useSession` hook from your Better Auth client. This hook is the key to managing authentication state on the client side. It provides the session data and a pending status. ```tsx file=src/app/dashboard/page.tsx "use client"; @@ -816,7 +820,7 @@ export default function DashboardPage() { const router = useRouter(); const { data: session, isPending } = useSession(); - //add-start: redirect if not signed in + //add-start useEffect(() => { if (!isPending && !session?.user) { router.push("/sign-in"); @@ -851,7 +855,7 @@ export default function DashboardPage() { } }, [isPending, session, router]); - //add-start: loading and redirect states + //add-start if (isPending) return

Loading...

; if (!session?.user) @@ -890,7 +894,7 @@ export default function DashboardPage() { if (!session?.user) return

Redirecting...

; - //add-start: destructure user from session + //add-start const { user } = session; //add-end @@ -899,7 +903,7 @@ export default function DashboardPage() {

Dashboard

Welcome, {user.name || "User"}!

Email: {user.email}

- {/* add-start: sign out button */} + {/* add-start */} + +

Already have an account? Sign in here.

+ //add-end + + + +``` + +Now add a script to handle form submission. Import the `authClient` and add an event listener to the form that prevents the default submission behavior, extracts the form data, and calls the Better Auth sign-up method. + +```html file=src/pages/sign-up/index.astro +--- +export const prerender = false; +--- + + + + + + Sign Up + + +
+

Sign Up

+
+ + + + +
+

Already have an account? Sign in here.

+
+ //add-start + + //add-end + + +``` + +Finally, add a server-side check to redirect authenticated users away from this page. If a user is already signed in, they should be redirected to the dashboard instead. + +```html file=src/pages/sign-up/index.astro +--- +export const prerender = false; + +//add-start +if (Astro.locals.user?.id) return Astro.redirect("/dashboard"); +//add-end +--- + + + + + + Sign Up + + +
+

Sign Up

+
+ + + + +
+

Already have an account? Sign in here.

+
+ + + +``` + +### 7.2. Sign in page + +This page allows existing users to authenticate. Start with the basic HTML structure in `src/pages/sign-in/index.astro`. + +```html file=src/pages/sign-in/index.astro +--- +export const prerender = false; +--- + + + + + + Sign In + + +
+

Sign In

+
+ + +``` + +Add a form with input fields for email and password. This form will collect the user's credentials. + +```html file=src/pages/sign-in/index.astro +--- +export const prerender = false; +--- + + + + + + Sign In + + +
+

Sign In

+ //add-start +
+ + + +
+

Don't have an account? Sign up here.

+ //add-end +
+ + +``` + +Now add a script to handle form submission. Import the `authClient` and add an event listener that prevents default submission, extracts the form data, and calls the Better Auth sign-in method. + +```html file=src/pages/sign-in/index.astro +--- +export const prerender = false; +--- + + + + + + Sign In + + +
+

Sign In

+
+ + + +
+

Don't have an account? Sign up here.

+
+ //add-start + + //add-end + + +``` + +Finally, add a server-side check to redirect authenticated users away from this page. If a user is already signed in, they should be redirected to the dashboard instead. + +```html file=src/pages/sign-in/index.astro +--- +export const prerender = false; + +//add-start +if (Astro.locals.user?.id) return Astro.redirect("/dashboard"); +//add-end +--- + + + + + + Sign In + + +
+

Sign In

+
+ + + +
+

Don't have an account? Sign up here.

+
+ + + +``` + +### 7.3. Dashboard page + +This is the protected page for authenticated users. Start with the basic HTML structure in `src/pages/dashboard/index.astro`. + +```html file=src/pages/dashboard/index.astro +--- +export const prerender = false; +--- + + + + + + Dashboard + + +
+

Dashboard

+
+ + +``` + +Add a server-side check to protect this route. If the user is not authenticated, redirect them to the sign-in page. + +```html file=src/pages/dashboard/index.astro +--- +export const prerender = false; + +//add-start +if (!Astro.locals.user?.id) return Astro.redirect("/sign-in"); +//add-end +--- + + + + + + Dashboard + + +
+

Dashboard

+
+ + +``` + +Now display the authenticated user's information. The `Astro.locals.user` object contains the user data that was set by the middleware. + +```html file=src/pages/dashboard/index.astro +--- +export const prerender = false; + +if (!Astro.locals.user?.id) return Astro.redirect("/sign-in"); +--- + + + + + + Dashboard + + +
+

Dashboard

+ //add-next-line +
{JSON.stringify(Astro.locals.user, null, 2)}
+
+ + +``` + +Finally, add a sign-out button. Import the `authClient` and add a button that calls the sign-out method, allowing the user to log out and be redirected to the sign-in page. + +```html file=src/pages/dashboard/index.astro +--- +export const prerender = false; + +if (!Astro.locals.user?.id) return Astro.redirect("/sign-in"); +--- + + + + + + Dashboard + + +
+

Dashboard

+
{JSON.stringify(Astro.locals.user, null, 2)}
+ //add-next-line + +
+ //add-start + + //add-end + + +``` + +### 7.4. Home page + +Finally, update the home page to provide simple navigation. Replace the contents of `src/pages/index.astro` with the following: + +```html file=src/pages/index.astro +--- +export const prerender = false; +--- + + + + + + Better Auth + Astro + Prisma + + +
+

Better Auth + Astro + Prisma

+ { + Astro.locals.user ? ( +
+

Welcome back, {Astro.locals.user.name}!

+ Go to Dashboard +
+ ) : ( +
+ Sign Up + Sign In +
+ ) + } +
+ + +``` + +## 8. Test it out + +Your application is now fully configured. + +1. Start the development server to test it: + +```terminal +npm run dev +``` + +2. Navigate to `http://localhost:4321` in your browser. You should see the home page with "Sign Up" and "Sign In" links. + +3. Click on **Sign Up**, create a new account, and you should be redirected to the dashboard. You can then sign out and sign back in. + +4. To view the user data directly in your database, you can use Prisma Studio. + +```terminal +npx prisma studio +``` + +5. This will open a new tab in your browser where you can see the `User`, `Session`, and `Account` tables and their contents. + +:::success + +Congratulations! You now have a fully functional authentication system built with Better Auth, Prisma, and Astro. + +::: + +## Next steps + +- Add support for social login or magic links +- Implement password reset and email verification +- Add user profile and account management pages +- Deploy to Vercel or Netlify and secure your environment variables +- Extend your Prisma schema with custom application models + +## Further reading + +- [Better Auth documentation](https://www.better-auth.com/docs) +- [Prisma documentation](/orm/overview/introduction) +- [Astro documentation](https://astro.build/docs) diff --git a/sidebars.ts b/sidebars.ts index e38be5d27a..1240220659 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -447,6 +447,7 @@ const sidebars: SidebarsConfig = { "guides/clerk-nextjs", "guides/shopify", "guides/permit-io-access-control", + "guides/betterauth-astro", "guides/betterauth-nextjs", "guides/ai-sdk-nextjs", "guides/authjs-nextjs", diff --git a/static/img/guides/prisma-betterauth-astro-cover.png b/static/img/guides/prisma-betterauth-astro-cover.png new file mode 100644 index 0000000000..5ac0879424 Binary files /dev/null and b/static/img/guides/prisma-betterauth-astro-cover.png differ