Skip to content

Commit

Permalink
fix(remix): Adjust RootAuthLoaderCallbackReturn to allow returning nu…
Browse files Browse the repository at this point in the history
…ll (#1704)
  • Loading branch information
BRKalow authored Sep 14, 2023
1 parent 7fa9364 commit 7eea924
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/proud-crabs-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/remix': patch
---

Adjust return type of `rootAuthLoader`'s callback to allow returning `null`.
21 changes: 17 additions & 4 deletions packages/remix/src/ssr/rootAuthLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { invalidRootLoaderCallbackReturn } from '../errors';
import { authenticateRequest } from './authenticateRequest';
import type { LoaderFunctionArgs, LoaderFunctionReturn, RootAuthLoaderCallback, RootAuthLoaderOptions } from './types';
import {
assertObject,
assertValidHandlerResult,
injectAuthIntoRequest,
injectRequestStateIntoResponse,
interstitialJsonResponse,
Expand All @@ -15,14 +15,25 @@ import {

interface RootAuthLoader {
<Options extends RootAuthLoaderOptions, Callback extends RootAuthLoaderCallback<Options>>(
/**
* Arguments passed to the loader function.
*/
args: LoaderFunctionArgs,
/**
* A loader function with authentication state made available to it. Allows you to fetch route data based on the user's authentication state.
*/
callback: Callback,
options?: Options,
): Promise<ReturnType<Callback>>;

(args: LoaderFunctionArgs, options?: RootAuthLoaderOptions): Promise<LoaderFunctionReturn>;
}

/**
* Makes authorization state available in your application by wrapping the root loader.
*
* @see https://clerk.com/docs/quickstarts/remix
*/
export const rootAuthLoader: RootAuthLoader = async (
args: LoaderFunctionArgs,
handlerOrOptions: any,
Expand Down Expand Up @@ -51,7 +62,7 @@ export const rootAuthLoader: RootAuthLoader = async (
}

const handlerResult = await handler(injectAuthIntoRequest(args, sanitizeAuthObject(requestState.toAuth())));
assertObject(handlerResult, invalidRootLoaderCallbackReturn);
assertValidHandlerResult(handlerResult, invalidRootLoaderCallbackReturn);

if (isResponse(handlerResult)) {
try {
Expand All @@ -67,6 +78,8 @@ export const rootAuthLoader: RootAuthLoader = async (
}
}

// if the user returned a plain object, create an empty response and inject requestState and handlerResult
return injectRequestStateIntoResponse(new Response(JSON.stringify(handlerResult)), requestState);
// if the return value of the user's handler is null or a plain object, create an empty response to inject Clerk's state into
const responseBody = JSON.stringify(handlerResult ?? {});

return injectRequestStateIntoResponse(new Response(responseBody), requestState);
};
13 changes: 8 additions & 5 deletions packages/remix/src/ssr/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ export type RootAuthLoaderCallback<Options extends RootAuthLoaderOptions> = (
args: LoaderFunctionArgsWithAuth<Options>,
) => RootAuthLoaderCallbackReturn;

export type RootAuthLoaderCallbackReturn =
| Promise<Response>
| Response
| Promise<Record<string, unknown>>
| Record<string, unknown>;
type ObjectLike = Record<string, unknown> | null;

/**
* We are not using `LoaderFunctionReturn` here because we can't support non-object return values. We need to be able to decorate the return value with authentication state, and so we need something object-like.
*
* In the case of `null`, we will return an object containing only the authentication state.
*/
export type RootAuthLoaderCallbackReturn = Promise<Response> | Response | Promise<ObjectLike> | ObjectLike;

export type LoaderFunctionArgs = DataFunctionArgs;
export type LoaderFunctionReturn = ReturnType<LoaderFunction>;
Expand Down
4 changes: 2 additions & 2 deletions packages/remix/src/ssr/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export const parseCookies = (req: Request) => {
return cookie.parse(req.headers.get('cookie') || '');
};

export function assertObject(val: any, error?: string): asserts val is Record<string, unknown> {
if (!val || typeof val !== 'object' || Array.isArray(val)) {
export function assertValidHandlerResult(val: any, error?: string): asserts val is Record<string, unknown> | null {
if ((val !== null && typeof val !== 'object') || Array.isArray(val)) {
throw new Error(error || '');
}
}
Expand Down

0 comments on commit 7eea924

Please sign in to comment.