npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@buiducnhat/nest-better-auth

v1.1.0

Published

NestJs Better Auth package, work with express and fastify

Readme

nest-better-auth

A NestJS integration library for better-auth, providing seamless authentication support for both Express and Fastify platforms.

npm version License: MIT

Features

  • 🚀 Easy Integration: Simple setup with NestJS modules
  • 🔒 Authentication Guard: Built-in guard for protecting routes
  • 🎯 Decorators: Convenient decorators for accessing user session and data, and isPublic for marking routes as publicly accessible
  • 🌐 Multi-Platform: Supports both Express and Fastify
  • ⚙️ Flexible Configuration: Both synchronous and asynchronous configuration options
  • 📝 Type-Safe: Full TypeScript support with proper typing

Installation

Before you start, make sure you have a Better Auth instance configured. If you haven't done that yet, check out the installation.

[!WARNING] This is not the official library of Better Auth. It is a community-driven library that is not officially supported by Better Auth.

npm install @buiducnhat/nest-better-auth
# or
yarn add @buiducnhat/nest-better-auth
# or
pnpm add @buiducnhat/nest-better-auth

Quick Start

1. Basic Setup with Express

import { AuthGuard, AuthModule } from "@buiducnhat/nest-better-auth";
import { Module } from "@nestjs/common";
import { APP_GUARD } from "@nestjs/core";
import { betterAuth } from "better-auth";

@Module({
  imports: [
    AuthModule.forRoot({
      betterAuth: betterAuth({
        basePath: "/auth",
        secret: process.env.AUTH_SECRET,
        emailAndPassword: {
          enabled: true,
        },
        database: {
          // Your database configuration
        },
      }),
      options: {
        routingProvider: "express", // default
        jsonParser: true, // default
      },
    }),
  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
})
export class AppModule {}

[!WARNING] Due to this document:

Don’t use express.json() before the Better Auth handler. Use it only for other routes, or the client API will get stuck on "pending".

So, you need to turn off the bodyParser option on main.ts file.

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { bufferLogs: true, bodyParser: false });
  await app.listen(9001);
}

2. Basic Setup with Fastify

import { AuthGuard, AuthModule } from "@buiducnhat/nest-better-auth";
import { Module } from "@nestjs/common";
import { APP_GUARD } from "@nestjs/core";
import { betterAuth } from "better-auth";

@Module({
  imports: [
    AuthModule.forRoot({
      betterAuth: betterAuth({
        basePath: "/auth",
        secret: process.env.AUTH_SECRET,
        emailAndPassword: {
          enabled: true,
        },
        database: {
          // Your database configuration
        },
      }),
      options: {
        routingProvider: "fastify",
      },
    }),
  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
})
export class AppModule {}

3. Using Controllers with Authentication

import { CurrentUser, IsPublic, Session, User, UserSession } from "@buiducnhat/nest-better-auth";
import { Body, Controller, Get, Post } from "@nestjs/common";

@Controller()
export class AppController {
  // Public route - no authentication required
  @IsPublic()
  @Get("public")
  getPublicData() {
    return { message: "This is a public endpoint" };
  }

  // Protected route - authentication required
  @Get("protected")
  getProtectedData() {
    return { message: "This is a protected endpoint" };
  }

  // Get current user information
  @Get("me")
  getCurrentUser(@CurrentUser() user: User) {
    return user;
  }

  // Get full session information
  @Get("session")
  getSession(@Session() session: UserSession) {
    return session;
  }

  // Protected POST route
  @Post("user-action")
  performUserAction(@CurrentUser() user: User, @Body() data: any) {
    return {
      user: user.id,
      action: "performed",
      data,
    };
  }
}

Advanced Configuration

Async Configuration

For more complex setups, you can use async configuration with dependency injection:

import { AuthGuard, AuthModule } from "@buiducnhat/nest-better-auth";
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { APP_GUARD } from "@nestjs/core";
import { betterAuth } from "better-auth";

@Module({
  imports: [
    ConfigModule.forRoot(),
    AuthModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        betterAuth: betterAuth({
          basePath: configService.get("AUTH_BASE_PATH", "/auth"),
          secret: configService.get("AUTH_SECRET"),
          emailAndPassword: {
            enabled: true,
          },
          database: {
            provider: configService.get("DB_PROVIDER"),
            url: configService.get("DATABASE_URL"),
          },
        }),
        options: {
          routingProvider: configService.get("ROUTING_PROVIDER", "express"),
        },
      }),
    }),
  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
})
export class AppModule {}

Configuration File Example

// config/configuration.ts
export default () => ({
  betterAuth: {
    basePath: process.env.AUTH_BASE_PATH || "/auth",
    secret: process.env.AUTH_SECRET,
    database: {
      provider: process.env.DB_PROVIDER || "sqlite",
      url: process.env.DATABASE_URL || "./database.db",
    },
  },
});

