feature-request: Add Nest js support #72

Closed
opened 2026-03-13 07:31:00 -05:00 by GiteaMirror · 46 comments
Owner

Originally created by @danyalutsevich on GitHub (Oct 14, 2024).

This is feature request to add nest js support

Originally created by @danyalutsevich on GitHub (Oct 14, 2024). This is feature request to add nest js support
Author
Owner

@NoHaxito commented on GitHub (Oct 15, 2024):

@Bekacru i have an idea on how to "implement" without modifying the current code (only docs)
Is it okay if i do a PR with a
"temporary guide"? (If you plan to do a "custom" integration as is the case with Next.js, Svelte, etc.)

The only thing is to create a controller and module and get the req and res parameters from the decorators.

@NoHaxito commented on GitHub (Oct 15, 2024): @Bekacru i have an idea on how to "implement" without modifying the current code (only docs) Is it okay if i do a PR with a "temporary guide"? (If you plan to do a "custom" integration as is the case with Next.js, Svelte, etc.) The only thing is to create a controller and module and get the `req` and `res` parameters from the decorators.
Author
Owner

@Bekacru commented on GitHub (Oct 15, 2024):

is it okay if i do a PR with a "temporary guide"? (If you plan to do a "custom" integration as is the case with Next.js, Svelte, etc.)

yes feel free to open a pr. and we're not going to have integration we only need the docs.

@Bekacru commented on GitHub (Oct 15, 2024): > is it okay if i do a PR with a "temporary guide"? (If you plan to do a "custom" integration as is the case with Next.js, Svelte, etc.) yes feel free to open a pr. and we're not going to have integration we only need the docs.
Author
Owner

@NoHaxito commented on GitHub (Oct 15, 2024):

is it okay if i do a PR with a "temporary guide"? (If you plan to do a "custom" integration as is the case with Next.js, Svelte, etc.)

yes feel free to open a pr. and we're not going to have integration we only need the docs.

Ok! I will try to have the nestjs docs soon 🙌🏼🚀

@NoHaxito commented on GitHub (Oct 15, 2024): > > is it okay if i do a PR with a "temporary guide"? (If you plan to do a "custom" integration as is the case with Next.js, Svelte, etc.) > > yes feel free to open a pr. and we're not going to have integration we only need the docs. > Ok! I will try to have the nestjs docs soon 🙌🏼🚀
Author
Owner

@ismoiliy98 commented on GitHub (Oct 21, 2024):

Any progress on this?

@ismoiliy98 commented on GitHub (Oct 21, 2024): Any progress on this?
Author
Owner

@danyalutsevich commented on GitHub (Oct 24, 2024):

@ismoiliy98 join us in discord
we have couple of problems with module system in nest and better-auth
feels like we are about to finish)

@danyalutsevich commented on GitHub (Oct 24, 2024): @ismoiliy98 join us in discord we have couple of problems with module system in nest and better-auth feels like we are about to finish)
Author
Owner

@BayBreezy commented on GitHub (Nov 2, 2024):

I tried adding better auth to nest but the ESM nature of better auth is nor playing nicey with NestJS. @danyalutsevich which discord do i need to join to see the discusiion?

@BayBreezy commented on GitHub (Nov 2, 2024): I tried adding better auth to nest but the ESM nature of better auth is nor playing nicey with NestJS. @danyalutsevich which discord do i need to join to see the discusiion?
Author
Owner

@danyalutsevich commented on GitHub (Nov 2, 2024):

@BayBreezy you can check the repo from this #359 issue
you can use dynamic import as a workaround

discord: https://discord.com/invite/GYC3W7tZzb
if it tells you that invite is expired copy the link and paste it to join server window
Screenshot 2024-11-02 at 15 09 36

