Can't return data from before hook #508

Closed
opened 2026-03-13 07:50:17 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @RetrogradeDev on GitHub (Jan 1, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Create a (next.js) backend
  2. Try to modify a request using the before hook, like described here: https://www.better-auth.com/docs/concepts/hooks#example-modify-request-context

Current vs. Expected behavior

I excepted to change the request, but instead this typescript error pops up:

  Type 'Endpoint<Handler<string, EndpointOptions, { body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; } | undefined>, EndpointOptions>' is not assignable to type 'HookBeforeHandler'.
    Type 'Promise<{ body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; } | undefined>' is not assignable to type 'Promise<void | { context?: Partial<HookEndpointContext<{}>> | undefined; } | Response | { response: Record<string, any>; body: any; _flag: "json"; }>'.
      Type '{ body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; } | undefined' is not assignable to type 'void | { context?: Partial<HookEndpointContext<{}>> | undefined; } | Response | { response: Record<string, any>; body: any; _flag: "json"; }'.
        Type '{ body: { name: string; }; context: AuthContext & { returned?: APIError | Response | Record<string, any>; endpoint: Endpoint; }; request?: Request | undefined; headers?: Headers | undefined; ... 10 more ...; responseHeader: Headers; }' is not assignable to type 'void | { context?: Partial<HookEndpointContext<{}>> | undefined; } | Response | { response: Record<string, any>; body: any; _flag: "json"; }'.
          Type '{ body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; }' is missing the following properties from type 'Response': ok, redirected, status, statusText, and 9 more.ts(2322)

Note: without typescript there is no error, but the user just can't login (it says success but useSession returns no data).

What version of Better Auth are you using?

1.1.8

Provide environment information

- OS: Windows 11
- Browser: Firefox

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { createAuthMiddleware } from "better-auth/api";
import { username } from "better-auth/plugins";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "@prisma/client";

import { APIError } from "better-call";
import { sendMail } from "./email-server";

const usernameRegex = /^[a-z0-9_-]{3,32}$/i;

const prisma = new PrismaClient();
export const auth = betterAuth({
	database: prismaAdapter(prisma, {
		provider: "postgresql",
	}),
	plugins: [username()],
	hooks: {
		before: createAuthMiddleware(async (ctx) => {
			if (ctx.path !== "/sign-up/email") {
                                // Error here
				return {
					...ctx,
					body: {
						...ctx.body,
						name: "John Doe",
					},
				};
			}

			if (
				!ctx.body.name ||
				!ctx.body.email ||
				!ctx.body.username ||
				!ctx.body.password ||
				!ctx.body.image
			) {
				throw new APIError("BAD_REQUEST", {
					message: "All fields are required.",
				});
			}

			if (
				!usernameRegex.test(ctx.body.username) ||
				ctx.body.username.length < 3 ||
				ctx.body.username.length > 32
			) {
				throw new APIError("BAD_REQUEST", {
					message:
						"Username should only contain letters, numbers, hyphens and underscores.",
				});
			}

			return;
		}),
	},
	user: {
		changeEmail: {
			enabled: true,
			sendChangeEmailVerification: async (
				{ user, newEmail, url, token },
				request,
			) => {
				await sendMail({
					to: newEmail,
					subject: "Verify your email",
					html: `Click <a href="${url}">here</a> to verify your email`,
				});
			},
		},
	},
	emailVerification: {
		sendOnSignUp: true,
		autoSignInAfterVerification: true,
		sendVerificationEmail: async ({ user, url, token }, request) => {
			await sendMail({
				to: user.email,
				subject: "Verify your email",
				html: `Click <a href="${url}">here</a> to verify your email`,
			});
		},
	},
	emailAndPassword: {
		enabled: true,
		requireEmailVerification: true,
		sendResetPassword: async ({ user, url, token }, request) => {
			await sendMail({
				to: user.email,
				subject: "Reset your password",
				html: `Click <a href="${url}">here</a> to reset your password`,
			});
		},
	},
	socialProviders: {
		google: {
			clientId: process.env.GOOGLE_CLIENT_ID as string,
			clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
			mapProfileToUser: (profile) => {
				return {
					email: profile.email,
					name: profile.name,
					image: profile.picture,
					username: `${profile.name.replace(/[^a-zA-Z0-9]/g, "")}${Math.floor(
						Math.random() * 1000,
					)}`,
				};
			},
		},
	},
});

Additional context

No response

Originally created by @RetrogradeDev on GitHub (Jan 1, 2025). ### Is this suited for github? - [X] Yes, this is suited for github ### To Reproduce 1. Create a (next.js) backend 2. Try to modify a request using the `before` hook, like described here: [https://www.better-auth.com/docs/concepts/hooks#example-modify-request-context](https://www.better-auth.com/docs/concepts/hooks#example-modify-request-context) ### Current vs. Expected behavior I excepted to change the request, but instead this typescript error pops up: ```Type 'Endpoint<Handler<string, EndpointOptions, { body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; } | undefined>, EndpointOptions>' is not assignable to type 'HookBeforeHandler | undefined'. Type 'Endpoint<Handler<string, EndpointOptions, { body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; } | undefined>, EndpointOptions>' is not assignable to type 'HookBeforeHandler'. Type 'Promise<{ body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; } | undefined>' is not assignable to type 'Promise<void | { context?: Partial<HookEndpointContext<{}>> | undefined; } | Response | { response: Record<string, any>; body: any; _flag: "json"; }>'. Type '{ body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; } | undefined' is not assignable to type 'void | { context?: Partial<HookEndpointContext<{}>> | undefined; } | Response | { response: Record<string, any>; body: any; _flag: "json"; }'. Type '{ body: { name: string; }; context: AuthContext & { returned?: APIError | Response | Record<string, any>; endpoint: Endpoint; }; request?: Request | undefined; headers?: Headers | undefined; ... 10 more ...; responseHeader: Headers; }' is not assignable to type 'void | { context?: Partial<HookEndpointContext<{}>> | undefined; } | Response | { response: Record<string, any>; body: any; _flag: "json"; }'. Type '{ body: { name: string; }; context: AuthContext & { returned?: Response | Record<string, any> | APIError | undefined; endpoint: Endpoint; }; ... 12 more ...; responseHeader: Headers; }' is missing the following properties from type 'Response': ok, redirected, status, statusText, and 9 more.ts(2322) ``` Note: without typescript there is no error, but the user just can't login (it says success but `useSession` returns no data). ### What version of Better Auth are you using? 1.1.8 ### Provide environment information ```bash - OS: Windows 11 - Browser: Firefox ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { createAuthMiddleware } from "better-auth/api"; import { username } from "better-auth/plugins"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { PrismaClient } from "@prisma/client"; import { APIError } from "better-call"; import { sendMail } from "./email-server"; const usernameRegex = /^[a-z0-9_-]{3,32}$/i; const prisma = new PrismaClient(); export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), plugins: [username()], hooks: { before: createAuthMiddleware(async (ctx) => { if (ctx.path !== "/sign-up/email") { // Error here return { ...ctx, body: { ...ctx.body, name: "John Doe", }, }; } if ( !ctx.body.name || !ctx.body.email || !ctx.body.username || !ctx.body.password || !ctx.body.image ) { throw new APIError("BAD_REQUEST", { message: "All fields are required.", }); } if ( !usernameRegex.test(ctx.body.username) || ctx.body.username.length < 3 || ctx.body.username.length > 32 ) { throw new APIError("BAD_REQUEST", { message: "Username should only contain letters, numbers, hyphens and underscores.", }); } return; }), }, user: { changeEmail: { enabled: true, sendChangeEmailVerification: async ( { user, newEmail, url, token }, request, ) => { await sendMail({ to: newEmail, subject: "Verify your email", html: `Click <a href="${url}">here</a> to verify your email`, }); }, }, }, emailVerification: { sendOnSignUp: true, autoSignInAfterVerification: true, sendVerificationEmail: async ({ user, url, token }, request) => { await sendMail({ to: user.email, subject: "Verify your email", html: `Click <a href="${url}">here</a> to verify your email`, }); }, }, emailAndPassword: { enabled: true, requireEmailVerification: true, sendResetPassword: async ({ user, url, token }, request) => { await sendMail({ to: user.email, subject: "Reset your password", html: `Click <a href="${url}">here</a> to reset your password`, }); }, }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, mapProfileToUser: (profile) => { return { email: profile.email, name: profile.name, image: profile.picture, username: `${profile.name.replace(/[^a-zA-Z0-9]/g, "")}${Math.floor( Math.random() * 1000, )}`, }; }, }, }, }); ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-03-13 07:50:17 -05:00
Author
Owner

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

Docs are updated. You need to return a context object instead

return {
	context: {
             ...ctx,
	     body: {
		...ctx.body,
		name: "John Doe",
           }, 
       }				
};
@Bekacru commented on GitHub (Jan 2, 2025): Docs are updated. You need to return a `context` object instead ```ts return { context: { ...ctx, body: { ...ctx.body, name: "John Doe", }, } }; ```
Author
Owner

@RetrogradeDev commented on GitHub (Jan 2, 2025):

Thanks, it worked!

@RetrogradeDev commented on GitHub (Jan 2, 2025): Thanks, it worked!
Author
Owner

@mishelen commented on GitHub (Mar 13, 2025):

Docs are updated. You need to return a context object instead

Hey @Bekacru!

I'm also struggling with before hook
Is it mandatory to return context from it?
Because I have to use early return from there in form


return ctx.json({});

and I'm getting

 [Error: No response is returned from route handler '...../api/auth/[...all]/route.ts'. Ensure you return a `Response` or a `NextResponse` in all branches of your handler.]
@mishelen commented on GitHub (Mar 13, 2025): > Docs are updated. You need to return a `context` object instead > Hey @Bekacru! I'm also struggling with `before` hook Is it mandatory to return `context` from it? Because I have to use early return from there in form ```js return ctx.json({}); ``` and I'm getting ``` [Error: No response is returned from route handler '...../api/auth/[...all]/route.ts'. Ensure you return a `Response` or a `NextResponse` in all branches of your handler.] ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#508