Skip to content

Commit

Permalink
fix(nextjs): Handle dynamicIO errors when request apis are accessed o…
Browse files Browse the repository at this point in the history
…n prerender (#4836)
  • Loading branch information
panteliselef authored Jan 14, 2025
1 parent 7182b93 commit cce371f
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/brown-kids-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/nextjs': patch
---

Handle `dynamicIO` errors when request apis are accessed on prerender. This fixes issues with `ppr: true, dynamicIO: true` when using `<ClerkProvider dynamic/>`.
39 changes: 25 additions & 14 deletions packages/nextjs/src/app-router/server/ClerkProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { AuthObject } from '@clerk/backend';
import type { InitialState, Without } from '@clerk/types';
import { headers } from 'next/headers';
import React from 'react';
Expand Down Expand Up @@ -27,21 +26,33 @@ export async function ClerkProvider(
props: Without<NextClerkProviderProps, '__unstable_invokeMiddlewareOnAuthStateChange'>,
) {
const { children, dynamic, ...rest } = props;
let statePromise: Promise<null | AuthObject> = Promise.resolve(null);
let nonce = Promise.resolve('');

if (dynamic) {
async function generateStatePromise() {
if (!dynamic) {
return Promise.resolve(null);
}
if (isNext13) {
/**
* For some reason, Next 13 requires that functions which call `headers()` are awaited where they are invoked.
* Without the await here, Next will throw a DynamicServerError during build.
*/
return Promise.resolve(await getDynamicClerkState());
}
return getDynamicClerkState();
}

async function generateNonce() {
if (!dynamic) {
return Promise.resolve('');
}
if (isNext13) {
/**
* For some reason, Next 13 requires that functions which call `headers()` are awaited where they are invoked.
* Without the await here, Next will throw a DynamicServerError during build.
*/
statePromise = Promise.resolve(await getDynamicClerkState());
nonce = Promise.resolve(await getNonceFromCSPHeader());
} else {
statePromise = getDynamicClerkState();
nonce = getNonceFromCSPHeader();
return Promise.resolve(await getNonceFromCSPHeader());
}
return getNonceFromCSPHeader();
}

const propsWithEnvs = mergeNextClerkPropsWithEnv({
Expand All @@ -51,8 +62,8 @@ export async function ClerkProvider(
let output = (
<ClientClerkProvider
{...mergeNextClerkPropsWithEnv(rest)}
nonce={await nonce}
initialState={await statePromise}
nonce={await generateNonce()}
initialState={await generateStatePromise()}
>
{children}
</ClientClerkProvider>
Expand All @@ -75,8 +86,8 @@ export async function ClerkProvider(
__internal_claimKeylessApplicationUrl: newOrReadKeys.claimUrl,
__internal_copyInstanceKeysUrl: newOrReadKeys.apiKeysUrl,
})}
nonce={await nonce}
initialState={await statePromise}
nonce={await generateNonce()}
initialState={await generateStatePromise()}
>
{children}
</ClientClerkProvider>
Expand All @@ -88,7 +99,7 @@ export async function ClerkProvider(
if (dynamic) {
return (
// TODO: fix types so AuthObject is compatible with InitialState
<PromisifiedAuthProvider authPromise={statePromise as unknown as Promise<InitialState>}>
<PromisifiedAuthProvider authPromise={generateStatePromise() as unknown as Promise<InitialState>}>
{output}
</PromisifiedAuthProvider>
);
Expand Down
4 changes: 2 additions & 2 deletions packages/nextjs/src/app-router/server/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const isPrerenderingBailout = (e: unknown) => {
return routeRegex.test(message) || dynamicServerUsage || bailOutPrerendering;
};

export async function buildRequestLike() {
export async function buildRequestLike(): Promise<NextRequest> {
try {
// Dynamically import next/headers, otherwise Next12 apps will break
// @ts-expect-error: Cannot find module 'next/headers' or its corresponding type declarations.ts(2307)
Expand Down Expand Up @@ -67,7 +67,7 @@ export function getScriptNonceFromHeader(cspHeaderValue: string): string | undef
// Grab the nonce by trimming the 'nonce-' prefix.
?.slice(7, -1);

// If we could't find the nonce, then we're done.
// If we couldn't find the nonce, then we're done.
if (!nonce) {
return;
}
Expand Down

0 comments on commit cce371f

Please sign in to comment.