@danyalutsevich commented on GitHub (Nov 2, 2024): @BayBreezy you can check the [repo](https://github.com/Snazzy72/better-auth-nestjs) from this #359 issue you can use dynamic import as a workaround discord: https://discord.com/invite/GYC3W7tZzb if it tells you that invite is expired copy the link and paste it to join server window <img width="440" alt="Screenshot 2024-11-02 at 15 09 36" src="https://github.com/user-attachments/assets/394ebf35-782a-4733-9f53-aacef27fdad4">
Author
Owner

@danyalutsevich commented on GitHub (Nov 4, 2024):

I dont know if it's possible to merge issues on github
we have duplicate issues

#406 #359

@danyalutsevich commented on GitHub (Nov 4, 2024): I dont know if it's possible to merge issues on github we have duplicate issues #406 #359
Author
Owner

@subenksaha commented on GitHub (Nov 14, 2024):

I think the problem is from nanoid module. I am getting the following error:
Screenshot 2024-11-13 at 9 57 55 PM

if anybody can fix this that would be great help. I could do it by myself but I am getting DTS error when I build the project.

@subenksaha commented on GitHub (Nov 14, 2024): I think the problem is from nanoid module. I am getting the following error: ![Screenshot 2024-11-13 at 9 57 55 PM](https://github.com/user-attachments/assets/b829abe4-f664-442a-b19e-34637e8b319d) if anybody can fix this that would be great help. I could do it by myself but I am getting DTS error when I build the project.
Author
Owner

@aemara commented on GitHub (Nov 15, 2024):

@subenksaha

I think the problem is from nanoid module. I am getting the following error: Screenshot 2024-11-13 at 9 57 55 PM

if anybody can fix this that would be great help. I could do it by myself but I am getting DTS error when I build the project.

I remember running into this problem with nanoid. I was using version 5.0.7, and when I used an older one (3.3.7) the problem went away. I tried to use dynamic import as a workaround but for some reason it didn't work.

@aemara commented on GitHub (Nov 15, 2024): @subenksaha > I think the problem is from nanoid module. I am getting the following error: ![Screenshot 2024-11-13 at 9 57 55 PM](https://github.com/user-attachments/assets/b829abe4-f664-442a-b19e-34637e8b319d) > > if anybody can fix this that would be great help. I could do it by myself but I am getting DTS error when I build the project. I remember running into this problem with nanoid. I was using version 5.0.7, and when I used an older one (3.3.7) the problem went away. I tried to use dynamic import as a workaround but for some reason it didn't work.
Author
Owner

@subenksaha commented on GitHub (Nov 15, 2024):

@aemara yes, that can be workaround but not permanent fix. I created a PR #550 replacing nanoid with @paralleldrive/cuid2 that might solve the issue.

@subenksaha commented on GitHub (Nov 15, 2024): @aemara yes, that can be workaround but not permanent fix. I created a PR #550 replacing nanoid with @paralleldrive/cuid2 that might solve the issue.
Author
Owner

@Innei commented on GitHub (Dec 3, 2024):

I've tried to get the migration done and working in nest js.
I use a way to compile to cjs and then use it directly in nestjs.

49cc5b628f/apps/core/src/modules/auth/auth.implement.ts

@Innei commented on GitHub (Dec 3, 2024): I've tried to get the migration done and working in nest js. I use a way to compile to cjs and then use it directly in nestjs. https://github.com/mx-space/core/blob/49cc5b628fd6e4b8cd5c2adf35c40bf982621b28/apps/core/src/modules/auth/auth.implement.ts
Author
Owner

@subenksaha commented on GitHub (Dec 3, 2024):

@Innei , so you compiled inside your code? or compiled to another package? What is the way?

@subenksaha commented on GitHub (Dec 3, 2024): @Innei , so you compiled inside your code? or compiled to another package? What is the way?
Author
Owner

@Innei commented on GitHub (Dec 3, 2024):

complied sometimes has to be done, in writing nodejs server a few used pkg does not provide cjs export, so need to be converted.

Create a separate subpackage dedicated to compiling these packages using a monorepo.

6e50bee8da/packages/complied/tsup.config.ts

@Innei commented on GitHub (Dec 3, 2024): complied sometimes has to be done, in writing nodejs server a few used pkg does not provide cjs export, so need to be converted. Create a separate subpackage dedicated to compiling these packages using a monorepo. https://github.com/mx-space/core/blob/6e50bee8dafbd7e56742b711d01a167c70f96f9a/packages/complied/tsup.config.ts
Author
Owner

@marcomuser commented on GitHub (Dec 5, 2024):

Are there plans on the nest.js side to move to esm? That would seem like the more healthy fix since the whole ecosystem is moving that direction. In particular since also typescript is starting to think about deprecating and removing features nest.js relies on, e.g. useDefineForClassFields: false and probably later on experimentalDecorators: true. See: https://github.com/microsoft/TypeScript/issues/45995#issuecomment-2291606365. If nest.js doesn't want to be left back in the dark there is gonna have to be some movement soon anyway towards esm and standard decorators.

@marcomuser commented on GitHub (Dec 5, 2024): Are there plans on the nest.js side to move to esm? That would seem like the more healthy fix since the whole ecosystem is moving that direction. In particular since also typescript is starting to think about deprecating and removing features nest.js relies on, e.g. useDefineForClassFields: false and probably later on experimentalDecorators: true. See: https://github.com/microsoft/TypeScript/issues/45995#issuecomment-2291606365. If nest.js doesn't want to be left back in the dark there is gonna have to be some movement soon anyway towards esm and standard decorators.
Author
Owner

@ismoiliy98 commented on GitHub (Dec 5, 2024):

Are there plans on the nest.js side to move to esm?

@marcomuser Unfortunately, no 😞

https://github.com/nestjs/nest/issues/13319
https://github.com/nestjs/nest/issues/13817

@ismoiliy98 commented on GitHub (Dec 5, 2024): > Are there plans on the nest.js side to move to esm? @marcomuser Unfortunately, no 😞 https://github.com/nestjs/nest/issues/13319 https://github.com/nestjs/nest/issues/13817
Author
Owner

@ddedic commented on GitHub (Dec 19, 2024):

Any updates on this? Is there rough eta for nestjs support?

@ddedic commented on GitHub (Dec 19, 2024): Any updates on this? Is there rough eta for nestjs support?
Author
Owner

@BayBreezy commented on GitHub (Dec 19, 2024):

We out here waiting patiently @ddedic

@BayBreezy commented on GitHub (Dec 19, 2024): We out here waiting patiently @ddedic
Author
Owner

@niraj-khatiwada commented on GitHub (Dec 28, 2024):

Oh man I was so excited to integrate better-auth but was disappointed that it does not support Nest.js.
Maybe in the future then.

@niraj-khatiwada commented on GitHub (Dec 28, 2024): Oh man I was so excited to integrate better-auth but was disappointed that it does not support Nest.js. Maybe in the future then.
Author
Owner

@Bekacru commented on GitHub (Jan 13, 2025):

could anyone please confirm me if there are still issue with nest js support?

@Bekacru commented on GitHub (Jan 13, 2025): could anyone please confirm me if there are still issue with nest js support?
Author
Owner

@BayBreezy commented on GitHub (Jan 13, 2025):

@Bekacru I am still investigating.

I was able to get most endpoints working by adding this middleware

import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
import * as express from "express";

@Injectable()
export class RawBodyMiddleware implements NestMiddleware {
  constructor() {
    console.log("RawBodyMiddleware initialized");
  }

  use(req: Request, res: Response, next: NextFunction) {
    // Check if the route matches the desired pattern
    if (req.baseUrl.startsWith("/api/auth")) {
      // Skip JSON and URL-encoded body parsing for these routes
      console.log("Skipping body parsing for:", req.baseUrl);
      next();
      return;
    }

    // Otherwise, parse the body as usual
    express.json()(req, res, (err) => {
      if (err) {
        next(err); // Pass any errors to the error-handling middleware
        return;
      }
      express.urlencoded({ extended: true })(req, res, next);
    });
  }
}

And then registering it like so

export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(RawBodyMiddleware).forRoutes("*");
  }
}

This is my auth config

import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "@prisma/client";
import { openAPI, bearer, admin } from "better-auth/plugins";

const prisma = new PrismaClient();
export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "sqlite", // or "mysql", "postgresql", ...etc
  }),
  // @ts-expect-error - TS shinanigans
  plugins: [openAPI(), admin(), bearer()],
  emailAndPassword: {
    enabled: true,
    autoSignIn: true,
  },
});

This is the controller

@Controller()
export class AppController {
  @All("api/auth/*")
  async auth(@Req() req: Request, @Res() res: Response) {
    return toNodeHandler(auth)(req, res);
  }
}

In the main.ts file, disable global bodyParser

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule, {
    bodyParser: false,
  });
  app.enableCors();
  await app.listen(process.env.PORT);
  Logger.log(`Server running on ${process.env.PUBLIC_URL}`, "Bootstrap");
  Logger.log(`Better Auth API Spec on: ${process.env.PUBLIC_URL}/api/auth/reference`, "Bootstrap");
}
bootstrap();

I will report back if i run into any trouble. For now most things work. I did notice that the bearer & admin plugins were causing all types of type errors. Not sure what that is about

The TS Errors
[5:39:15 AM] Starting compilation in watch mode...

src/lib/auth.ts:11:24 - error TS2322: Type '{ id: "admin"; init(ctx: AuthContext): { options: { databaseHooks: { user: { create: { before(user: { id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string; }): Promise<...>; }; }; session: { ...; }; }; }; }; hooks: { ...; }; endpoints: { ...; }; $ERROR_COD...' is not assignable to type 'BetterAuthPlugin'.
  The types of 'hooks.after' are incompatible between these types.
    Type '{ matcher(context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>): boolean; handler: Endpoint<...>; }[]' is not assignable to type '{ matcher: (context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>) => boolean; handler: HookAfterHandler; }[]'.
      Type '{ matcher(context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>): boolean; handler: Endpoint<...>; }' is not assignable to type '{ matcher: (context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>) => boolean; handler: HookAfterHandler; }'.
        Types of property 'handler' are incompatible.
          Type 'Endpoint<Handler<string, EndpointOptions, { response: { body: any; status: number; statusText: string; headers: Record<string, string>; }; body: SessionWithImpersonatedBy[]; _flag: "json"; }>, EndpointOptions>' is not assignable to type 'HookAfterHandler'.
            Types of parameters 'ctx' and 'context' are incompatible.
              Type 'HookEndpointContext<{}>' is not assignable to type '{ body: { [x: string]: any; }; params?: Record<string, string>; method?: "GET"; headers: Headers; request: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; } | ... 26 more ... | { ...; }'.
                Type 'HookEndpointContext<{}>' is not assignable to type '{ body: { [x: string]: any; }; params?: Record<string, string>; method: Method; headers?: Headers; request?: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; }'.
                  Property 'method' is optional in type 'HookEndpointContext<{}>' but required in type '{ body: { [x: string]: any; }; params?: Record<string, string>; method: Method; headers?: Headers; request?: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; }'.

