Skip to content

leamsigc/nuxt-better-auth-drizzle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

How to use?

  1. Click on the "Use this template" button
  2. click on the "Create repository from template" button.
  3. Create your own repo
  4. Clone the project locally
  5. Crate the .env file base on the .env-example cp .env-example .env
  6. Run pnpm i
  7. Run pnpm db:generate
  8. Run pnpm db:migrate
  9. Run pnpm dev
  10. To view the db studio run pnpm db:studio

Nuxt and Better Auth integration example | Step by step tutorial | Basic Setup

  1. User registration | email and password
  2. User login | email and password
  3. User logout

Protected routes /app/

Unprotected routes /login /register /

  • Create new Nuxt 4 project
  • Install Basic modules for the project
    • Tailwind css
    • SEO Module
    • Vue Use
    • Drizzle ORM
  1. Install Drizzle ORM
pnpm add drizzle-orm @libsql/client dotenv
pnpm add -D drizzle-kit tsx
  1. Create .env

  2. Create config Drizzle

  3. Create schema

    • Nuxt Image
    • Nuxt font
    • Nuxt scripts
    • Nuxt content
    • Formkit
  4. Create the config file for the FormKit

  5. Update the nuxt.config file

  6. Install the theme for the FormKit styles npx formkit theme --theme=regenesis

  7. Include the FormKit theme config to the tailwindcss configuration

  8. There is a issue with Formkit and Nuxt when setting the autimport:true in the Formkit config to FormKitSchema have an issue with the data banding.

Optional Modules:

  • tailwind-variants
  • typographty → Tailwind css
  • @unlighthouse/nuxt

npx nuxi@latest module add tailwind seo vueuse image font scripts content formkit

  • Install the Better auth
  • Connect better auth
  • Create username and password example

Base on the documentation of Better Auth Follow the next steps

  1. Install the Better auth package pnpm add better-auth
  2. Generate the env BETTER_AUTH_SECRET
  3. Create instance of the auth in lib/auth.ts
  4. Connect to Drizzle orm
  5. Create the tables and migrate npx @better-auth/cli generate & npx @better-auth/cli migrate
Notes:

It seems like the import path for the drizzle config need to be relative and not the auto import from Nuxt ~~

The better auth CLI migrate is not compatible with Drizzle ORM Manual generate and migrate command from drizzle-kit need to be trigger.

The schema generated from Better auth you can move the content from that schema generated to the Drizzle schema

export const user = sqliteTable("user", {
	id: text("id").primaryKey(),
	name: text('name').notNull(),
	firstName: text('firstName').notNull(),
	lastName: text('lastName').notNull(),
	email: text('email').notNull().unique(),
	emailVerified: integer('emailVerified', {
		mode: "boolean"
	}).notNull(),
	image: text('image'),
	createdAt: integer('createdAt', {
		mode: "timestamp"
	}).notNull(),
	updatedAt: integer('updatedAt', {
		mode: "timestamp"
	}).notNull()
});

export const session = sqliteTable("session", {
	id: text("id").primaryKey(),
	expiresAt: integer('expiresAt', {
		mode: "timestamp"
	}).notNull(),
	ipAddress: text('ipAddress'),
	userAgent: text('userAgent'),
	userId: text('userId').notNull().references(() => user.id)
});

export const account = sqliteTable("account", {
	id: text("id").primaryKey(),
	accountId: text('accountId').notNull(),
	providerId: text('providerId').notNull(),
	userId: text('userId').notNull().references(() => user.id),
	accessToken: text('accessToken'),
	refreshToken: text('refreshToken'),
	idToken: text('idToken'),
	expiresAt: integer('expiresAt', {
		mode: "timestamp"
	}),
	password: text('password')
});

export const verification = sqliteTable("verification", {
	id: text("id").primaryKey(),
	identifier: text('identifier').notNull(),
	value: text('value').notNull(),
	expiresAt: integer('expiresAt', {
		mode: "timestamp"
	}).notNull()
});

Note:

The firstName and lastName are fields that I added manually.

Steps After installation

  1. Create auth route on the server folder /api/[...auth].ts
import { auth } from "~~/lib/auth";

  

export default defineEventHandler(async (event) => {

return auth.handler(toWebRequest(event));

});
  1. Create a <root Project Directory>/lib create the following files:

auth.ts

import { betterAuth, type User } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
//You can import the schema from where the drizzle schema is pointing
import * as schema from "../db/schema";

//You probabaly call this db or any other name to the drizzle connection
import { useDrizzle } from "../server/utils/drizzle";


export const auth = betterAuth({
    database: drizzleAdapter(useDrizzle(), {
        provider: "sqlite",
        schema: {
            ...schema
        }
    }),
    user: {
    //Add the extra fields that you have in the user table I created the fistName and last name sot need to add them here
        additionalFields: {
            firstName: {
                type: "string",
                fieldName: "firstName",
                returned: true,
                input: true,
                required: true,
            },
            lastName: {
                type: "string",
                fieldName: "lastName",
                returned: true,
                input: true,
                required: true,
            }
        }
    },
    emailAndPassword: {
        enabled: true,
        async sendResetPassword(url, user) {
            console.log("Reset password url:", url);
        },
    },
})

auth-client.ts

import { inferAdditionalFields } from "better-auth/client/plugins";
import { createAuthClient } from "better-auth/vue";
import { auth } from "./auth"
export const authClient = createAuthClient({
	plugins: [inferAdditionalFields<typeof auth>()],
});

export const {
	signIn,
	signOut,
	signUp,
	useSession,
	forgetPassword,
	resetPassword,
} = authClient;

The useDrizzle is coming from

<root Directory>/server/utils/drizzle.ts

import { createClient } from "@libsql/client";
import { drizzle } from "drizzle-orm/libsql";
//If you have a config file if not this is not necessary
import { tursoConfig } from "../../config/turso.config";
//Point to the schema file from Drizzle 
import * as schema from "../../db/schema";

const tursoClient = createClient({
    url: tursoConfig.url,
    authToken: tursoConfig.authToken
})

//Instance of Drizzle to be use anywhere else
export const useDrizzle = () => {
    return drizzle(tursoClient)
}


export const tables = schema;



export const UserInsert = schema.user.$inferInsert;
export type UserRegisterType = Omit<typeof UserInsert, "createdAt" | "updatedAt" | "id" | "emailVerified">;

Fork the project

shadcn-vue-landing-page to Nuxt for the home page

Home Page

Home page

Login Page

Login Page

Register Page

Register Page

Dashboard

Dashboard

TODO

  • When a new user registered, send a verification email to the user
  • If the user is not verified show a verification screen
await authClient.signIn.emailAndPassword({
    email: "[email protected]",
    password: "password"
}, {
    onError: (ctx) => {
        // Handle the error
        if(ctx.error.status === 403) {
            // Display a verification overlay
        }
    }
});
  • Enforce email verification
export const auth = betterAuth({
    emailAndPassword: {
        requireEmailVerification: true
    }
})

About

Nuxt 4 Integration with Better auth and Drizzle ORM

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages