yarn create next-app
- We want to use typescript - typescript > javascript.
- ESLINT - consistent linting for all of us.
- Tailwind CSS - easier atomic styling.
- Don’t use
src
dir - we’ll use the app dir. - Let’s use the app router.
- No need to customise the default import alias. (I chose yes by accident)
Let’s add prettier as a dev dependency.
Run this commands in the project’s directory. In my case I need to cd nextjs-starter-activity
yarn add -D prettier@2.8.8 eslint-config-prettier prettier-plugin-tailwindcss@3.3.2
Modify the .eslintrc.json
so it contains this instead.
{
"extends": ["next/core-web-vitals", "prettier"]
}
Now create .prettierrc.json
with following content.
{
"trailingComma": "es5",
"semi": true,
"tabWidth": 2,
"singleQuote": true,
"jsxSingleQuote": true,
"plugins": ["prettier-plugin-tailwindcss"]
}
Now to package.json
also add this snippet in scripts. This allows us to
run yarn format
and format the project.
"scripts": {
...
"format": "prettier --check --ignore-path .gitignore .",
"format:fix": "prettier --write --ignore-path .gitignore ."
}
Install Prettier - Code formatter
extension.
Now setup for automatic code formatting.
Add this to your settings.json…?
// Set the default
"editor.formatOnSave": true,
You will also want to install Prisma
for .prisma
files.
Follow the guide on prettier.nvim.
Follow the guide on vim-prisma
or You can also install prisma through Mason
and then list prismals
as your language server 😉.
Add prisma as a dev dependency.
yarn add -D prisma
Initialise prisma.
npx prisma init --datasource-provider sqlite
Add these models to prisma/schema.prisma
. We will also modify the
database url from env(DATABASE_URL)
to file:./dev.db
.
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
Now to sync your changes to models you made in the file to the database, you’ll need to run a migration.
npx prisma migrate dev --name init
You can check if your models have been created through the GUI
npx prisma studio
We will now install prisma client to access our database from Next.js
yarn add @prisma/client
By default a new prisma client is created on every migration but we can manually generate it to keep up to date.
npx prisma generate
We want only a single instance of PrismaClient
that you can import to any file where its needed. Let’s create a file lib/prisma.ts
.
mkdir lib && touch lib/prisma.ts
import { PrismaClient } from '@prisma/client';
let prisma: PrismaClient;
if (process.env.NODE_ENV === 'production') {
prisma = new PrismaClient();
} else {
if (!global.prisma) {
global.prisma = new PrismaClient();
}
prisma = global.prisma;
}
export default prisma;
We have only one prisma instance if running locally, but many instances when running in production.
Now you can import it in your files using
import prisma from '@/lib/prisma';
Lets now install and integrate NextAuth. Docs are using nextjs 12 so you’ll need to follow below.
yarn add next-auth @auth/prisma-adapter
We need to create to variables in your .env
file - NEXTAUTH_SECRET
Create it using openssl rand -base64 32
. It’ll be used for encoding. -
NEXTAUTH_URL
So your .env
file should have
NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_SECRET="LFSdf9HOftNtxyhJseKqVQcuFQNzErF+ReIl8+exFjw="
I’ll walk you through this in-person.
- https://console.cloud.google.com
- APIs and Services -> Credentials
- Create Project (create project)
- OAuth consent screen (external users) -> add yourself as a test user.
Now create OAuth Client ID
Create Credentials -> OAuth client ID -> Web application
Then
Add URI to Authorised JavaScript origins - https://localhost:3000
Add http://localhost:3000/api/auth/callback/google
as one of the
Authorised redirect URI’s.
Then copy your Client ID and Client secret into the .env
file.
GOOGLE_CLIENT_ID="..." GOOGLE_CLIENT_SECRET=".."
We need to add this adapter to app/api/auth/[...nextauth]/route.ts
import { PrismaAdapter } from '@auth/prisma-adapter';
import { PrismaClient } from '@prisma/client';
import NextAuth, { AuthOptions } from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
const prisma = new PrismaClient();
export const authOptions: AuthOptions = {
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
};
export const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Let’s update our Prisma schema for NextAuth
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
Now we run the command
npx prisma migrate dev --name nextauth-models
npx prisma generate
Let’s test it by visiting the automatically generated endpoint at http://localhost:3000/api/auth/signin.
After logging in. Lets have a look at the updated database using
npx prisma studio
and visiting http://localhost:5555.
We will now wrap our application in a session provider so we know our session details in all pages.
Create a component called SessionProvider.tsx app/components/SessionProvider.tsx
.
'use client';
import { SessionProvider } from 'next-auth/react';
export default SessionProvider;
This just takes the SessionProvider provided by next-auth by makes it a client component.
Now we are going to put that into our layout.tsx
.
Make sure to also copy the components/Navbar.tsx
so that the import works.
import './globals.css';
import type { Metadata } from 'next';
import { getServerSession } from 'next-auth';
import SessionProvider from '@/app/components/SessionProvider';
import { Inter } from 'next/font/google';
import NavMenu from './components/Navbar';
import { authOptions } from './api/auth/[...nextauth]/route';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
};
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await getServerSession(authOptions);
return (
<html lang='en'>
<body className={inter.className}>
<SessionProvider session={session}>
<main className='flex min-h-screen flex-col items-center justify-between p-24'>
<NavMenu />
{children}
</main>
</SessionProvider>
</body>
</html>
);
}
- Role based authentication - https://www.youtube.com/watch?v=urZ0iMugiiI If we need easy role based actions, for example: user, moderator, admin. (edflix flasback 😵💫)