11   plugins: [openAPI(), admin(), bearer()],
                          ~~~~~~~

src/lib/auth.ts:11:33 - error TS2322: Type '{ id: "bearer"; hooks: { before: { matcher(context: HookEndpointContext): boolean; handler: (c: HookEndpointContext) => Promise<{ context: HookEndpointContext; }>; }[]; after: { ...; }[]; }; }' is not assignable to type 'BetterAuthPlugin'.
  The types of 'hooks.after' are incompatible between these types.
    Type '{ matcher(context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>): boolean; handler: Endpoint<...>; }[]' is not assignable to type '{ matcher: (context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>) => boolean; handler: HookAfterHandler; }[]'.
      Type '{ matcher(context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>): boolean; handler: Endpoint<...>; }' is not assignable to type '{ matcher: (context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>) => boolean; handler: HookAfterHandler; }'.
        Types of property 'handler' are incompatible.
          Type 'Endpoint<Handler<string, EndpointOptions, { responseHeader: Headers; }>, EndpointOptions>' is not assignable to type 'HookAfterHandler'.
            Types of parameters 'ctx' and 'context' are incompatible.
              Type 'HookEndpointContext<{}>' is not assignable to type '{ body: { [x: string]: any; }; params?: Record<string, string>; method?: "GET"; headers: Headers; request: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; } | ... 26 more ... | { ...; }'.
                Type 'HookEndpointContext<{}>' is not assignable to type '{ body: { [x: string]: any; }; params?: Record<string, string>; method: Method; headers?: Headers; request?: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; }'.
                  Property 'method' is optional in type 'HookEndpointContext<{}>' but required in type '{ body: { [x: string]: any; }; params?: Record<string, string>; method: Method; headers?: Headers; request?: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; }'.

11   plugins: [openAPI(), admin(), bearer()],
                                   ~~~~~~~~

[5:39:23 AM] Found 2 errors. Watching for file changes.
@BayBreezy commented on GitHub (Jan 13, 2025): @Bekacru I am still investigating. I was able to get most endpoints working by adding this middleware ```ts import { Injectable, NestMiddleware } from "@nestjs/common"; import { Request, Response, NextFunction } from "express"; import * as express from "express"; @Injectable() export class RawBodyMiddleware implements NestMiddleware { constructor() { console.log("RawBodyMiddleware initialized"); } use(req: Request, res: Response, next: NextFunction) { // Check if the route matches the desired pattern if (req.baseUrl.startsWith("/api/auth")) { // Skip JSON and URL-encoded body parsing for these routes console.log("Skipping body parsing for:", req.baseUrl); next(); return; } // Otherwise, parse the body as usual express.json()(req, res, (err) => { if (err) { next(err); // Pass any errors to the error-handling middleware return; } express.urlencoded({ extended: true })(req, res, next); }); } } ``` And then registering it like so ```ts export class AppModule { configure(consumer: MiddlewareConsumer) { consumer.apply(RawBodyMiddleware).forRoutes("*"); } } ``` This is my auth config ```ts import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { PrismaClient } from "@prisma/client"; import { openAPI, bearer, admin } from "better-auth/plugins"; const prisma = new PrismaClient(); export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "sqlite", // or "mysql", "postgresql", ...etc }), // @ts-expect-error - TS shinanigans plugins: [openAPI(), admin(), bearer()], emailAndPassword: { enabled: true, autoSignIn: true, }, }); ``` This is the controller ```ts @Controller() export class AppController { @All("api/auth/*") async auth(@Req() req: Request, @Res() res: Response) { return toNodeHandler(auth)(req, res); } } ``` In the main.ts file, disable global bodyParser ```ts async function bootstrap() { const app = await NestFactory.create<NestExpressApplication>(AppModule, { bodyParser: false, }); app.enableCors(); await app.listen(process.env.PORT); Logger.log(`Server running on ${process.env.PUBLIC_URL}`, "Bootstrap"); Logger.log(`Better Auth API Spec on: ${process.env.PUBLIC_URL}/api/auth/reference`, "Bootstrap"); } bootstrap(); ``` I will report back if i run into any trouble. For now most things work. I did notice that the `bearer` & `admin` plugins were causing all types of `type` errors. Not sure what that is about <details> <summary>The TS Errors</summary> ```sh [5:39:15 AM] Starting compilation in watch mode... src/lib/auth.ts:11:24 - error TS2322: Type '{ id: "admin"; init(ctx: AuthContext): { options: { databaseHooks: { user: { create: { before(user: { id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string; }): Promise<...>; }; }; session: { ...; }; }; }; }; hooks: { ...; }; endpoints: { ...; }; $ERROR_COD...' is not assignable to type 'BetterAuthPlugin'. The types of 'hooks.after' are incompatible between these types. Type '{ matcher(context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>): boolean; handler: Endpoint<...>; }[]' is not assignable to type '{ matcher: (context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>) => boolean; handler: HookAfterHandler; }[]'. Type '{ matcher(context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>): boolean; handler: Endpoint<...>; }' is not assignable to type '{ matcher: (context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>) => boolean; handler: HookAfterHandler; }'. Types of property 'handler' are incompatible. Type 'Endpoint<Handler<string, EndpointOptions, { response: { body: any; status: number; statusText: string; headers: Record<string, string>; }; body: SessionWithImpersonatedBy[]; _flag: "json"; }>, EndpointOptions>' is not assignable to type 'HookAfterHandler'. Types of parameters 'ctx' and 'context' are incompatible. Type 'HookEndpointContext<{}>' is not assignable to type '{ body: { [x: string]: any; }; params?: Record<string, string>; method?: "GET"; headers: Headers; request: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; } | ... 26 more ... | { ...; }'. Type 'HookEndpointContext<{}>' is not assignable to type '{ body: { [x: string]: any; }; params?: Record<string, string>; method: Method; headers?: Headers; request?: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; }'. Property 'method' is optional in type 'HookEndpointContext<{}>' but required in type '{ body: { [x: string]: any; }; params?: Record<string, string>; method: Method; headers?: Headers; request?: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; }'. 11 plugins: [openAPI(), admin(), bearer()], ~~~~~~~ src/lib/auth.ts:11:33 - error TS2322: Type '{ id: "bearer"; hooks: { before: { matcher(context: HookEndpointContext): boolean; handler: (c: HookEndpointContext) => Promise<{ context: HookEndpointContext; }>; }[]; after: { ...; }[]; }; }' is not assignable to type 'BetterAuthPlugin'. The types of 'hooks.after' are incompatible between these types. Type '{ matcher(context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>): boolean; handler: Endpoint<...>; }[]' is not assignable to type '{ matcher: (context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>) => boolean; handler: HookAfterHandler; }[]'. Type '{ matcher(context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>): boolean; handler: Endpoint<...>; }' is not assignable to type '{ matcher: (context: HookEndpointContext<{ returned: Response | Record<string, any> | APIError; endpoint: Endpoint; }>) => boolean; handler: HookAfterHandler; }'. Types of property 'handler' are incompatible. Type 'Endpoint<Handler<string, EndpointOptions, { responseHeader: Headers; }>, EndpointOptions>' is not assignable to type 'HookAfterHandler'. Types of parameters 'ctx' and 'context' are incompatible. Type 'HookEndpointContext<{}>' is not assignable to type '{ body: { [x: string]: any; }; params?: Record<string, string>; method?: "GET"; headers: Headers; request: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; } | ... 26 more ... | { ...; }'. Type 'HookEndpointContext<{}>' is not assignable to type '{ body: { [x: string]: any; }; params?: Record<string, string>; method: Method; headers?: Headers; request?: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; }'. Property 'method' is optional in type 'HookEndpointContext<{}>' but required in type '{ body: { [x: string]: any; }; params?: Record<string, string>; method: Method; headers?: Headers; request?: Request; query: any; _flag?: "json" | "router" | "default"; ... 10 more ...; responseHeader: Headers; }'. 11 plugins: [openAPI(), admin(), bearer()], ~~~~~~~~ [5:39:23 AM] Found 2 errors. Watching for file changes. ``` </details>
Author
Owner

