[GH-ISSUE #6845] Polar BetterAuth client APIs (checkout, portal, customer.*) do not work in Next.js App Router + Turbopack #19282

Closed
opened 2026-04-15 18:09:54 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @isaiahpere on GitHub (Dec 18, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6845

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Create next app with the following environment:

Next.js: 15.5.9 (App Router)
Bundler: Turbopack
better-auth: 1.4.6
@polar-sh/better-auth: 1.6.1
@polar-sh/sdk: 0.41.5
React: 19.1.0
Database: Prisma (PostgreSQL)

Server Setup
`// src/lib/auth.ts
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { polar, checkout, portal } from "@polar-sh/better-auth";
import prisma from "./database";
import { polarClient } from "./polar";

export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: "postgresql",
}),
emailAndPassword: {
enabled: true,
autoSignIn: true,
},
plugins: [
polar({
client: polarClient,
createCustomerOnSignUp: true,
use: [
checkout({
products: [
{
productId: "…",
slug: "pro",
},
],
authenticatedUsersOnly: true,
}),
portal(),
],
}),
],
});
`

Then integrate polar server and client plugins in better-auth configs. The build should break:

./node_modules/better-auth/dist/chunk-DieNfLhd.mjs

Code generation for chunk item errored
An error occurred while generating the chunk item [project]/node_modules/better-auth/dist/chunk-DieNfLhd.mjs [app-client] (ecmascript)

Caused by:

  • the chunking context (unknown) does not support external modules (request: node:module)

Debug info:

  • An error occurred while generating the chunk item [project]/node_modules/better-auth/dist/chunk-DieNfLhd.mjs [app-client] (ecmascript)
  • Execution of *EcmascriptChunkItemContent::module_factory failed
  • Execution of ::content_with_async_module_info failed
  • Execution of *EcmascriptChunkItemContent::new failed
  • Execution of EcmascriptModuleContent::new failed
  • the chunking context (unknown) does not support external modules (request: node:module)

Current vs. Expected behavior

What Does NOT Work
Client-side BetterAuth APIs (as documented)
await authClient.checkout();
await authClient.customer.portal();
await authClient.customer.state();

Issues encountered:

  • polarClient() cannot be safely imported on the client (breaks Turbopack)
  • Client APIs are undefined or missing
  • /api/auth/portal returns 404
  • Manually creating /api/auth/portal/route.ts does not help
  • Portal endpoint never registers despite portal() plugin being enabled

Expected Behavior
Based on the docs:
portal() plugin should expose a working portal endpoint
authClient.customer.portal() should redirect the user
authClient.customer.state() should return customer state
Client APIs should be usable in App Router environments

Actual Behavior
Client APIs cannot be used in Turbopack
Portal endpoints are unreachable
Checkout only works via server-side fetch
Documentation examples cannot be followed in App Router + Turbopack

What version of Better Auth are you using?

1.4.6

System info

Next.js: 15.5.9 (App Router)
Bundler: Turbopack
better-auth: 1.4.6
@polar-sh/better-auth: 1.6.1
@polar-sh/sdk: 0.41.5
React: 19.1.0
Database: Prisma (PostgreSQL)

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

Client

Auth config (if applicable)

// src/lib/auth.ts
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { polar, checkout, portal } from "@polar-sh/better-auth";
import prisma from "./database";
import { polarClient } from "./polar";

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql",
  }),
  emailAndPassword: {
    enabled: true,
    autoSignIn: true,
  },
  plugins: [
    polar({
      client: polarClient,
      createCustomerOnSignUp: true,
      use: [
        checkout({
          products: [
            {
              productId: "…",
              slug: "pro",
            },
          ],
          authenticatedUsersOnly: true,
        }),
        portal(),
      ],
    }),
  ],
});

Additional context

// src/app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { GET, POST } = toNextJsHandler(auth);

// src/lib/auth-client
import { createAuthClient } from "better-auth/react";
import { polarClient } from "@polar-sh/better-auth";
export const authClient = createAuthClient({
plugins: [polarClient()],
});