Use the better-auth instance

You can use the better-auth instance to access the Better Auth API:

import { auth } from "@/libs/auth";
// Use can create a dedicated auth.ts like better-auth traditional way for using cli, and infer its type for fully support API
import {
  AuthGuard,
  BETTER_AUTH_INSTANCE_TOKEN,
  Session,
  type UserSession,
} from "@buiducnhat/nest-better-auth";
import { Controller, Get, Inject, Req, UseGuards } from "@nestjs/common";
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
import { fromNodeHeaders } from "better-auth/node";
import type { Request } from "express";

@ApiTags("Admin Users")
@ApiBearerAuth()
@Controller("admin/users")
@UseGuards(AuthGuard)
export class AdminUsersController {
  constructor(@Inject(BETTER_AUTH_INSTANCE_TOKEN) private readonly authInstance: typeof auth) {}
  // You can absolutely use the Auth type from better-auth
  // import { Auth } from "better-auth";
  // ...
  // constructor(@Inject(BETTER_AUTH_INSTANCE_TOKEN) private readonly authInstance: Auth) {}

  @Get("/")
  getUsers(@Session() session: UserSession, @Req() req: Request) {
    return this.authInstance.api.listUserAccounts({
      headers: fromNodeHeaders(req.headers),
    });
  }
}

API Reference

AuthModule

Static Methods

  • forRoot(options): Configure the module synchronously
  • forRootAsync(options): Configure the module asynchronously

Options

interface AuthModuleOptions {
  betterAuth: ReturnType<typeof betterAuth>;
  options?: {
    routingProvider?: "express" | "fastify"; // default: 'express'
    jsonParser?: boolean; // default: true
  };
}

AuthGuard

A guard that protects routes by checking for valid authentication sessions.

import { AuthGuard } from '@buiducnhat/nest-better-auth';
import { APP_GUARD } from '@nestjs/core';

// Apply globally
{
  provide: APP_GUARD,
  useClass: AuthGuard,
}

// Apply to specific controllers
@UseGuards(AuthGuard)
@Controller('protected')
export class ProtectedController {}

Decorators

@IsPublic()

Mark routes as publicly accessible, bypassing the AuthGuard:

@IsPublic()
@Get('public')
getPublicData() {
  return { message: 'No authentication required' };
}

@CurrentUser()

Inject the current authenticated user:

@Get('profile')
getProfile(@CurrentUser() user: User) {
  return user;
}

@Session()

Inject the full session object:

@Get('session-info')
getSessionInfo(@Session() session: UserSession) {
  return {
    user: session.user,
    sessionId: session.id,
    expiresAt: session.expiresAt,
  };
}

Error Handling

The library automatically handles authentication errors and returns appropriate HTTP status codes:

  • 401 Unauthorized: When authentication is required but not provided
  • The library integrates with better-auth's error handling system
import { ArgumentsHost, Catch, ExceptionFilter } from "@nestjs/common";
import { APIError } from "better-auth/api";

@Catch(APIError)
export class AuthExceptionFilter implements ExceptionFilter {
  catch(exception: APIError, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();

    response.status(exception.status).json({
      statusCode: exception.status,
      message: exception.message,
      error: exception.body?.code,
    });
  }
}

Examples

The repository includes complete examples for different setups:

Integration with Better Auth

This library is designed to work seamlessly with better-auth features:

Plugins

import { bearer, twoFactor } from "better-auth/plugins";

AuthModule.forRoot({
  betterAuth: betterAuth({
    plugins: [
      bearer(), // Bearer token authentication
      twoFactor(), // Two-factor authentication
      // ... other plugins
    ],
  }),
});

Custom Authentication Logic

You can extend the AuthGuard for custom authentication logic:

import { AuthGuard } from "@buiducnhat/nest-better-auth";
import { Injectable } from "@nestjs/common";

@Injectable()
export class CustomAuthGuard extends AuthGuard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const isAuthenticated = await super.canActivate(context);

    if (!isAuthenticated) return false;

    // Add custom logic here
    const request = context.switchToHttp().getRequest();
    const user = request.user;

    // Example: Check user role
    if (user.role !== "admin") {
      throw new ForbiddenException("Admin access required");
    }

    return true;
  }
}

Requirements

  • Node.js 18+
  • NestJS 10+
  • Better Auth 1.3.6+

Peer Dependencies

  • @nestjs/common ^11.1.6
  • @nestjs/core ^11.1.6
  • better-auth ^1.3.6

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

If you found this library helpful, please consider giving it a ⭐ on GitHub!

For issues and feature requests, please use the GitHub Issues.