@BayBreezy commented on GitHub (Jan 14, 2025):

Anyone else here able to test out nest?
@wh5938316 ? I tried it and it seems to work with a few changes.
I must say because of how deep the types are in better-auth, it slows vscode to a crawl in a nestjs project. is there a way around that?

@BayBreezy commented on GitHub (Jan 14, 2025): Anyone else here able to test out nest? @wh5938316 ? I tried it and it seems to work with a few changes. I must say because of how deep the types are in better-auth, it slows vscode to a crawl in a nestjs project. is there a way around that?
Author
Owner

@Oupsla commented on GitHub (Jan 16, 2025):

@BayBreezy I have tried your solution with the RawBodyMiddleware and it is working great.

I have gone a step further and put the creation of the auth object into a service, like that I can use the nestjs DI (to inject my database for example).

I didn't found a simple type from better auth to type the "auth" object, so I have left any at the moment

The module

import { Module } from "@nestjs/common";
import { DatabaseModule } from "src/services/database/database.module";
import { DatabaseService } from "src/services/database/database.service";
import { AuthController } from "./auth.controller";
import { AuthService } from "./auth.service";
import { ConfigModule, ConfigService } from "@nestjs/config";

const authServiceProvider = {
  provide: AuthService,
  useFactory: (
    databaseService: DatabaseService,
    configService: ConfigService
  ) => {
    const baseURL = configService.get("BETTER_AUTH_URL");
    const secret = configService.get("BETTER_AUTH_SECRET");
    const googleClientId = configService.get("GOOGLE_CLIENT_ID");
    const googleClientSecret = configService.get("GOOGLE_CLIENT_SECRET");

    return new AuthService(
      databaseService,
      googleClientId,
      googleClientSecret,
      baseURL,
      secret
    );
  },
  inject: [DatabaseService, ConfigService],
};

@Module({
  imports: [DatabaseModule, ConfigModule],
  providers: [authServiceProvider],
  controllers: [AuthController],
})
export class AuthModule {}

The service

import { Injectable } from "@nestjs/common";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { toNodeHandler } from "better-auth/node";
import { openAPI } from "better-auth/plugins";
import { Request, Response } from "express";
import { DatabaseService } from "src/services/database/database.service";

@Injectable()
export class AuthService {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  private auth: any;

  constructor(
    private readonly database: DatabaseService,
    private readonly googleClientId: string,
    private readonly googleClientSecret: string,
    private readonly baseURL: string,
    private readonly secret: string
  ) {
    this.auth = betterAuth({
      secret: this.secret,
      baseURL: this.baseURL,
      database: drizzleAdapter(this.database.getDatabase(), {
        provider: "pg",
        usePlural: true,
      }),
      emailAndPassword: {
        enabled: true,
        autoSignIn: true,
      },
      socialProviders: {
        google: {
          clientId: this.googleClientId,
          clientSecret: this.googleClientSecret,
        },
      },
      plugins: [openAPI()],
    });
  }

  async handler(req: Request, res: Response): Promise<void> {
    return toNodeHandler(this.auth)(req, res);
  }
}

The controller

import { All, Controller, Req, Res } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
import type { Request, Response } from "express";
import { AuthService } from "./auth.service";

@ApiTags("auth")
@Controller()
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @All("api/auth/*")
  @ApiOperation({
    summary: "Auth handler",
  })
  async auth(@Req() req: Request, @Res() res: Response) {
    return this.authService.handler(req, res);
  }
}

And I have used your example for the app.module and the middleware.
For people coming on this post, use the middleware and not the app.use(json({ limit: "5mb" })); from express to avoid the following error Response body object should not be disturbed or locked

@Oupsla commented on GitHub (Jan 16, 2025): @BayBreezy I have tried your solution with the RawBodyMiddleware and it is working great. I have gone a step further and put the creation of the auth object into a service, like that I can use the nestjs DI (to inject my database for example). I didn't found a simple type from better auth to type the "auth" object, so I have left `any` at the moment The module ```ts import { Module } from "@nestjs/common"; import { DatabaseModule } from "src/services/database/database.module"; import { DatabaseService } from "src/services/database/database.service"; import { AuthController } from "./auth.controller"; import { AuthService } from "./auth.service"; import { ConfigModule, ConfigService } from "@nestjs/config"; const authServiceProvider = { provide: AuthService, useFactory: ( databaseService: DatabaseService, configService: ConfigService ) => { const baseURL = configService.get("BETTER_AUTH_URL"); const secret = configService.get("BETTER_AUTH_SECRET"); const googleClientId = configService.get("GOOGLE_CLIENT_ID"); const googleClientSecret = configService.get("GOOGLE_CLIENT_SECRET"); return new AuthService( databaseService, googleClientId, googleClientSecret, baseURL, secret ); }, inject: [DatabaseService, ConfigService], }; @Module({ imports: [DatabaseModule, ConfigModule], providers: [authServiceProvider], controllers: [AuthController], }) export class AuthModule {} ``` The service ```ts import { Injectable } from "@nestjs/common"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { toNodeHandler } from "better-auth/node"; import { openAPI } from "better-auth/plugins"; import { Request, Response } from "express"; import { DatabaseService } from "src/services/database/database.service"; @Injectable() export class AuthService { // eslint-disable-next-line @typescript-eslint/no-explicit-any private auth: any; constructor( private readonly database: DatabaseService, private readonly googleClientId: string, private readonly googleClientSecret: string, private readonly baseURL: string, private readonly secret: string ) { this.auth = betterAuth({ secret: this.secret, baseURL: this.baseURL, database: drizzleAdapter(this.database.getDatabase(), { provider: "pg", usePlural: true, }), emailAndPassword: { enabled: true, autoSignIn: true, }, socialProviders: { google: { clientId: this.googleClientId, clientSecret: this.googleClientSecret, }, }, plugins: [openAPI()], }); } async handler(req: Request, res: Response): Promise<void> { return toNodeHandler(this.auth)(req, res); } } ``` The controller ```ts import { All, Controller, Req, Res } from "@nestjs/common"; import { ApiOperation, ApiTags } from "@nestjs/swagger"; import type { Request, Response } from "express"; import { AuthService } from "./auth.service"; @ApiTags("auth") @Controller() export class AuthController { constructor(private readonly authService: AuthService) {} @All("api/auth/*") @ApiOperation({ summary: "Auth handler", }) async auth(@Req() req: Request, @Res() res: Response) { return this.authService.handler(req, res); } } ``` And I have used your example for the app.module and the middleware. For people coming on this post, use the middleware and not the `app.use(json({ limit: "5mb" }));` from express to avoid the following error `Response body object should not be disturbed or locked`
Author
Owner

