Database hooks lifecycle
Database hooks allow custom logic during lifecycle of core database operations in Better Auth. You can create hooks for user, session, and account models. Two types of hooks exist: before (called before create, update, or delete; can abort operation by returning false or replace payload by returning data object) and after (called after create or update for additional actions).
Database hooks before hook behavior
A before hook is called before an entity is created, updated, or deleted. If it returns false, the operation aborts. If it returns a data object, it replaces the original payload. The hook receives the entity and a context object.
Database hooks after hook behavior
An after hook is called after an entity is created or updated. You can perform additional actions or modifications after the entity has been successfully created or updated.
Example: Database hooks for user creation
```typescript
import { betterAuth } from "better-auth";
export const auth = betterAuth({
databaseHooks: {
user: {
create: {
before: async (user, ctx) => {
return {
data: {
...user,
firstName: user.name.split(" ")[0],
lastName: user.name.split(" ")[1],
},
};
},
after: async (user) => {
// Create stripe customer or other actions
},
},
},
},
});
```
Example: Database hooks for preventing user deletion
```typescript
import { betterAuth } from "better-auth";
export const auth = betterAuth({
databaseHooks: {
user: {
delete: {
before: async (user, ctx) => {
if (user.email.includes("admin")) {
return false; // Abort deletion
}
return true; // Allow deletion
},
after: async (user) => {
console.log(`User ${user.email} has been deleted`);
},
},
},
},
});
```
Database hooks error handling with APIError
To stop a database hook from proceeding, throw errors using the APIError class imported from 'better-auth/api'. Example: throw new APIError('BAD_REQUEST', { message: 'User must agree to the TOS before signing up.' })
Database hooks context object
The context object (ctx) passed as the second argument to a hook contains useful information. For update hooks, it includes the current session, which you can use to access the logged-in user's details via ctx.context.session.userId.
Access additional OAuth data in hooks with getOAuthState
Additional data passed through the OAuth flow is available in hooks during the OAuth callback through the getOAuthState function. This usually works for /callback/:id paths and the generic OAuth plugin callback path (/oauth2/callback/:providerId).
getOAuthState in after hook example
Example using getOAuthState in an after hook to access additional data:
```ts
import { betterAuth } from "better-auth";
import { getOAuthState } from "better-auth/api";
export const auth = betterAuth({
hooks: {
after: [
{
matcher: () => true,
handler: async (ctx) => {
if (ctx.path === "/callback/:id") {
const additionalData = await getOAuthState<{
referralCode?: string;
source?: string;
}>();
if (additionalData) {
// IMPORTANT: Validate and sanitize the data before using it
// This data comes from the client and should not be trusted
if (additionalData.referralCode) {
const isValidFormat = /^[A-Z0-9]{6}$/.test(additionalData.referralCode);
if (isValidFormat) {
const referral = await db.referrals.findByCode(additionalData.referralCode);
if (referral) {
await db.referrals.incrementUsage(referral.id);
}
}
}
if (additionalData.source) {
await analytics.track("oauth_signin", {
source: additionalData.source,
userId: ctx.context.session?.user.id,
});
}
}
}
},
},
],
},
});
```
getOAuthState in database hook example
Example using getOAuthState in a database hook:
```ts
databaseHooks: {
user: {
create: {
before: async (user, ctx) => {
if (ctx.path === "/callback/:id") {
const additionalData = await getOAuthState<{ referredFrom?: string }>();
if (additionalData?.referredFrom) {
return {
data: {
referredFrom: additionalData.referredFrom,
},
};
}
}
},
},
},
}
```
Token encryption with databaseHooks
Better Auth does not encrypt tokens by default. To encrypt tokens like accessToken or refreshToken, use databaseHooks with the account.create.before hook to encrypt before saving to the database, and decrypt when retrieving.
Token encryption example with databaseHooks
Example of encrypting tokens in databaseHooks: In the account.create.before hook, check if account.accessToken or account.refreshToken exist, encrypt them using your encrypt function, and return the modified account in the data property of the returned object.