better-auth does not connect routes when using Hono routes with similar param order/rules #271

Closed
opened 2026-03-13 07:40:20 -05:00 by GiteaMirror · 9 comments
Owner

Originally created by @prokopsimek on GitHub (Nov 24, 2024).

Describe the bug
better-auth does not mount any routes for Hono if there is some param mismatch that is valid for Hono router

To Reproduce
Steps to reproduce the behavior:

  1. Create two new routes for Hono that behave similarly in terms of parameters in path:

    import { Hono } from "hono";
    import { validator } from "hono-openapi/zod";
    import { z } from "zod";
    
    // this gets a user with a given ID
    export const usersRoute = new Hono().get(
        "/users/:id",
        validator("param", z.object({ id: z.string() })),
        async (c) => {
    	    const { id } = c.req.valid("param");
    	    return c.json(`This is the user #${id}`);
        },
    );
    
    // similarly to /users/:id this gets info just about me
    export const meRoute = new Hono().get("/users/me", async (c) => {
        return c.json("This is the /me route");
    });
    
    export const usersRouter = new Hono()
        .basePath("/sandbox")
        .route("/", meRoute) // first match
        .route("/", usersRoute); // otherwise - if you switch these routes so the /me route doesn't work, it's probably fine
    

^^^^ This works well with Hono.

  1. With this route setup the better-auth won't ever mount the URLs for auth (https://www.better-auth.com/docs/integrations/hono#mount-the-handler)
  2. All better-auth URLs returns 404 without any console error.

Expected behavior
All better-auth routes are mounted correctly if you use the /users/:id and /users/me paths.

Screenshots
If applicable, add screenshots to help explain your problem.

/users/me route, meRouter for Hono
Screenshot 2024-11-24 at 11 04 31

/users/:id route, usersRouter for Hono
Screenshot 2024-11-24 at 11 04 37
/users/:id route, usersRouter for Hono
Screenshot 2024-11-24 at 11 04 47

404 - invalid better-auth base prefix
image
404 - correct URL but better-auth wasn't mounted
image
200 - correct URL w/o conflicting Hono routes (usersRouter + meRouter)
image

Desktop (please complete the following information):

Additional context

  • using custom better-auth path prefix /auth
  • using Next.js apps/web and for Hono server apps/api builded with next build --turbo

My better-auth config:

export const auth = betterAuth({
	baseURL: "http://localhost:3001",
	basePath: "/auth",
	trustedOrigins: ["http://localhost:3000"],
	database: prismaAdapter(db, {
		provider: "postgresql",
	}),
	advanced: {
		crossSubDomainCookies: {
			enabled: true,
		},
	},
	session: {
		expiresIn: config.auth.sessionCookieMaxAge,
	},
	account: {
		accountLinking: {
			enabled: true,
			trustedProviders: ["google", "github"],
		},
	},
	user: {
		additionalFields: {
			onboardingComplete: {
				type: "boolean",
				required: false,
			},
		},
	},
	emailAndPassword: {
		enabled: true,
		autoSignIn: false,
		requireEmailVerification: true,
		sendResetPassword: async ({ user, url }, request) => {
			const locale = getLocaleFromRequest(request);
			await sendEmail({
				to: user.email,
				templateId: "forgotPassword",
				context: {
					url,
					name: user.name,
				},
				locale,
			});
		},
	},
	emailVerification: {
		sendOnSignUp: true,
		sendVerificationEmail: async ({ user: { email, name }, url }, request) => {
			const locale = getLocaleFromRequest(request);
			await sendEmail({
				to: email,
				templateId: "emailVerification",
				context: {
					url,
					name,
				},
				locale,
			});
		},
	},
	socialProviders: {
		google: {
			clientId: process.env.GOOGLE_CLIENT_ID as string,
			clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
			scope: ["email", "profile"],
		},
		github: {
			clientId: process.env.GITHUB_CLIENT_ID as string,
			clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
			scope: ["user:email"],
		},
	},
	plugins: [
		username(),
		admin(),
		passkey(),
		openAPI(),
		magicLink({
			disableSignUp: true,
			sendMagicLink: async ({ email, url }, request) => {
				const locale = getLocaleFromRequest(request);
				await sendEmail({
					to: email,
					templateId: "magicLink",
					context: {
						url,
					},
					locale,
				});
			},
		}),
		organization({
			sendInvitationEmail: async (
				{ email, id, organization, inviter },
				request,
			) => {
				const locale = getLocaleFromRequest(request);
				const existingUser = await getUserByEmail(email);

				const url = new URL(
					existingUser ? "/auth/login" : "/auth/signup",
					getBaseUrl(),
				);

				url.searchParams.set("invitationId", id);
				url.searchParams.set("email", email);

				await sendEmail({
					to: email,
					templateId: "organizationInvitation",
					locale,
					context: {
						organizationName: organization.name,
						url: url.toString(),
					},
				});
			},
		}),
	],
	onAPIError: {
		onError(error, ctx) {
			logger.error(error, { ctx });
		},
	},
});
Originally created by @prokopsimek on GitHub (Nov 24, 2024). **Describe the bug** better-auth does not mount any routes for Hono if there is some param mismatch that is valid for Hono router **To Reproduce** Steps to reproduce the behavior: 1. Create two new routes for Hono that behave similarly in terms of parameters in path: ```ts import { Hono } from "hono"; import { validator } from "hono-openapi/zod"; import { z } from "zod"; // this gets a user with a given ID export const usersRoute = new Hono().get( "/users/:id", validator("param", z.object({ id: z.string() })), async (c) => { const { id } = c.req.valid("param"); return c.json(`This is the user #${id}`); }, ); // similarly to /users/:id this gets info just about me export const meRoute = new Hono().get("/users/me", async (c) => { return c.json("This is the /me route"); }); export const usersRouter = new Hono() .basePath("/sandbox") .route("/", meRoute) // first match .route("/", usersRoute); // otherwise - if you switch these routes so the /me route doesn't work, it's probably fine ``` **^^^^ This works well with Hono.** 3. With this route setup the better-auth won't ever mount the URLs for auth (https://www.better-auth.com/docs/integrations/hono#mount-the-handler) 4. All better-auth URLs returns 404 without any console error. **Expected behavior** All better-auth routes are mounted correctly if you use the `/users/:id` and `/users/me` paths. **Screenshots** If applicable, add screenshots to help explain your problem. **/users/me route, `meRouter` for Hono** <img width="457" alt="Screenshot 2024-11-24 at 11 04 31" src="https://github.com/user-attachments/assets/d2b3969b-f7fc-4027-9100-a764d84b28cf"> **/users/:id route, `usersRouter` for Hono** <img width="471" alt="Screenshot 2024-11-24 at 11 04 37" src="https://github.com/user-attachments/assets/c5555f0d-c744-4d03-b786-c1fa34a54f29"> **/users/:id route, `usersRouter` for Hono** <img width="510" alt="Screenshot 2024-11-24 at 11 04 47" src="https://github.com/user-attachments/assets/8e3db6ea-d29d-411d-ba63-723bd26d3a85"> **404 - invalid better-auth base prefix** <img width="458" alt="image" src="https://github.com/user-attachments/assets/27786f35-1acb-4415-8369-2628f684d7c5"> **404 - correct URL but better-auth wasn't mounted** <img width="426" alt="image" src="https://github.com/user-attachments/assets/80530ff4-fb66-4494-a21d-1cb67a095354"> **200 - correct URL w/o conflicting Hono routes (usersRouter + meRouter)** <img width="413" alt="image" src="https://github.com/user-attachments/assets/95671355-6b02-4ff2-9cc6-608af19a678d"> **Desktop (please complete the following information):** - OS: macOS 15.2 - Browser Brave is up to date [Version 1.73.91 Chromium: 131.0.6778.85 (Official Build) (arm64)](https://brave.com/latest/) - Version 1.73.91 **Additional context** - using custom better-auth path prefix `/auth` - using Next.js `apps/web` and for Hono server `apps/api` builded with `next build --turbo` My better-auth config: ```ts export const auth = betterAuth({ baseURL: "http://localhost:3001", basePath: "/auth", trustedOrigins: ["http://localhost:3000"], database: prismaAdapter(db, { provider: "postgresql", }), advanced: { crossSubDomainCookies: { enabled: true, }, }, session: { expiresIn: config.auth.sessionCookieMaxAge, }, account: { accountLinking: { enabled: true, trustedProviders: ["google", "github"], }, }, user: { additionalFields: { onboardingComplete: { type: "boolean", required: false, }, }, }, emailAndPassword: { enabled: true, autoSignIn: false, requireEmailVerification: true, sendResetPassword: async ({ user, url }, request) => { const locale = getLocaleFromRequest(request); await sendEmail({ to: user.email, templateId: "forgotPassword", context: { url, name: user.name, }, locale, }); }, }, emailVerification: { sendOnSignUp: true, sendVerificationEmail: async ({ user: { email, name }, url }, request) => { const locale = getLocaleFromRequest(request); await sendEmail({ to: email, templateId: "emailVerification", context: { url, name, }, locale, }); }, }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, scope: ["email", "profile"], }, github: { clientId: process.env.GITHUB_CLIENT_ID as string, clientSecret: process.env.GITHUB_CLIENT_SECRET as string, scope: ["user:email"], }, }, plugins: [ username(), admin(), passkey(), openAPI(), magicLink({ disableSignUp: true, sendMagicLink: async ({ email, url }, request) => { const locale = getLocaleFromRequest(request); await sendEmail({ to: email, templateId: "magicLink", context: { url, }, locale, }); }, }), organization({ sendInvitationEmail: async ( { email, id, organization, inviter }, request, ) => { const locale = getLocaleFromRequest(request); const existingUser = await getUserByEmail(email); const url = new URL( existingUser ? "/auth/login" : "/auth/signup", getBaseUrl(), ); url.searchParams.set("invitationId", id); url.searchParams.set("email", email); await sendEmail({ to: email, templateId: "organizationInvitation", locale, context: { organizationName: organization.name, url: url.toString(), }, }); }, }), ], onAPIError: { onError(error, ctx) { logger.error(error, { ctx }); }, }, }); ```
Author
Owner