@BayBreezy commented on GitHub (Jan 16, 2025):

Ty @Oupsla !
The reason why i did not go with the service setup was because of the generate command. I am assuming that it did not pickup your configuration and you had to create the tables manually?

Yes, I struggled with the Response body object should not be disturbed or locked error initially.

I was thinking about duplicating the auth config - create a service for Nest DI & one for the better-auth cli. Thoughts?

@BayBreezy commented on GitHub (Jan 16, 2025): Ty @Oupsla ! The reason why i did not go with the service setup was because of the generate command. I am assuming that it did not pickup your configuration and you had to create the tables manually? Yes, I struggled with the `Response body object should not be disturbed or locked` error initially. I was thinking about duplicating the auth config - create a service for Nest DI & one for the better-auth cli. Thoughts?
Author
Owner

@Oupsla commented on GitHub (Jan 17, 2025):

I am assuming that it did not pickup your configuration and you had to create the tables manually?

Yes

I was thinking about duplicating the auth config - create a service for Nest DI & one for the better-auth cli. Thoughts?

Yes I think you should have to use the Nest DI, because if not you will also have to duplicate every services that you use in your auth config (db service for example, but also services used by this one, etc...)

Maybe a good solution will be to do the same thing that drizzle, having a drizzle.config.ts at the root of your project for the CLI, that will contains info about tables configurations, etc...
And then you will have your auth object that will pick up this base configuration and add things like the db connector, secrets, etc...

Maybe this will require some changes in the current state of better-auth lib

@Oupsla commented on GitHub (Jan 17, 2025): > I am assuming that it did not pickup your configuration and you had to create the tables manually? Yes > I was thinking about duplicating the auth config - create a service for Nest DI & one for the better-auth cli. Thoughts? Yes I think you should have to use the Nest DI, because if not you will also have to duplicate every services that you use in your auth config (db service for example, but also services used by this one, etc...) Maybe a good solution will be to do the same thing that drizzle, having a drizzle.config.ts at the root of your project for the CLI, that will contains info about tables configurations, etc... And then you will have your auth object that will pick up this base configuration and add things like the db connector, secrets, etc... Maybe this will require some changes in the current state of better-auth lib
Author
Owner

@Syarx commented on GitHub (Jan 28, 2025):

@Oupsla

you can use

  private auth: ReturnType<typeof betterAuth>;
@Syarx commented on GitHub (Jan 28, 2025): @Oupsla you can use ```ts private auth: ReturnType<typeof betterAuth>; ```
Author
Owner

@ThallesP commented on GitHub (Jan 30, 2025):

Hey @Bekacru is a nestjs integration not wanted? i wouldn't mind contribuing an integration for nestjs
i've thought of something like this:

import { Module } from '@nestjs/common';
import { AuthModule } from 'better-auth/nestjs';

@Module({
  imports: [
    AuthModule.forRoot({ // everything needed for auth!
      emailAndPassword: {
        enabled: true,
      },
    }),
  ],
})
export class AppModule {}

I might also include decorators for hooks:

import { Injectable } from "@nestjs/common";
import { BeforeHook, AuthContext } from "better-auth/nest";
import { SignUpService } from "./sign-up.service";

@Injectable()
export class SignUpHook {
    constructor(private readonly signUpService: SignUpService) {}

    @BeforeHook('/sign-up/email')
    async handle(ctx: AuthContext) {
        // custom logic like enforcing email domain registration
        // might throw APIError if something is wrong
        await this.signUpService.execute(ctx);
    }
}

And also, I'm figuring out an easy way to integrate adapters and DI (for example a PrismaService and prismaAdapter) but I might consider just using a nestjs factory instead... if anyone from the community has a better idea lmk!

@ThallesP commented on GitHub (Jan 30, 2025): Hey @Bekacru is a nestjs integration not wanted? i wouldn't mind contribuing an integration for nestjs i've thought of something like this: ```ts import { Module } from '@nestjs/common'; import { AuthModule } from 'better-auth/nestjs'; @Module({ imports: [ AuthModule.forRoot({ // everything needed for auth! emailAndPassword: { enabled: true, }, }), ], }) export class AppModule {} ``` I might also include decorators for hooks: ```ts import { Injectable } from "@nestjs/common"; import { BeforeHook, AuthContext } from "better-auth/nest"; import { SignUpService } from "./sign-up.service"; @Injectable() export class SignUpHook { constructor(private readonly signUpService: SignUpService) {} @BeforeHook('/sign-up/email') async handle(ctx: AuthContext) { // custom logic like enforcing email domain registration // might throw APIError if something is wrong await this.signUpService.execute(ctx); } } ``` And also, I'm figuring out an easy way to integrate adapters and DI (for example a PrismaService and prismaAdapter) but I might consider just using a nestjs factory instead... if anyone from the community has a better idea lmk!
Author
Owner

@BayBreezy commented on GitHub (Jan 31, 2025):

Hey guys,

Just reporting back with an example app I created with NestJS. the code can be found here: https://github.com/BayBreezy/LearnReact/tree/main/login-system/api

I am learning React now(I think they have more jobs than Vue/Nuxt).

Here is the frontend to test it out: https://login-system.learn-react.behonbaker.com/

Feedback appreciated 🙏🏽

P.S: @Oupsla I did not go with the approach you mentioned(injecting auth as a provider). Thanks @Bekacru for updating the types. Not getting the same errors with the plugins anymore.

@BayBreezy commented on GitHub (Jan 31, 2025): Hey guys, Just reporting back with an example app I created with NestJS. the code can be found here: https://github.com/BayBreezy/LearnReact/tree/main/login-system/api I am learning React now(I think they have more jobs than Vue/Nuxt). Here is the frontend to test it out: https://login-system.learn-react.behonbaker.com/ Feedback appreciated 🙏🏽 P.S: @Oupsla I did not go with the approach you mentioned(injecting auth as a provider). Thanks @Bekacru for updating the types. Not getting the same errors with the plugins anymore.
Author
Owner

@phuongwd commented on GitHub (Feb 23, 2025):

Based on the Express integration docs, it looks like we'll need to set up a similar integration for NestJS.

// src/modules/auth/better-auth.middleware.ts
@Injectable()
export class BetterAuthMiddleware implements NestMiddleware {
  constructor(private readonly auth: BetterAuth) {}

