-
use bun -
bun create next-app@latest- select
No, customize settings- TypeScript, ESLint, React Compiler, Tailwind CSS,src/**/*, App Router ✅ - import alias ❌ - > default -
@/* bun devtest -http://localhost:3000git log --all --graph
- select
-
clean up code
- remove files from
public/* - clear globals.css exclude @tailwind, README.md with title, page.tsx -
Hello next!with tw height/font/color -text-3xl font-bold underline text-teal-500 - lookup delete .next if not working
- test
- remove files from
-
install shadcn
https://ui.shadcn.com/docs/installation/next- base color -
Zinc - install components -
bunx --bun shadcn@latest add button- button, label, input, sonner or install all with--all - test - add button with
Click me!
- base color -
-
create remote repo - GitHub - check orgs - default me
git push
- prettier -
https://github.com/tailwindlabs/prettier-plugin-tailwindcssnpm install -D prettier prettier-plugin-tailwindcss- add to
.prettierrcconfig file in plugins - it must be add as last plugin after all plugins in prettier 👲 - add
.prettierignorewith node_modules, dist, build, .git, .gitignore, .next if nextjs and some other to ignore by prettier
-
install better-auth -
https://www.better-auth.com/docs/installation-
create
.env- set up env vars - also add NEXT_PUBLIC_API_URL - to access API on client comps - prefix with NEXT_PUBLIC to use in client comp -.env.example-!.env.examplein.gitignore -
create
lib/auth.ts -
setup
postgresdatabase withneon.techor docker-compose.yml -docker compose up -d -
install
prisma-bun add prisma --save-dev- IMP:bun --bun run prisma [command] -
init prisma
npx prisma initorbunx --bun prisma init- cleanup - default output = "../src/generated/prisma" - then addDATABASE_URLin .env -prisma db pullto introspect db and generate auto schemas models if any found in db -
define schemas in schema.prisma and run
prisma migrate devto apply schema -
create Post Model - for test - atleast push one model in db
-
push database changes
bunx --bun prisma db pushornpx prisma db push- check db to see applied schema/tables/columns - it only only apply changes of schema to db -"prisma:studio": "bunx --bun prisma studio" -
as if db push cmd not generate auto generate folder - run manually
bunx --bun prisma generateto generate prisma client - add/src/generated✅ or/src/generated/prismato.gitignore -
adjust scripts in
pkg.json- run prisma generate before dev and build cmd"dev": "bunx --bun prisma generate && next dev",- keep dev server sync with prisma schema as long as schema don't change"build": "bunx --bun prisma generate && next build",
-
create single prisma client in
lib/prisma.ts- Comprehensive Guide to Using Prisma ORM with Next.js - the file name can belib/db.tsalso to make agnostic but for now use primsa.ts which export generated instance to whole project -
add adapter to connect prisma instance to pg -
bun add @prisma/adapter-pgalso addbun add @prisma/clientbefore running better-auth cli -
setup prisma adapter with better-auth - this db setup done first so that better-auth cli can gererate models according to setup used
-
generate auth tables
npx @better-auth/cli generate- add --output to not affect original schema.prisma file after generating we can copy it into main schema.prisma and deleted output specified file -bunx @better-auth/cli generate --output=auth.schema.prisma -
make tweaks to
schema.prisma- put createdAt & updatedAt at top under id in models, seperate relations, add @unique infront of columns instead of @@unique([columnName]) at last and change model names @@map to plural names 💎 -
quick walkthrough the models:
UserSessionAccountVerificationPost- attach to user 1:n relation - its custom table not come with better-auth
-
push db changes
bunx --bun prisma db push -
configure the authentication methods to use - built-in support for email/password, social sign-on providers
-
create Mount Handlers in
app/api/auth/[...all]/routes.ts -
adjust
eslint.config.mjsto ignoresrc/generated/**/*- put it in globalIgnores(["src/generated/**/*"]) -
create client instance in
lib/auth-client.ts- use NEXT_PUBLIC_API_URL - this instance is use to interact in react to handle all auth function - either useauthClientthen access functionality with dot or export directly function instances from this file to export specific ->export const {} = authClient;- add what to export in {}
-
-
Enable Email & Password Authentication - already done ✅
- Create Sign Up Page PT1
- Create Form
components/register-form.tsx- put it inapp/auth/register/page.tsx - Log Form Values - test
- Create Form
- Setup Sonner - put Toaster component in layout.tsx from
@/components/ui/sonner.tsx - Create Sign Up Page PT2
- Add Form Validation
- minPasswordLength: 6, in auth.ts // by default, better-auth - use 8 as minPasswordLength
- Destructure SignUp Function
- Showcase
onError
- Add Form Validation
- OPTIONS - minPasswordLength - already done ✅
- Create Sign Up PT3
- Sign Up default automatically signs in the user - better-auth signin auto since email verification is off, so when signup - session created and cookie stored on account register - cookie max age - default
7d
- Sign Up default automatically signs in the user - better-auth signin auto since email verification is off, so when signup - session created and cookie stored on account register - cookie max age - default
- Show Session on Profile Page
- Show Data in Neon Dashboard
- Sign Out User
- Destructure SignOut Function
- Show Removed Cookies
- Create Sign In Page PT1
- Create Form
components/login-form.tsx - Log Form Values - test
- Destructure SignIn Function
- Create Form
- Show Unauthorized on Profile Page
- Create Sign In Page PT2
- Showcase
onError - Sign In User
- Showcase
- Create Sign Up Page PT1
-
IMP Auth done
=================================================
- For user convinences or nice to look at 👇 - to change the way generate ID of table and hash password before store in db
return-button.tsx💎 - add it to all pages needed, auth-pages, profile- Showcase
onRequestandonResponse- pending state, disable submit btn,onSuccess- redirect using router to/profilefor both signIn and signUp as better-auth signIn auto by default - show toast.success onSuccess then redirect - Add Convienence Links for Auth Pages - don't have an acc / already have an acc
- Showcase Full Cycle Again - test
- OPTIONS - autoSignIn - disable as it is enabled by default
- showcase - test
- OPTIONS - advanced.database.generateId -
https://www.better-auth.com/docs/concepts/database#id-generation- to use uuid - disable better-auth option which uses default - Web crypto.getRandomValues()- Table IDs (change
schema.prismaand push) - stop dev - make changes to prisma models@default(uuid())- push to db and truncate db with cascade one"prisma:push": "bunx --bun prisma db push","prisma:generate": "bunx --bun prisma generate"
- Showcase - test
- Truncate Tables
- Table IDs (change
- OPTIONS - emailAndPassword.password - better-auth uses scrypt to hash passwords -
https://www.better-auth.com/docs/reference/security- to customized it to use own hashing version- Create User
- Argon2
bun add @node-rs/argon2- faster performance - cross platform support - small pkg size - Add to
next.config.ts- to tell server about external pkg -serverExternalPackages: ["@node-rs/argon2"], - Create Utilities
lib/argon2.ts- config accorgin to lucia-auth -https://v3.lucia-auth.com/tutorials/username-and-password/nextjs-app - Add to
lib/auth.ts - Showcase
- Truncate Tables
- Create User
=================
- SERVER ACTIONS - since client side auth is convinent and better approach - now try to integrate same with server side with server components - validate and auth at server - now make both signin and signup implement with server actions, whole manually and then less manually by using direct feature given by better-auth - nextCookies plugin --- this will be same for Express Application
- Sign Up User via SERVER ACTIONS
- Create Action
- Log Form Values
- Sign Up user on server
- Sign In User via SERVER ACTIONS PT1 HERE HERE HERE
- Create Action
- Log Form Values
- Sign In User on Server - catch - when wroking with server actions in nextjs, you have set manually set cookies. - work with cookies api to set cookies. without this you only get login but not set cookie in browser as not passed in res - so set cookie in res
- Showcase - No Cookies
- Manually Set Cookies
- Showcase - Cookies
- Get Additional Session Properties
- PLUGINS - nextCookies() 💎 do all manually setup auto
- use headers() from
next/headersalso to pass userAgent even if use plugin nextCookies()
=======================================================
- Get the session on Client
- Create Get Started Button
- Destructure useSession
- showcase
- OPTIONS - session.expiresIn
- change to 15 seconds
- showcase
- Change to 30 days
- Middleware -
proxy.tsinsrc-src/proxy.ts✅ to work - useauth.api.getSessionsince its server side - for now using proxy to only check session- check for existence of a session cookie
- showcase on auth routes
- THIS IS NOT SECURE! - This is the
recommended approachto optimistically redirect users - recommend handling auth checks in each page/route - config matcher from next.js in proxy.ts -
https://nextjs.org/docs/app/api-reference/file-conventions/proxy#negative-matching
- Error Handling - use better auth typing to access robust error info -
auth.$ERROR_CODES - Hooks - to do some task
beforeandafterendpoint or auth logic -https://www.better-auth.com/docs/concepts/hooks- Validate Email -
https://www.better-auth.com/docs/concepts/hooks#example-enforce-email-domain-restrictionvalidate email before signup logic hook - Transform Name
- Validate Email -
===================================================
- Roles (Custom Method)
- Prisma
- Add UserRole Enum - put them under models that use them or in a seperate location if use in multiple location
- Push changes
bunx --bun prisma db push
- User
- Show field is added because of
@default - Truncate Tables
- Create new User
- Show field is added because of
- Profile PT1
- Show role is not typed in
session.user- as you have to add it manually with better-auth even if added in prisma model
- Show role is not typed in
- OPTIONS - user.additionalFields
- showcase
inputoption - need to passinput: falseto make not cumpolsion to pass role when signup with better-auth
- showcase
- Profile PT2
- show role is now typed and added to
session.user
- show role is now typed and added to
- ISSUE: Client session has no Context of the role
- Cute circle on
get-started-button.tsx - InferAdditionalFields plugin on Client - as we don't have role on authClient since we need authClient instance to inference of additional field added to auth 💎
- Cute circle on
- Prisma
============
- Admin Panel
- Create Page / Link
- Manually change role
- Update middleware
- Guard
/admin/dashboard - List users with prisma query
- Delete user with prisma query
- Database hooks
============
- Roles (Admin Plugin) from better auth
https://www.better-auth.com/docs/plugins/admin- more like Authorization plugin - tells what are allowed to do- generate auth tables
bunx @better-auth/cli generate --output=roles.schema.prisma- this to run as we add admin plugin which add more things in schema tables to work with them - things added more -https://www.better-auth.com/docs/plugins/admin#schema - compare & contrast (look at Schema section)
- Push changes
bunx --bun prisma db push- as we add more fields to models - Create Permissions (No Posts) -
https://www.better-auth.com/docs/plugins/admin#create-roles-lib/permissions.ts - Add to
lib/auth.tsandlib/auth-client.ts- useac&rolesfrompermissions.tsand pass it to admin plugin - List Users with Admin Plugin - by using instead of prisma, use better-auth api
listUsers- its aadmin functionality - EXERCISE: Delete user with Admin Plugin - nothing wrong with using prisma query but for exercise we are using better-auth admin features to do some prisma query stuff
- Change Permissions (With Posts)
- generate auth tables
- Create Dropdown to change role for Admins
=======================================
- Oauth - Google & GitHub
-
Create Buttons
-
Google Oauth -
https://www.better-auth.com/docs/authentication/google- before this you have to setup oauth consent screen - where you have to give details of all contacts of project - this is where you can setup branding also (optional)
- create new project > credentials > create oauth client id
- authorized js origin -
http://localhost:3000- in prod will actual domain - authorized redirect url -
http://localhost:3000/api/auth/callback/googlefrom better-auth docs
- authorized js origin -
-
GitHub Oauth -
https://www.better-auth.com/docs/authentication/github- Homepage Url -
http://localhost:3000 - Authorization callback URL -
http://localhost:3000/api/auth/callback/github
- Homepage Url -
-
if register with with mail used in google/github account and then sign in with google/github, user image will be null, this lead to three account with same user - as better-auth by default enable
account linking -
Account Linking - make account linking false - now if try let say signIn with github then google then at google signin - it will redirect to error url
http://localhost:3000/auth/login/error?error=account_not_linked404 as same credentials of signin found as its a single user -
Error Handling - create page to handle the above redirect 404 page - customized it by adding one
src/app/auth/login/error/page.tsx/auth/login/error
-
======
- Discord Oauth
========================================
- Email Verification
-
Nodemailer
- Create Template
bun add nodemailer&bun add -D @types/nodemailerlib/nodemailer.ts-https://nodemailer.com/smtp/oauth2&https://nodemailer.com/smtp/customauth- create transporter func and use env
&where app password is used not gmail password, to get app password, first setup 2-step verification i.e. 2 factor authentication for that user email through which emails are going to send, then set app passwordhttps://myaccount.google.com/apppasswords- create app name and it shows password one time so copy it - done nodemailer setup - check all credentials
- create
actions/send-email.action.ts- to create a async server generic function to send emails
- Create Template
-
Verify Email
emailAndPassword.requireEmailVerification- make it true to enableemailVerification- configure auth to send email with defined customesend-email.action.ts💎- handle Error / Expired
/auth/verify- like when wrong token or expired token or any - it should show a page with Login Error and have form to verify email again 💎 - when invalid token - catches on home page - but you don't want to catch on home page - add custom url/auth/verify- now if error, it gets redirect tohttp://localhost:3000/auth/verify?error=invalid_tokenwhere you catch param and show according to error orhttp://localhost:3000/auth/verify?error=token_expired - destructure
sendVerificationEmailfrom better-auth to do it simply likeemailVerificationfrom better-auth
- handle login page not verified - by adding more verbose error message in signin action/form
-
Create Post signup page showcase
-
Forgot password - logic starts from login-form, setup auth
sendResetPasswordthen only exportrequestPasswordResetfrom auth-client- Page / Form / Success
-
Reset password
- Page / Form / Success
- showcase
-
===========================================================
- Show the image of user
- Update User - this exported from auth-client -
updateUser- use to update user values 💎- change name / image
- update hook
- updating password
- Custom Sessions - customizing session obj we get to only allow specific fields to get when access session - to do use plugin name
customSession- type inference for plugins workaround - as we need to defined options type as role from admin plugin is not getting infered by customSession plugin. to work it we need to defined by lifting it up and then spread all accross where it needed - admin and customeSession plugins, to defined we use type from better-auth
type BetterAuthOptionsfrom better-auth- create obj satisfies
BetterAuthOptionsthen spread it to better-auth auth instance - then add plugin and spread options.plugins and then addcustomSessionplugin with passing second option as options defined asBetterAuthOptions - now we have to make auth-client instance also to infer typing for customeSession - use
customSessionClientplugin frombetter-auth/client/pluginsand typeauthto infer - now can add or remove in typing of session to access only specific part of session
- create obj satisfies
- type inference for plugins workaround - as we need to defined options type as role from admin plugin is not getting infered by customSession plugin. to work it we need to defined by lifting it up and then spread all accross where it needed - admin and customeSession plugins, to defined we use type from better-auth
- PLUGINS - Magic Link - signin without password - it send email when signin with email - click email btn- you will be authenticated💎 -
https://www.better-auth.com/docs/plugins/magic-link- add to client instance - this will add it to
signIninstance come from better auth client instance - create UI
- adjust hooks - before hook we put is only on
/sign-up/emailso it only work on signup - we want to work normalizeName function on/sign-in/magic-linkand also when update user/update-user
- add to client instance - this will add it to
- Cookie cache -
https://www.better-auth.com/docs/guides/optimizing-for-performance
===============================================================
- rate limit
- basic ratelimit by better-auth -
https://www.better-auth.com/docs/reference/options#ratelimit-rateLimitoption in auth instance steup - no bot protection - storage default
memory, but nextjs is serverless so we need to usedatabasewhich generate a model schema by migrating with better-auth cli -bunx @better-auth/cli generate --output=rateLimit.schema.prisma- then copy rateLimit with config by you and put inschema.prismafile and push to db - but there is better way - Arcjet - Setup ArcJet - create team, free tier upto 2 teammates - create new site - name
Better-auth- choosenextjs-https://docs.arcjet.com/get-started?f=next-js- features can include
- Bot detection. Rate limiting. Email validation. Attack protection. Data redaction. - any feature can be add on any http method - go through docs
bun add @arcjet/next @arcjet/inspect- here inspect for checking snooping stuff - all need to configurebun add @arcjet/ipfor ip related stuff
- modify
app/api/auth/[...all]/routes.ts- GET and POST handles to include arcjet first to modify response and early return according to request obj💎- create instance of arcjet which include api key
ARCJET_KEY, on which basis to check - charasteristicsuserIdOrIp- check userId or Ip of user, rules -> shield mode "LIVE" so to run in without reporting in dev mode - define botSettings, restrictiveRateLimitSettings , laxRateLimitSettings , emailSettings so to use this settings in different scenerios
- create instance of arcjet which include api key
- create
checkArcjetasync function to check passedrequestobject get in POST handler - return decision object with rules appled on it are validated and protect request obj and throw error into decision obj which can be check in POST handler and return response according to it.- add routes to check like
/auth/sign-upwhere get POST request
- add routes to check like
- 👲 as better-auth also read the body of request obj at the same time and you can't have two things reading same req object - so clone req obj for better-auth to read after arcjet used - so clone req obj for arcjet with request.clone() method 🚨🚨 and passed to POST handler to be used by better-auth to read
- more to write here TODO: complete this, also there is a log of bug at the time of signup - check for it
- features can include
- basic ratelimit by better-auth -
=====
- 1. Scafold a new project with shadcn create new project - which add guard rails to which design system to use for ai
- select custom style if want and run next.js cli command given after selecting component lib, style, base color, theme, icon lib, font, radius, menu color, menu accent, etc. scafolds nextjs, tailwindcss and shadcn.
- 2. add md files for specific models -> gives a long term memory <- reads at the start of every session
- claude - CLAUDE.md - to generate by claude itself, run
initcmd in its interface, lookk for project scafolded earlier and generate md file - put it in next app - put only textagents.mdin it - important to add
- Do, Don't, Tech Stack, Commands/Scripts, Boundaries, Project Structure, Key/Imp Files, Code Examples, PR Guidelines, PR Checklist, When Stuck what to do, Extended Documentation referencing to .agents/.claude folder
- gemini & gpt - AGENTS.md - so all config in this file only.
- claude - CLAUDE.md - to generate by claude itself, run
- 3. add skills for framework knowledge
- skills are - reusableknowledge packs - install with simple command - get skills from
https://skills.sh/ - to add skills to project - select which skills to add -
vercel-react-best-practices- it gives cmdnpx skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-best-practices- run in terminal- select antigravity and claude code and all other imp are selected by default
- select Project to install to specificly for project
- select symlink - to install all models agents file in single source or truth - then proceed for installation
- creates folders of
.agents/skillsand for claude as we selected before - same config but in diff folder.claude/skills
- creates folders of
vercel-composition-patternsnext-best-practices
- to update skills -
npx skills update
- skills are - reusableknowledge packs - install with simple command - get skills from
- 4. Plan before you execute - make sure agent have everything needed to build in plan mode - ex in PROMPT.md for plan mode
- add docs reference
ask me questions before start buildingsentence in prompt.
- 5. Checkpoint with Git
- commit after every successful step
- if something breaks -
git restoreto restore to previous commited code
==================================
- session management
-
parse userAgent data from session with better approach - using lib
ua-parser-js💎 -bun add ua-parser-js -
even after revoke session that is open in other tab or device, it will still be logged in for
1mas session is cached by default 💎
-
==================================
- Delete User Account
- to enable the deletion of account, you have to set enable this feature in auth instance.
- user.deleteUser.enabled - to true and send email of deletion initiation to actually delete user by user itself
sendDeleteAccountVerificationin user.deleteUser field which takes async function like other to send email.
- user.deleteUser.enabled - to true and send email of deletion initiation to actually delete user by user itself
- to enable the deletion of account, you have to set enable this feature in auth instance.
==================================
- Two Factor Auth -
https://www.better-auth.com/docs/plugins/2fa-
use better-auth plugin
twoFactor()and also give name to app inappNameas it will be use as an issuer -
run generate cli cmd from better auth to get schema for it to add it to original
schema.prismafile which then push to db as changes to modelsbunx @better-auth/cli generate --output=twoFA.schema.prisma<- this must run after adding plugin to see changes in schema - add one field in usertwoFactorEnabledand a new model -bunx --bun prisma db push -
IMP 🚨🚨 to put
twoFactorEnabled: user.twoFactorEnabled,in user in customSession as to get typing, i need to debug just to find out that i didn't enable this. -
bun add zod -
create form with react-hook-form with zod resolver as validation with zod
-
create schema with zod obj for form
twoFactorAuthSchema- then infer type namedTwoFactorAuthFormfromtwoFactorAuthSchemawithz.infer<> -
bun install react-hook-form&bun i @hookform/resolvers -
bunx --bun shadcn@latest add field -
bunx --bun shadcn@latest add @wds/loading-swap -
bun i react-qr-code -
bunx --bun shadcn@latest add tabs -
⚠️ If you too long time to enter 2fa code after login to app in 2fa page, it will give error as their is set timeout to enter code, so if you get error then again login and enter authentication code as early as possible -
as backup codes are one-time use only so if use one of given then it will not be use again when login and 2fa with backupcode tab
-
2fa authentication tab is use with codes from mobile authenticator app
-
-
=====================================
- Passkey -
https://www.better-auth.com/docs/plugins/passkey-
add passkey plugins to both auth and auth-client instance -
bun add @better-auth/passkey -
bunx @better-auth/cli generate --output=passkey.schema.prisma- modify tables -bunx --bun prisma db push- then generate -
bunx --bun shadcn@latest add dialog -
TODO: detail it
-
=====================================
- Organization - better-auth organization plugin
-
add organization plugin
https://www.better-auth.com/docs/plugins/organization- comes withbetter-auth/pluginsitself unlike passkey - add to instances likeorganization()inauth- then migrate with better-auth cli to generate schema changes acc to organization pluginbunx @better-auth/cli generate --output=organization.schema.prisma- then make changes acc to this to schema.prisma with custom structure by arranging order correctly. Don't forgot look for each models changes like session Model also. Last push to db and generate with prismabunx --bun prisma db push&bun run prisma:generate. -
create organization
-
create invitations
-
list organizations
-
sending invite emails
-
invites page to accept or reject invitation
-
organization select
-
members tab - listing members of organization
-
================
-
React-Hook-FormwithZodFor Validation-
installation
bun i react-hook-form- actual libbun i zod- validation lib - create i/p schemas for form or fetched data types, etc.bun i @hookform/resolvers- helper to integrate validation lib like zod -zodResolverfrom@hookform/resolvers/zod- optional - add devtool from react-hook-form for form creation & debug -
bun i -D @hookform/devtools-https://www.react-hook-form.com/dev-tools/
-
create schema with zod - named on what going to do - like for CreateOrganizationButton - its a form ->
createOrganizationSchema- since for form so it would be an object z.object({}) - add robust/simple error to show if invalidation trigger of input 🏭 -
infer type for to use in form validation by react-hook-form with useForm - create type for form named based on which form -
type CreateOrganizationForm- "use client" -
create form instance with useForm passing type of Form - it must be done inside component where form is going to defined, pass zodResolver from @hookform/resolvers/zod to resolver key and zodResolver use schema we defined for that form and also pass defaultValues value as initial/ui/default value - this result a form object which has key formState to get state related attributes like isSubmitting bool to pass to loader in form
-
add form related functions inside component, make async if return Promise to await inside logic - named starts with
handlelikehandleCreateOrganization- paramdata: CreateOrganizationFormobj with all field values attach to name of field of form -
use shadcn field components to create form
-
add onSubmit with value
form.handleSubmit(handleCreateOrganization)here function pass which will trigger when submit form -
to create field that controlled by react-hook-form -
<Controller />from react-hook-form which render field with function with params {field, fieldState} from which spread field to Input and use fieldState to show error by zod, controller name should match schema key names to track -
add
data-invalid&aria-invalid
-
=================
- shadcn components
- installation -
https://ui.shadcn.com/docs/installation - install components that are most to use
- most use
- button
- input
- field - for forms
- label - when input is use
- sonner - for toast
- dialog - for modal
- textarea
- checkbox
- most complex and used if needed
- card
- sidebar
- select
- accordion 😓
- table
- great ux
- switch
- tabs
- drawer
- spinner - for loading state
- not so much
- badge
- separator
- Kbd - for showing keybindings
- avatar
- most use
- installation -