Originally created by @isaiahpere on GitHub (Dec 18, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6845 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Create next app with the following environment: Next.js: 15.5.9 (App Router) Bundler: Turbopack better-auth: 1.4.6 @polar-sh/better-auth: 1.6.1 @polar-sh/sdk: 0.41.5 React: 19.1.0 Database: Prisma (PostgreSQL) **Server Setup** `// src/lib/auth.ts import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { polar, checkout, portal } from "@polar-sh/better-auth"; import prisma from "./database"; import { polarClient } from "./polar"; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), emailAndPassword: { enabled: true, autoSignIn: true, }, plugins: [ polar({ client: polarClient, createCustomerOnSignUp: true, use: [ checkout({ products: [ { productId: "…", slug: "pro", }, ], authenticatedUsersOnly: true, }), portal(), ], }), ], }); ` Then integrate polar server and client plugins in better-auth configs. The build should break: ./node_modules/better-auth/dist/chunk-DieNfLhd.mjs Code generation for chunk item errored An error occurred while generating the chunk item [project]/node_modules/better-auth/dist/chunk-DieNfLhd.mjs [app-client] (ecmascript) Caused by: - the chunking context (unknown) does not support external modules (request: node:module) Debug info: - An error occurred while generating the chunk item [project]/node_modules/better-auth/dist/chunk-DieNfLhd.mjs [app-client] (ecmascript) - Execution of *EcmascriptChunkItemContent::module_factory failed - Execution of <ModuleChunkItem as EcmascriptChunkItem>::content_with_async_module_info failed - Execution of *EcmascriptChunkItemContent::new failed - Execution of EcmascriptModuleContent::new failed - the chunking context (unknown) does not support external modules (request: node:module) ### Current vs. Expected behavior What Does NOT Work ❌ Client-side BetterAuth APIs (as documented) await authClient.checkout(); await authClient.customer.portal(); await authClient.customer.state(); Issues encountered: - polarClient() cannot be safely imported on the client (breaks Turbopack) - Client APIs are undefined or missing - /api/auth/portal returns 404 - Manually creating /api/auth/portal/route.ts does not help - Portal endpoint never registers despite portal() plugin being enabled Expected Behavior Based on the docs: portal() plugin should expose a working portal endpoint authClient.customer.portal() should redirect the user authClient.customer.state() should return customer state Client APIs should be usable in App Router environments Actual Behavior Client APIs cannot be used in Turbopack Portal endpoints are unreachable Checkout only works via server-side fetch Documentation examples cannot be followed in App Router + Turbopack ### What version of Better Auth are you using? 1.4.6 ### System info ```bash Next.js: 15.5.9 (App Router) Bundler: Turbopack better-auth: 1.4.6 @polar-sh/better-auth: 1.6.1 @polar-sh/sdk: 0.41.5 React: 19.1.0 Database: Prisma (PostgreSQL) ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript // src/lib/auth.ts import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { polar, checkout, portal } from "@polar-sh/better-auth"; import prisma from "./database"; import { polarClient } from "./polar"; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), emailAndPassword: { enabled: true, autoSignIn: true, }, plugins: [ polar({ client: polarClient, createCustomerOnSignUp: true, use: [ checkout({ products: [ { productId: "…", slug: "pro", }, ], authenticatedUsersOnly: true, }), portal(), ], }), ], }); ``` ### Additional context // src/app/api/auth/[...all]/route.ts import { auth } from "@/lib/auth"; import { toNextJsHandler } from "better-auth/next-js"; export const { GET, POST } = toNextJsHandler(auth); // src/lib/auth-client import { createAuthClient } from "better-auth/react"; import { polarClient } from "@polar-sh/better-auth"; export const authClient = createAuthClient({ plugins: [polarClient()], });
GiteaMirror added the locked label 2026-04-15 18:09:54 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Dec 18, 2025):

(tag @better-auth-agent if you would like an answer from the Agent)

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3668216762 --> @better-auth-agent[bot] commented on GitHub (Dec 18, 2025): (tag @better-auth-agent if you would like an answer from the Agent) _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@dosubot[bot] commented on GitHub (Dec 18, 2025):

Hi @isaiahpere! I'm Dosu and I’m helping the better-auth team.

The issue is caused by the Polar BetterAuth client plugins importing Node.js-only modules (like node:async_hooks), which Turbopack and Next.js App Router cannot bundle for the client. This breaks client-side APIs such as authClient.checkout(), authClient.customer.portal(), and authClient.customer.state(), and prevents the portal endpoint from registering or being reachable. The problem persists across recent BetterAuth and Polar plugin versions and is documented in detail here.

Workarounds that have helped others include:

  • Importing polarClient from @polar-sh/better-auth/client instead of @polar-sh/better-auth for client-side usage. This can resolve some import errors, but may cause TypeScript issues or incomplete client APIs.
  • Creating a custom client plugin that avoids problematic imports and uses direct fetch calls to the relevant endpoints. Example workaround code is available in the linked issue.
  • Splitting server and client plugin code into separate files and importing client plugins directly from their client-specific paths, rather than through a shared index or re-export.

The Polar plugin documentation does not currently address Turbopack or App Router compatibility, and there is no official fix yet. For now, structuring your imports to strictly separate server and client code, and using custom client plugins or direct fetch calls, is the most reliable approach.

If you want to check your setup, make sure:

  • All client-side imports use the /client entry points.
  • No Node.js-only modules are referenced in client code.
  • Server and client plugin logic are kept in separate files.