  async use(req: Request, res: Response, next: NextFunction) {
    if (req.path.startsWith('/api/auth/')) {
      // Similar to Express integration
      const handler = toNodeHandler(this.auth);
      return handler(req, res);
    }
    next();
  }
}
// src/modules/auth/auth.module.ts
@Module({
  imports: [],
  providers: [
    {
      provide: 'BETTER_AUTH',
      useFactory: () => {
        return new BetterAuth({
          // Similar config to Express example
          baseUrl: '/api/auth',
          // ... other config
        });
      }
    }
  ]
})
export class AuthModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(BetterAuthMiddleware)
      .forRoutes('*');
  }
}
// src/modules/auth/guards/auth.guard.ts
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    @Inject('BETTER_AUTH') private auth: BetterAuth
  ) {}

  async canActivate(context: ExecutionContext) {
    const req = context.switchToHttp().getRequest();
    const session = await this.auth.api.getSession({
      headers: fromNodeHeaders(req.headers)
    });
    
    if (!session) {
      throw new UnauthorizedException();
    }

    req.user = session.user;
    return true;
  }
}
@phuongwd commented on GitHub (Feb 23, 2025): Based on the [Express integration docs](https://www.better-auth.com/docs/integrations/express), it looks like we'll need to set up a similar integration for NestJS. ``` // src/modules/auth/better-auth.middleware.ts @Injectable() export class BetterAuthMiddleware implements NestMiddleware { constructor(private readonly auth: BetterAuth) {} async use(req: Request, res: Response, next: NextFunction) { if (req.path.startsWith('/api/auth/')) { // Similar to Express integration const handler = toNodeHandler(this.auth); return handler(req, res); } next(); } } ``` ``` // src/modules/auth/auth.module.ts @Module({ imports: [], providers: [ { provide: 'BETTER_AUTH', useFactory: () => { return new BetterAuth({ // Similar config to Express example baseUrl: '/api/auth', // ... other config }); } } ] }) export class AuthModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(BetterAuthMiddleware) .forRoutes('*'); } } ``` ``` // src/modules/auth/guards/auth.guard.ts @Injectable() export class AuthGuard implements CanActivate { constructor( @Inject('BETTER_AUTH') private auth: BetterAuth ) {} async canActivate(context: ExecutionContext) { const req = context.switchToHttp().getRequest(); const session = await this.auth.api.getSession({ headers: fromNodeHeaders(req.headers) }); if (!session) { throw new UnauthorizedException(); } req.user = session.user; return true; } } ```
Author
Owner

@laakal commented on GitHub (Apr 25, 2025):

Hey everyone,

For those looking for a NestJS authentication example using Better Auth, check out this my project! It demonstrates integration with:

  • NestJS v11
  • Express v5
  • MongoDB
  • Includes Google, GitHub, and Credentials authentication providers.
  • Shows basic auth guard usage in a controller.
  • Provides a Next.js frontend example for createAuthClient configuration and sign-in with callback URLs.

Remember to configure your .env file with the necessary MongoDB URI and OAuth credentials. Hope this helps! Special thanks to ThallesP for Better Auth!

Working example project: https://github.com/laakal/nestjs-better-auth-template

@laakal commented on GitHub (Apr 25, 2025): Hey everyone, For those looking for a NestJS authentication example using [Better Auth](https://www.google.com/search?q=https://better-auth.dev/), check out this my [project](https://github.com/laakal/nestjs-better-auth-template)! It demonstrates integration with: - NestJS v11 - Express v5 - MongoDB - Includes Google, GitHub, and Credentials authentication providers. - Shows basic auth guard usage in a controller. - Provides a Next.js frontend example for createAuthClient configuration and sign-in with callback URLs. Remember to configure your .env file with the necessary MongoDB URI and OAuth credentials. Hope this helps! Special thanks to [ThallesP](https://github.com/ThallesP) for Better Auth! **Working example project:** https://github.com/laakal/nestjs-better-auth-template
Author
Owner

@pisacode commented on GitHub (Apr 30, 2025):

Like @laakal explained it is now working on my side too, but be very careful about the version, my nestjs was below 11 and it was not working until I pushed it above 11.

Thanks @laakal

@pisacode commented on GitHub (Apr 30, 2025): Like @laakal explained it is now working on my side too, but be very careful about the version, my nestjs was below 11 and it was not working until I pushed it above 11. Thanks @laakal
Author
Owner

@laakal commented on GitHub (May 3, 2025):

Upon requests, I added a detailed guide for NextJS integration issues(session management in SSR, CSR etc). Check readme.

@laakal commented on GitHub (May 3, 2025): Upon requests, I added a detailed guide for NextJS integration issues(session management in SSR, CSR etc). Check [readme.](https://github.com/laakal/nestjs-better-auth-template/blob/main/README.md)
Author
Owner

@niraj-khatiwada commented on GitHub (May 5, 2025):

Here's an example of using Better Auth with Nest.js + Fastify + PostgreSQL:
https://github.com/niraj-khatiwada/ultimate-nestjs-boilerplate

You can find the client usage example on the README.

Thanks @laakal for the working example above.

@niraj-khatiwada commented on GitHub (May 5, 2025): Here's an example of using Better Auth with Nest.js + Fastify + PostgreSQL: https://github.com/niraj-khatiwada/ultimate-nestjs-boilerplate You can find the client usage example on the README. Thanks @laakal for the working example above.
Author
Owner

@Rykuno commented on GitHub (Jul 10, 2025):

You don't need anything custom for NestJS if you use the pre-existing database integrations(drizzle/prisma). NestJS is simply a wrapper around express/fastify, so just follow those guides in the better-auth docs.

Here's how I implement it

Example Repo: https://github.com/Rykuno/SojuStack < This is just a template repo I use with its implementation. If you just wanna get it working follow the steps below because you don't need 90% of what I use in the repo.

REQUIRED

  1. Create a better-auth service(I have both a better-auth.service.ts(better-auth specific logic) and an auth.service.ts(generic auth logic)
    better-auth.service.ts
import { Injectable } from '@nestjs/common';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { betterAuth } from 'better-auth';

@Injectable()
export class BetterAuthService {
  readonly client: ReturnType<typeof betterAuth>;

  constructor() {
    this.client = betterAuth(...)
  }
}
  1. In the auth module constructor, add the express or fastify middleware to route to better-auth endpoints.
    auth.module.ts
import { Inject, Module } from '@nestjs/common';
import { BetterAuthService } from './better-auth.service';
import { AuthController } from './auth.controller';
import { HttpAdapterHost } from '@nestjs/core';
import { toNodeHandler } from 'better-auth/node';

@Module({
  imports: [],
  providers: [BetterAuthService],
  controllers: [AuthController],
  exports: [BetterAuthService],
})
export class AuthModule {
  constructor(
    private readonly adapter: HttpAdapterHost,
    private readonly betterAuthService: BetterAuthService,
  ) {
    app.getHttpAdapter().use(cors(corsOptions));  // enable cors for underlying adapter too (this is important)
    this.adapter.httpAdapter.all(
      `auth/client/{*any}`,
      toNodeHandler(betterAuthService.client),
    );
  }
  1. don't forget to configure cors
    main.ts

 // enable for nestJS
 app.enableCors(corsOptions); // get this from better-auth docs

}

OPTIONAL

  1. Lets create an auth guard
    auth.guard.ts
import {
  CanActivate,
  ExecutionContext,
  Injectable
} from '@nestjs/common';
import { BetterAuthService } from './better-auth.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly betterAuthService: BetterAuthService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const session = await this.betterAuthService.client.api.getSession({
      headers: request.headers,
    });
    request['session'] = session?.session;
    return true;
  }
}
  1. Now lets create a decorator to get the user data in the controllers
    active-session.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import type { Session } from 'better-auth';

