@buiducnhat/nest-better-auth
Version:
NestJs Better Auth package, work with express and fastify
456 lines (365 loc) • 11.5 kB
Markdown
# nest-better-auth
A NestJS integration library for [better-auth](https://www.better-auth.com/), providing seamless authentication support for both Express and Fastify platforms.
[](https://badge.fury.io/js/%40buiducnhat%2Fnest-better-auth)
[](https://opensource.org/licenses/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](https://www.better-auth.com/docs/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.
```bash
npm install /nest-better-auth
# or
yarn add /nest-better-auth
# or
pnpm add /nest-better-auth
```
## Quick Start
### 1. Basic Setup with Express
```typescript
import { AuthGuard, AuthModule } from "@buiducnhat/nest-better-auth";
import { Module } from "@nestjs/common";
import { APP_GUARD } from "@nestjs/core";
import { betterAuth } from "better-auth";
({
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](https://www.better-auth.com/docs/integrations/express#mount-the-handler):
>
> 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.
```typescript
async function bootstrap() {
const app = await NestFactory.create(AppModule, { bufferLogs: true, bodyParser: false });
await app.listen(9001);
}
```
### 2. Basic Setup with Fastify
```typescript
import { AuthGuard, AuthModule } from "@buiducnhat/nest-better-auth";
import { Module } from "@nestjs/common";
import { APP_GUARD } from "@nestjs/core";
import { betterAuth } from "better-auth";
({
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
```typescript
import { CurrentUser, IsPublic, Session, User, UserSession } from "@buiducnhat/nest-better-auth";
import { Body, Controller, Get, Post } from "@nestjs/common";
()
export class AppController {
// Public route - no authentication required
()
("public")
getPublicData() {
return { message: "This is a public endpoint" };
}
// Protected route - authentication required
("protected")
getProtectedData() {
return { message: "This is a protected endpoint" };
}
// Get current user information
("me")
getCurrentUser(() user: User) {
return user;
}
// Get full session information
("session")
getSession(() session: UserSession) {
return session;
}
// Protected POST route
("user-action")
performUserAction(() user: User, () 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:
```typescript
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";
({
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
```typescript
// 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:
```typescript
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";
("Admin Users")
()
("admin/users")
(AuthGuard)
export class AdminUsersController {
constructor((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) {}
("/")
getUsers(() session: UserSession, () 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
```typescript
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.
```typescript
import { AuthGuard } from '/nest-better-auth';
import { APP_GUARD } from '/core';
// Apply globally
{
provide: APP_GUARD,
useClass: AuthGuard,
}
// Apply to specific controllers
(AuthGuard)
('protected')
export class ProtectedController {}
```
### Decorators
#### ()
Mark routes as publicly accessible, bypassing the AuthGuard:
```typescript
()
('public')
getPublicData() {
return { message: 'No authentication required' };
}
```
#### ()
Inject the current authenticated user:
```typescript
('profile')
getProfile(() user: User) {
return user;
}
```
#### ()
Inject the full session object:
```typescript
('session-info')
getSessionInfo(() 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
```typescript
import { ArgumentsHost, Catch, ExceptionFilter } from "@nestjs/common";
import { APIError } from "better-auth/api";
(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:
- [Express Example](./example/with-express/) - Basic Express setup
- [Fastify Example](./example/with-fastify/) - Basic Fastify setup
- [Async Configuration Example](./example/with-for-root-async/) - Advanced async configuration
## Integration with Better Auth
This library is designed to work seamlessly with better-auth features:
### Plugins
```typescript
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:
```typescript
import { AuthGuard } from "@buiducnhat/nest-better-auth";
import { Injectable } from "@nestjs/common";
()
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
- `/common` ^11.1.6
- `/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](LICENSE) file for details.
## Support
If you found this library helpful, please consider giving it a ⭐ on [GitHub](https://github.com/buiducnhat/nest-better-auth)!
For issues and feature requests, please use the [GitHub Issues](https://github.com/buiducnhat/nest-better-auth/issues).