NestJS Better Auth Module
The `nestjs-better-auth` module (version `0.6.4`) integrates the `better-auth` authentication and authorization library into NestJS applications. It provides authentication guards for route protection and decorators for convenient access to authenticated user sessions. This module supports both Express v5 and Fastify HTTP adapters, along with comprehensive GraphQL context capabilities. While it currently maintains CommonJS compatibility, its README indicates a future transition to ESM. This specific package, maintained by `underfisk`, appears to have a slower release cadence compared to a related, more actively developed package (`@thallesp/nestjs-better-auth`) which is at version `2.x.x`. Users should be aware of this distinction when choosing their authentication solution, as `nestjs-better-auth@0.6.4` has not seen recent updates and features like 'Hooks' are still marked 'WIP'.
Common errors
-
Error: Nest can't find the BetterAuthModule provider (when processing BetterAuthModule).
cause The `BetterAuthModule` has not been correctly imported into an `AppModule` or a feature module, or `forRoot()`/`forRootAsync()` was not called.fixEnsure `BetterAuthModule.forRoot({...})` or `BetterAuthModule.forRootAsync({...})` is present in the `imports` array of your `AppModule` or relevant feature module. -
Cannot read properties of undefined (reading 'user')` or `TypeError: Cannot read properties of undefined (reading 'session')` when using `@CurrentUserSession()`
cause The `@CurrentUserSession` decorator was used on a route that was not authenticated, or the authentication process failed, resulting in no active session being available.fixEnsure the route is protected by `BetterAuthGuard` and the user is successfully authenticated before attempting to access session data via `@CurrentUserSession`. -
Error [ERR_REQUIRE_ESM]: require() of ES Module .../jose/dist/webapi/index.js from .../better-auth/... not supported.
cause This error occurs in CommonJS NestJS projects when `better-auth` (a peer dependency) version `1.2.11` or higher is installed, as it internally requires an ESM-only package (`jose`).fixFor CommonJS projects, downgrade `better-auth` to version `1.2.10` or lower by running `pnpm add better-auth@1.2.10` (or `npm install better-auth@1.2.10`). Alternatively, convert your NestJS project to use ECMAScript Modules.
Warnings
- gotcha The `nestjs-better-auth` package (version `0.6.4`) is distinct from `@thallesp/nestjs-better-auth` (version `2.x.x`). The `underfisk/nestjs-better-auth` repository, corresponding to this package, shows limited recent activity. The `@thallesp` version appears to be a more actively maintained and updated alternative.
- breaking When using `nestjs-better-auth` in a CommonJS NestJS project, installing `better-auth` (the peer dependency) at version `1.2.11` or higher will cause an `ERR_REQUIRE_ESM` runtime error due to `better-auth`'s dependency on the ESM-only `jose` library.
- breaking This module requires NestJS's built-in body parser to be explicitly disabled in your `main.ts` file for `better-auth` to correctly process incoming authentication requests. Failing to do so will result in authentication failures.
- deprecated The package's README explicitly states a future plan to transition to full ECMAScript Modules (ESM). While CommonJS is currently supported, this future change will be a significant breaking change for projects relying solely on CJS imports.
- gotcha As a `0.x.x` version, `nestjs-better-auth` may introduce breaking changes between minor versions (e.g., `0.6.x` to `0.7.x`) without adhering to strict semantic versioning. Developers should review release notes carefully during upgrades.
- gotcha When `BetterAuthGuard` is registered globally (e.g., using `APP_GUARD`), all routes in your application become protected by default. Publicly accessible routes must be explicitly marked using the `skipAuthDecoratorMetadataKey` mechanism, or they will be unreachable.
- gotcha The `betterAuthConfig` object passed to `BetterAuthModule.forRoot()` directly configures the underlying `better-auth` library. Incomplete or incorrect configurations here can lead to authentication malfunctions, security vulnerabilities, or unexpected application behavior.
Install
-
npm install nestjs-better-auth -
yarn add nestjs-better-auth -
pnpm add nestjs-better-auth
Imports
- BetterAuthModule
const { BetterAuthModule } = require('nestjs-better-auth');import { BetterAuthModule } from 'nestjs-better-auth'; - BetterAuthGuard
const { BetterAuthGuard } = require('nestjs-better-auth');import { BetterAuthGuard } from 'nestjs-better-auth'; - CurrentUserSession
import CurrentUserSession from 'nestjs-better-auth';
import { CurrentUserSession } from 'nestjs-better-auth'; - BetterAuthUserSession
import { BetterAuthUserSession } from 'nestjs-better-auth';
Quickstart
// main.ts (application entry point)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
// CRITICAL: Disable NestJS's built-in body parser for better-auth to process raw requests.
bodyParser: false,
});
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
// app.module.ts (main application module)
import { Module } from '@nestjs/common';
import { BetterAuthModule, BetterAuthGuard, CurrentUserSession, BetterAuthUserSession } from 'nestjs-better-auth';
import { APP_GUARD } from '@nestjs/core'; // Required for global guards
import { Controller, Get, SetMetadata } from '@nestjs/common';
// Define a decorator to mark public routes, bypassing global authentication
const PublicRouteToken = Symbol('publicRoute');
const IsPublic = () => SetMetadata(PublicRouteToken, true);
@Controller()
class MyController {
@IsPublic()
@Get('public')
publicRoute() {
return { message: 'This route is publicly accessible without authentication.' };
}
@Get('me')
getMe(
@CurrentUserSession() userAndSession: BetterAuthUserSession,
@CurrentUserSession('user') user: BetterAuthUserSession['user'],
@CurrentUserSession('session') session: BetterAuthUserSession['session'],
) {
// In a real app, the BetterAuthGuard would prevent unauthenticated access.
// Here, we return session details for the authenticated user.
return { user, session, message: 'Authenticated user session data.' };
}
}
@Module({
imports: [
BetterAuthModule.forRoot({
// Configure which metadata key marks public routes (to skip global auth)
skipAuthDecoratorMetadataKey: PublicRouteToken,
// Provide configuration for the underlying better-auth library
betterAuthConfig: {
emailAndPassword: {
enabled: true, // Example: Enable email and password authentication
},
// ... add more better-auth specific configurations here
},
}),
],
providers: [
// Apply BetterAuthGuard globally to protect all routes by default
{
provide: APP_GUARD,
useClass: BetterAuthGuard,
},
],
controllers: [MyController],
})
export class AppModule {}