export const ActiveSession = createParamDecorator(
  (field: keyof Session | undefined, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const session: Session | undefined = request['session'];
    return field ? session?.[field] : session;
  },
);

Example of usage inside a controller

import { ActiveSession } from 'src/auth/active-session.decorator';
import type { Session } from 'better-auth';

import 
  @Get()
  @UseGuards(AuthGuard)
  doStuff(@ActiveSession() session: Session, @ActiveSession('userId') sessionUserId: Session) {
    console.log(session) // will print out entire session
    console.log(sessionUserId) // will print out just the `session.userId`
     ...
  }

I cant really expose my current projects code publicly with it, but i'll create a really simple boilerplate people can reference later.

@Rykuno commented on GitHub (Jul 10, 2025): You don't need anything custom for NestJS if you use the pre-existing database integrations(drizzle/prisma). NestJS is simply a wrapper around express/fastify, so just follow those guides in the better-auth docs. Here's how I implement it Example Repo: https://github.com/Rykuno/SojuStack < _This is just a template repo I use with its implementation. If you just wanna get it working follow the steps below because you don't need 90% of what I use in the repo._ REQUIRED --- 1) Create a better-auth service(I have both a _better-auth.service.ts_(better-auth specific logic) and an _auth.service.ts_(generic auth logic) `better-auth.service.ts` ```ts import { Injectable } from '@nestjs/common'; import { drizzleAdapter } from 'better-auth/adapters/drizzle'; import { betterAuth } from 'better-auth'; @Injectable() export class BetterAuthService { readonly client: ReturnType<typeof betterAuth>; constructor() { this.client = betterAuth(...) } } ``` 2) In the auth module constructor, add the express or fastify middleware to route to better-auth endpoints. `auth.module.ts` ```ts import { Inject, Module } from '@nestjs/common'; import { BetterAuthService } from './better-auth.service'; import { AuthController } from './auth.controller'; import { HttpAdapterHost } from '@nestjs/core'; import { toNodeHandler } from 'better-auth/node'; @Module({ imports: [], providers: [BetterAuthService], controllers: [AuthController], exports: [BetterAuthService], }) export class AuthModule { constructor( private readonly adapter: HttpAdapterHost, private readonly betterAuthService: BetterAuthService, ) { app.getHttpAdapter().use(cors(corsOptions)); // enable cors for underlying adapter too (this is important) this.adapter.httpAdapter.all( `auth/client/{*any}`, toNodeHandler(betterAuthService.client), ); } ``` 3) don't forget to configure cors `main.ts` ```ts // enable for nestJS app.enableCors(corsOptions); // get this from better-auth docs ``` } OPTIONAL --- 4) Lets create an auth guard `auth.guard.ts` ```ts import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { BetterAuthService } from './better-auth.service'; @Injectable() export class AuthGuard implements CanActivate { constructor(private readonly betterAuthService: BetterAuthService) {} async canActivate(context: ExecutionContext): Promise<boolean> { const request = context.switchToHttp().getRequest(); const session = await this.betterAuthService.client.api.getSession({ headers: request.headers, }); request['session'] = session?.session; return true; } } ``` 5) Now lets create a decorator to get the user data in the controllers `active-session.decorator.ts` ```ts import { createParamDecorator, ExecutionContext } from '@nestjs/common'; import type { Session } from 'better-auth'; export const ActiveSession = createParamDecorator( (field: keyof Session | undefined, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); const session: Session | undefined = request['session']; return field ? session?.[field] : session; }, ); ``` Example of usage inside a controller ```ts import { ActiveSession } from 'src/auth/active-session.decorator'; import type { Session } from 'better-auth'; import @Get() @UseGuards(AuthGuard) doStuff(@ActiveSession() session: Session, @ActiveSession('userId') sessionUserId: Session) { console.log(session) // will print out entire session console.log(sessionUserId) // will print out just the `session.userId` ... } ``` ~~I cant really expose my current projects code publicly with it, but i'll create a really simple boilerplate people can reference later.~~
Author
Owner

@IRediTOTO commented on GitHub (Jul 13, 2025):

I hope have official documentation for this integrate

@IRediTOTO commented on GitHub (Jul 13, 2025): I hope have official documentation for this integrate
Author
Owner

@ThallesP commented on GitHub (Jul 13, 2025):

for people looking for a library with all the good nestjs stuff, check out mine https://github.com/thallesp/nestjs-better-auth.

@ThallesP commented on GitHub (Jul 13, 2025): for people looking for a library with all the good nestjs stuff, check out mine https://github.com/thallesp/nestjs-better-auth.
Author
Owner

@sidyr6002 commented on GitHub (Jul 27, 2025):

Can we not add prisma adapter to the instance? Can anyone help how to do this?

@sidyr6002 commented on GitHub (Jul 27, 2025): Can we not add prisma adapter to the instance? Can anyone help how to do this?
Author
Owner

@RamAvni commented on GitHub (Aug 12, 2025):

Hi! Is this still being worked on?
I hope to see an official implementation of NestJs in better-auth :)

@RamAvni commented on GitHub (Aug 12, 2025): Hi! Is this still being worked on? I hope to see an official implementation of `NestJs` in better-auth :)
Author
Owner

@Edlavio commented on GitHub (Aug 12, 2025):

@RamAvni I think it’s official now. check it out:
www.better-auth.com/docs/integrations/nestjs

@Edlavio commented on GitHub (Aug 12, 2025): @RamAvni I think it’s official now. check it out: www.better-auth.com/docs/integrations/nestjs
Author
Owner

@RamAvni commented on GitHub (Aug 12, 2025):

@RamAvni I think it’s official now. check it out:
www.better-auth.com/docs/integrations/nestjs

No, its a 3rd party library

So because of the low stars, and questions on future maintenance, I sadly cannot use this in the organization I work at

So I hope it'll be officially suppored and integrated into better auth :)

Now what we do is implement an auth library ourselves, and we're also looking for an alternative.

@RamAvni commented on GitHub (Aug 12, 2025): > @RamAvni I think it’s official now. check it out: > www.better-auth.com/docs/integrations/nestjs No, its a 3rd party library So because of the low stars, and questions on future maintenance, I sadly cannot use this in the organization I work at So I hope it'll be officially suppored and integrated into better auth :) Now what we do is implement an auth library ourselves, and we're also looking for an alternative.
Author
Owner

@ThallesP commented on GitHub (Aug 12, 2025):

@RamAvni I think it’s official now. check it out:
www.better-auth.com/docs/integrations/nestjs

No, its a 3rd party library

So because of the low stars, and questions on future maintenance, I sadly cannot use this in the organization I work at

So I hope it'll be officially suppored and integrated into better auth :)