You can follow the GitHub issue for updates on a long-term solution or official fix.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3668228823 --> @dosubot[bot] commented on GitHub (Dec 18, 2025): <!-- Greeting --> Hi @isaiahpere! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The issue is caused by the Polar BetterAuth client plugins importing Node.js-only modules (like `node:async_hooks`), which Turbopack and Next.js App Router cannot bundle for the client. This breaks client-side APIs such as `authClient.checkout()`, `authClient.customer.portal()`, and `authClient.customer.state()`, and prevents the portal endpoint from registering or being reachable. The problem persists across recent BetterAuth and Polar plugin versions and is documented in detail [here](https://github.com/better-auth/better-auth/issues/5539). Workarounds that have helped others include: - Importing `polarClient` from `@polar-sh/better-auth/client` instead of `@polar-sh/better-auth` for client-side usage. This can resolve some import errors, but may cause TypeScript issues or incomplete client APIs. - Creating a custom client plugin that avoids problematic imports and uses direct fetch calls to the relevant endpoints. Example workaround code is available in the linked issue. - Splitting server and client plugin code into separate files and importing client plugins directly from their client-specific paths, rather than through a shared index or re-export. The Polar plugin documentation does not currently address Turbopack or App Router compatibility, and there is no official fix yet. For now, structuring your imports to strictly separate server and client code, and using custom client plugins or direct fetch calls, is the most reliable approach. If you want to check your setup, make sure: - All client-side imports use the `/client` entry points. - No Node.js-only modules are referenced in client code. - Server and client plugin logic are kept in separate files. You can follow the GitHub issue for updates on a long-term solution or official fix. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/3c659201-bbe5-4db7-9a61-1951e5181c4b?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/3c659201-bbe5-4db7-9a61-1951e5181c4b?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/3c659201-bbe5-4db7-9a61-1951e5181c4b?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/3c659201-bbe5-4db7-9a61-1951e5181c4b?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/3c659201-bbe5-4db7-9a61-1951e5181c4b?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/3c659201-bbe5-4db7-9a61-1951e5181c4b?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/3c659201-bbe5-4db7-9a61-1951e5181c4b?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=The%20issue%20is%20caused%20by%20the%20Polar%20BetterAuth%20client%20plugins%20importing%20Node.js-only%20modules%20%28like%20%60node%3Aasync_hooks%60%29%2C%20which%20Turbopack%20and%20Next.js%20App%20Router%20cannot%20bundle%20for%20the%20client.%20This%20breaks%20client-side%20APIs%20such%20as%20%60authClient.checkout%28%29%60%2C%20%60authClient.customer.portal%28%29%60%2C%20and%20%60authClient.customer.state%28%29%60%2C%20and%20prevents%20the%20portal%20endpoint%20from%20registering%20or%20being%20reachable.%20The%20problem%20persists%20across%20recent%20BetterAuth%20and%20Polar%20plugin%20versions%20and%20is%20documented%20in%20detail%20%5Bhere%5D%28https%3A//github.com/better-auth/better-auth/issues/5539%29.%0A%0AWorkarounds%20that%20have%20helped%20others%20include%3A%0A%0A-%20Importing%20%60polarClient%60%20from%20%60%40polar-sh/better-auth/client%60%20instead%20of%20%60%40polar-sh/better-auth%60%20for%20client-side%20usage.%20This%20can%20resolve%20some%20import%20errors%2C%20but%20may%20cause%20TypeScript%20issues%20or%20incomplete%20client%20APIs.%0A-%20Creating%20a%20custom%20client%20plugin%20that%20avoids%20problematic%20imports%20and%20uses%20direct%20fetch%20calls%20to%20the%20relevant%20endpoints.%20Example%20workaround%20code%20is%20available%20in%20the%20linked%20issue.%0A-%20Splitting%20server%20and%20client%20plugin%20code%20into%20separate%20files%20and%20importing%20client%20plugins%20directly%20from%20their%20client-specific%20paths%2C%20rather%20than%20through%20a%20shared%20index%20or%20re-export.%0A%0AThe%20Polar%20plugin%20documentation%20does%20not%20currently%20address%20Turbopack%20or%20App%20Router%20compatibility%2C%20and%20there%20is%20no%20official%20fix%20yet.%20For%20now%2C%20structuring%20your%20imports%20to%20strictly%20separate%20server%20and%20client%20code%2C%20and%20using%20custom%20client%20plugins%20or%20direct%20fetch%20calls%2C%20is%20the%20most%20reliable%20approach.%0A%0AIf%20you%20want%20to%20check%20your%20setup%2C%20make%20sure%3A%0A-%20All%20client-side%20imports%20use%20the%20%60/client%60%20entry%20points.%0A-%20No%20Node.js-only%20modules%20are%20referenced%20in%20client%20code.%0A-%20Server%20and%20client%20plugin%20logic%20are%20kept%20in%20separate%20files.%0A%0AYou%20can%20follow%20the%20GitHub%20issue%20for%20updates%20on%20a%20long-term%20solution%20or%20official%20fix.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/6845)
Author
Owner

@isaiahpere commented on GitHub (Dec 18, 2025):

Perfect!

Importing polarClient from @polar-sh/better-auth/client did the trick.

Thanks and closing issue.

<!-- gh-comment-id:3668393713 --> @isaiahpere commented on GitHub (Dec 18, 2025): Perfect! Importing `polarClient` from `@polar-sh/better-auth/client` did the trick. Thanks and closing issue.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19282