├── .env.example ├── .eslintrc.json ├── .gitignore ├── README.md ├── app ├── api │ └── auth │ │ └── [...nextauth] │ │ └── route.ts ├── auth.config.ts ├── auth.ts ├── db.ts ├── favicon.ico ├── form.tsx ├── globals.css ├── layout.tsx ├── login │ └── page.tsx ├── page.tsx ├── protected │ └── page.tsx ├── register │ └── page.tsx └── submit-button.tsx ├── middleware.ts ├── next-env.d.ts ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── tailwind.config.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | # Create a Postgres database on Vercel: https://vercel.com/postgres 2 | POSTGRES_URL= 3 | POSTGRES_PRISMA_URL= 4 | POSTGRES_URL_NON_POOLING= 5 | 6 | # Generate one here: https://generate-secret.vercel.app/32 (only required for localhost) 7 | AUTH_SECRET= 8 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env 30 | .env.local 31 | .env.development.local 32 | .env.test.local 33 | .env.production.local 34 | 35 | # vercel 36 | .vercel 37 | .env*.local 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js + PostgreSQL Auth Starter 2 | 3 | This is a [Next.js](https://nextjs.org/) starter kit that uses [NextAuth.js](https://next-auth.js.org/) for simple email + password login, [Drizzle](https://orm.drizzle.team) as the ORM, and a [Neon Postgres](https://vercel.com/postgres) database to persist the data. 4 | 5 | ## Deploy Your Own 6 | 7 | You can clone & deploy it to Vercel with one click: 8 | 9 | [](https://vercel.com/new/clone?demo-title=Next.js%20Prisma%20PostgreSQL%20Auth%20Starter&demo-description=Simple%20Next.js%2013%20starter%20kit%20that%20uses%20Next-Auth%20for%20auth%20and%20Prisma%20PostgreSQL%20as%20a%20database.&demo-url=https%3A%2F%2Fnextjs-postgres-auth.vercel.app%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F7rsVQ1ZBSiWe9JGO6FUeZZ%2F210cba91036ca912b2770e0bd5d6cc5d%2Fthumbnail.png&project-name=Next.js%%20Prisma%20PostgreSQL%20Auth%20Starter&repository-name=nextjs-postgres-auth-starter&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnextjs-postgres-auth-starter&from=templates&skippable-integrations=1&env=AUTH_SECRET&envDescription=Generate%20a%20random%20secret%3A&envLink=https://generate-secret.vercel.app/&stores=%5B%7B"type"%3A"postgres"%7D%5D) 10 | 11 | ## Developing Locally 12 | 13 | You can clone & create this repo with the following command 14 | 15 | ```bash 16 | npx create-next-app nextjs-typescript-starter --example "https://github.com/vercel/nextjs-postgres-auth-starter" 17 | ``` 18 | 19 | ## Getting Started 20 | 21 | First, run the development server: 22 | 23 | ```bash 24 | pnpm dev 25 | ``` 26 | 27 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 28 | 29 | ## Learn More 30 | 31 | To learn more about Next.js, take a look at the following resources: 32 | 33 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 34 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 35 | 36 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 37 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | export { GET, POST } from 'app/auth'; 2 | -------------------------------------------------------------------------------- /app/auth.config.ts: -------------------------------------------------------------------------------- 1 | import { NextAuthConfig } from 'next-auth'; 2 | 3 | export const authConfig = { 4 | pages: { 5 | signIn: '/login', 6 | }, 7 | providers: [ 8 | // added later in auth.ts since it requires bcrypt which is only compatible with Node.js 9 | // while this file is also used in non-Node.js environments 10 | ], 11 | callbacks: { 12 | authorized({ auth, request: { nextUrl } }) { 13 | let isLoggedIn = !!auth?.user; 14 | let isOnDashboard = nextUrl.pathname.startsWith('/protected'); 15 | 16 | if (isOnDashboard) { 17 | if (isLoggedIn) return true; 18 | return false; // Redirect unauthenticated users to login page 19 | } else if (isLoggedIn) { 20 | return Response.redirect(new URL('/protected', nextUrl)); 21 | } 22 | 23 | return true; 24 | }, 25 | }, 26 | } satisfies NextAuthConfig; 27 | -------------------------------------------------------------------------------- /app/auth.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from 'next-auth'; 2 | import Credentials from 'next-auth/providers/credentials'; 3 | import { compare } from 'bcrypt-ts'; 4 | import { getUser } from 'app/db'; 5 | import { authConfig } from 'app/auth.config'; 6 | 7 | export const { 8 | handlers: { GET, POST }, 9 | auth, 10 | signIn, 11 | signOut, 12 | } = NextAuth({ 13 | ...authConfig, 14 | providers: [ 15 | Credentials({ 16 | async authorize({ email, password }: any) { 17 | let user = await getUser(email); 18 | if (user.length === 0) return null; 19 | let passwordsMatch = await compare(password, user[0].password!); 20 | if (passwordsMatch) return user[0] as any; 21 | }, 22 | }), 23 | ], 24 | }); 25 | -------------------------------------------------------------------------------- /app/db.ts: -------------------------------------------------------------------------------- 1 | import { drizzle } from 'drizzle-orm/postgres-js'; 2 | import { pgTable, serial, varchar } from 'drizzle-orm/pg-core'; 3 | import { eq } from 'drizzle-orm'; 4 | import postgres from 'postgres'; 5 | import { genSaltSync, hashSync } from 'bcrypt-ts'; 6 | 7 | // Optionally, if not using email/pass login, you can 8 | // use the Drizzle adapter for Auth.js / NextAuth 9 | // https://authjs.dev/reference/adapter/drizzle 10 | let client = postgres(`${process.env.POSTGRES_URL!}?sslmode=require`); 11 | let db = drizzle(client); 12 | 13 | export async function getUser(email: string) { 14 | const users = await ensureTableExists(); 15 | return await db.select().from(users).where(eq(users.email, email)); 16 | } 17 | 18 | export async function createUser(email: string, password: string) { 19 | const users = await ensureTableExists(); 20 | let salt = genSaltSync(10); 21 | let hash = hashSync(password, salt); 22 | 23 | return await db.insert(users).values({ email, password: hash }); 24 | } 25 | 26 | async function ensureTableExists() { 27 | const result = await client` 28 | SELECT EXISTS ( 29 | SELECT FROM information_schema.tables 30 | WHERE table_schema = 'public' 31 | AND table_name = 'User' 32 | );`; 33 | 34 | if (!result[0].exists) { 35 | await client` 36 | CREATE TABLE "User" ( 37 | id SERIAL PRIMARY KEY, 38 | email VARCHAR(64), 39 | password VARCHAR(64) 40 | );`; 41 | } 42 | 43 | const table = pgTable('User', { 44 | id: serial('id').primaryKey(), 45 | email: varchar('email', { length: 64 }), 46 | password: varchar('password', { length: 64 }), 47 | }); 48 | 49 | return table; 50 | } 51 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/nextjs-postgres-auth-starter/fde8ecf1da9337223081f70cf88b420060039d6e/app/favicon.ico -------------------------------------------------------------------------------- /app/form.tsx: -------------------------------------------------------------------------------- 1 | export function Form({ 2 | action, 3 | children, 4 | }: { 5 | action: any; 6 | children: React.ReactNode; 7 | }) { 8 | return ( 9 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import './globals.css'; 2 | 3 | import { GeistSans } from 'geist/font/sans'; 4 | 5 | let title = 'Next.js + Postgres Auth Starter'; 6 | let description = 7 | 'This is a Next.js starter kit that uses NextAuth.js for simple email + password login and a Postgres database to persist the data.'; 8 | 9 | export const metadata = { 10 | title, 11 | description, 12 | twitter: { 13 | card: 'summary_large_image', 14 | title, 15 | description, 16 | }, 17 | metadataBase: new URL('https://nextjs-postgres-auth.vercel.app'), 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: { 23 | children: React.ReactNode; 24 | }) { 25 | return ( 26 | 27 | {children} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /app/login/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import { Form } from 'app/form'; 3 | import { signIn } from 'app/auth'; 4 | import { SubmitButton } from 'app/submit-button'; 5 | 6 | export default function Login() { 7 | return ( 8 |13 | Use your email and password to sign in 14 |
15 |26 | This is a{' '} 27 | 33 | Next.js 34 | {' '} 35 | starter kit that uses{' '} 36 | 42 | NextAuth.js 43 | {' '} 44 | for simple email + password login and a{' '} 45 | 51 | Postgres 52 | {' '} 53 | database to persist the data. 54 |
55 |·
64 | 70 | Deploy to Vercel 71 | 72 |28 | Create an account with your email and password 29 |
30 |