Now what we do is implement an auth library ourselves, and we're also looking for an alternative.

I am the maintainer of said library and we're already using it at our organization, hopefully that gives some level of trust.

If you're still looking for a no-third party library solution, feel free to examine the source code and implement the basics yourself. There are even examples created by users demonstrating how to implement it yourself in this issue (https://github.com/laakal/nestjs-better-auth-template).

The library is quite basic, and even if it becomes unmaintained, it should be easy to apply any fixes and continue working for years to come.

Regarding official support, no deep integration (modules, decorators and such) will ever be incorporated into better-auth. The best approach would be implementing a basic integration as others have mentioned here.

@ThallesP commented on GitHub (Aug 12, 2025): > > [@RamAvni](https://github.com/RamAvni) I think it’s official now. check it out: > > [www.better-auth.com/docs/integrations/nestjs](http://www.better-auth.com/docs/integrations/nestjs) > > No, its a 3rd party library > > So because of the low stars, and questions on future maintenance, I sadly cannot use this in the organization I work at > > So I hope it'll be officially suppored and integrated into better auth :) > > Now what we do is implement an auth library ourselves, and we're also looking for an alternative. I am the maintainer of said library and we're already using it at our organization, hopefully that gives some level of trust. If you're still looking for a no-third party library solution, feel free to examine the source code and implement the basics yourself. There are even examples created by users demonstrating how to implement it yourself in this issue (https://github.com/laakal/nestjs-better-auth-template). The library is quite basic, and even if it becomes unmaintained, it should be easy to apply any fixes and continue working for years to come. Regarding official support, no deep integration (modules, decorators and such) will ever be incorporated into better-auth. The best approach would be implementing a basic integration as others have mentioned here.
Author
Owner

@sidyr6002 commented on GitHub (Aug 12, 2025):

Hey, @ThallesP, thank you for implementing this. I have been trying to use this along with prisma and I have stepped into bunch of problems. I have went through the discussions and found out prisma is not supported as of now with this library. Is there any work around that you know of?

@sidyr6002 commented on GitHub (Aug 12, 2025): Hey, @ThallesP, thank you for implementing this. I have been trying to use this along with prisma and I have stepped into bunch of problems. I have went through the discussions and found out prisma is not supported as of now with this library. Is there any work around that you know of?
Author
Owner

@Duart3x commented on GitHub (Aug 12, 2025):

Hi @sidyr6002 , I have made this template (https://github.com/Duart3x/nestjs-boilerplate) for NestJs with Prisma, Better-Auth and other tools from the (https://github.com/niraj-khatiwada/ultimate-nestjs-boilerplate) which is originally made with Typeorm.
Feel free to check the implementation and make some improvements :)

@Duart3x commented on GitHub (Aug 12, 2025): Hi @sidyr6002 , I have made this template (https://github.com/Duart3x/nestjs-boilerplate) for NestJs with Prisma, Better-Auth and other tools from the (https://github.com/niraj-khatiwada/ultimate-nestjs-boilerplate) which is originally made with Typeorm. Feel free to check the implementation and make some improvements :)
Author
Owner

@ThallesP commented on GitHub (Aug 12, 2025):

Hey, @ThallesP, thank you for implementing this. I have been trying to use this along with prisma and I have stepped into bunch of problems. I have went through the discussions and found out prisma is not supported as of now with this library. Is there any work around that you know of?

Hey, there's no known issue with Prisma, I even use it myself, feel free to open an issue if you found any problems with it.

@ThallesP commented on GitHub (Aug 12, 2025): > Hey, [@ThallesP](https://github.com/ThallesP), thank you for implementing this. I have been trying to use this along with prisma and I have stepped into bunch of problems. I have went through the discussions and found out prisma is not supported as of now with this library. Is there any work around that you know of? Hey, there's no known issue with Prisma, I even use it myself, feel free to open an issue if you found any problems with it.
Author
Owner

@RawkFX commented on GitHub (Aug 23, 2025):

Hello guys, i have an issue and i'm trying to fix it:

When calling auth.api.createTeam or any other method from auth.api.*, TypeScript reports that the property does not exist on the inferred type.

Error message

TS2339: Property 'createTeam' does not exist on type
InferAPI<{ ok: { <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0?: ({ body?: undefined; } & { method?: "GET" | undefined; } & { query?: Record<string, any> | undefined; } & { params?: Record<string, any> | undefined; } & { ...; } & { ...; } & { ...; }) | undefined): ... 

Code sample

await auth.api.createTeam({
  body: {
    name: organization.name,
    organizationId: organization.id,
  },
})

Expected behavior
auth.api.createTeam should be available and callable without type errors.

Environment

  • @nestjs/core: ^11.1.6
  • better-auth: ^1.3.7
  • typescript: ^5.7.3
  • typescript-eslint: ^8.20.0
  • Node.js: v24.5.0

I'm using Webstorm 2025.2

Does anybody had the same problem and managed to fix it?

@RawkFX commented on GitHub (Aug 23, 2025): Hello guys, i have an issue and i'm trying to fix it: When calling auth.api.createTeam or any other method from auth.api.*, TypeScript reports that the property does not exist on the inferred type. Error message ``` TS2339: Property 'createTeam' does not exist on type InferAPI<{ ok: { <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0?: ({ body?: undefined; } & { method?: "GET" | undefined; } & { query?: Record<string, any> | undefined; } & { params?: Record<string, any> | undefined; } & { ...; } & { ...; } & { ...; }) | undefined): ... ``` Code sample ``` await auth.api.createTeam({ body: { name: organization.name, organizationId: organization.id, }, }) ``` Expected behavior
auth.api.createTeam should be available and callable without type errors. Environment * @nestjs/core: ^11.1.6 * better-auth: ^1.3.7 * typescript: ^5.7.3 * typescript-eslint: ^8.20.0 * Node.js: v24.5.0 I'm using Webstorm 2025.2 Does anybody had the same problem and managed to fix it?
Author
Owner

@dosubot[bot] commented on GitHub (Nov 22, 2025):

Hi, @danyalutsevich. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You requested official NestJS support for better-auth.
  • Community members have shared various integration approaches using middleware, services, and controllers.
  • Challenges include ESM compatibility, TypeScript typing issues, and adapter support like Prisma.
  • No official NestJS integration is currently planned, but third-party libraries and examples offer workable solutions.
  • Discussions continue around improving documentation and type support for NestJS users.

Next Steps:

  • Please let me know if this issue is still relevant with the latest version of better-auth by commenting below.
  • If I don’t hear back within 7 days, this issue will be automatically closed.

Thanks for your understanding and contribution!

@dosubot[bot] commented on GitHub (Nov 22, 2025): Hi, @danyalutsevich. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You requested official NestJS support for better-auth. - Community members have shared various integration approaches using middleware, services, and controllers. - Challenges include ESM compatibility, TypeScript typing issues, and adapter support like Prisma. - No official NestJS integration is currently planned, but third-party libraries and examples offer workable solutions. - Discussions continue around improving documentation and type support for NestJS users. **Next Steps:** - Please let me know if this issue is still relevant with the latest version of better-auth by commenting below. - If I don’t hear back within 7 days, this issue will be automatically closed. Thanks for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#72