@Bekacru commented on GitHub (Nov 24, 2024):

Better Auth does not affect Hono's router routing. Routing is handled by Hono until the handler is called.

@Bekacru commented on GitHub (Nov 24, 2024): Better Auth does not affect Hono's router routing. Routing is handled by Hono until the handler is called.
Author
Owner

@prokopsimek commented on GitHub (Nov 24, 2024):

@Bekacru Let's try to mount Better-Auth by the guide to Hono and you'll see that Better-Auth will stop working. ;) Sure that I was testing Hono w/o better-auth first and all routes worked. When I mount better-auth to such this configuration as described above, better-auth stops working - the routes are probably (log displays them but just with api/auth/**, so couldn't check if the exact routes such as /sign-in are mounted or not, but OpenAPI endpoint stopped working at all too and returned 404) not mounted and return 404.

It definitely might be an issue of Hono with asterisk routes, but the problem is first related with mounting better-auth routes in my case. So I'd love to investigate. Is there some Hono example with better-auth where I can do the MRE?

@prokopsimek commented on GitHub (Nov 24, 2024): @Bekacru Let's try to mount Better-Auth by the guide to Hono and you'll see that Better-Auth will stop working. ;) Sure that I was testing Hono w/o better-auth first and all routes worked. When I mount better-auth to such this configuration as described above, better-auth stops working - the routes are probably (log displays them but just with `api/auth/**`, so couldn't check if the exact routes such as `/sign-in` are mounted or not, but OpenAPI endpoint stopped working at all too and returned 404) not mounted and return 404. It definitely might be an issue of Hono with asterisk routes, but the problem is first related with mounting better-auth routes in my case. So I'd love to investigate. Is there some Hono example with better-auth where I can do the MRE?
Author
Owner

@soumame commented on GitHub (Dec 24, 2024):

I've experienced same issue just now while I'm looking docs. You can try /* instead of /** to solve this.

@soumame commented on GitHub (Dec 24, 2024): I've experienced same issue just now while I'm looking [docs](https://www.better-auth.com/docs/installation#mount-handler). You can try `/*` instead of `/**` to solve this.
Author
Owner

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

@Bekacru How was this resolved that you marked is as completed? Thank you

@prokopsimek commented on GitHub (Jan 13, 2025): @Bekacru How was this resolved that you marked is as completed? Thank you
Author
Owner

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

@prokopsimek assuming @soumame fixes the issue and as a I mentioned before, there isn't much we could do from better auth side to affect specifically hono's routing. If the issue is after hono routed to better auth, and better auth failed to resolve, please feel free to re-open and would love to take a look. But if the handler isn't reached there is nothing we could do.

@Bekacru commented on GitHub (Jan 13, 2025): @prokopsimek assuming @soumame fixes the issue and as a I mentioned before, there isn't much we could do from better auth side to affect specifically hono's routing. If the issue is after hono routed to better auth, and better auth failed to resolve, please feel free to re-open and would love to take a look. But if the handler isn't reached there is nothing we could do.
Author
Owner

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

also closing stale issues that hasn't been re-reported here on github or discord.

@Bekacru commented on GitHub (Jan 13, 2025): also closing stale issues that hasn't been re-reported here on github or discord.
Author
Owner

@prokopsimek commented on GitHub (Feb 11, 2025):

@soumame changing /** to /* works, thank you!

@prokopsimek commented on GitHub (Feb 11, 2025): @soumame changing `/**` to `/*` works, thank you!
Author
Owner

@ikemHood commented on GitHub (Jul 31, 2025):

Experiencing this, and nothing seems to work

@ikemHood commented on GitHub (Jul 31, 2025): Experiencing this, and nothing seems to work
Author
Owner

@ikemHood commented on GitHub (Jul 31, 2025):

Experiencing this, and nothing seems to work

Added base path basePath: "/api/v1/auth", and it worked.

@ikemHood commented on GitHub (Jul 31, 2025): > Experiencing this, and nothing seems to work Added base path `basePath: "/api/v1/auth",` and it worked.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#271