Skip to content

Commit 8d424da

Browse files
brkalowclaude
andcommitted
feat: Align Express FrontendApiProxyOptions.enabled with Next.js
Accept `boolean | ShouldProxyFn` for the enabled field, matching the Next.js interface. The function form receives the request URL and allows per-request proxy decisions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4a3b210 commit 8d424da

2 files changed

Lines changed: 33 additions & 19 deletions

File tree

packages/express/src/authenticateRequest.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ export const authenticateAndDecorateRequest = (options: ClerkMiddlewareOptions =
105105

106106
// Extract proxy configuration
107107
const frontendApiProxy = options.frontendApiProxy;
108-
const proxyEnabled = frontendApiProxy?.enabled === true;
109108
const proxyPath = stripTrailingSlashes(frontendApiProxy?.path ?? DEFAULT_PROXY_PATH);
110109

111110
// eslint-disable-next-line @typescript-eslint/no-misused-promises
@@ -119,9 +118,14 @@ export const authenticateAndDecorateRequest = (options: ClerkMiddlewareOptions =
119118
const secretKey = options.secretKey || env.secretKey;
120119

121120
// Handle Frontend API proxy requests early, before authentication
122-
if (proxyEnabled) {
123-
const requestPath = new URL(request.originalUrl || request.url, `http://${request.headers.host}`).pathname;
124-
if (requestPath === proxyPath || requestPath.startsWith(proxyPath + '/')) {
121+
if (frontendApiProxy) {
122+
const requestUrl = new URL(request.originalUrl || request.url, `http://${request.headers.host}`);
123+
const isEnabled =
124+
typeof frontendApiProxy.enabled === 'function'
125+
? frontendApiProxy.enabled(requestUrl)
126+
: frontendApiProxy.enabled;
127+
128+
if (isEnabled && (requestUrl.pathname === proxyPath || requestUrl.pathname.startsWith(proxyPath + '/'))) {
125129
// Convert Express request to Fetch API Request
126130
const proxyRequest = requestToProxyRequest(request);
127131

@@ -164,18 +168,25 @@ export const authenticateAndDecorateRequest = (options: ClerkMiddlewareOptions =
164168

165169
// Auto-derive proxyUrl from frontendApiProxy config if not explicitly set
166170
let resolvedOptions = options;
167-
if (proxyEnabled && !options.proxyUrl) {
168-
const forwardedProto = request.headers['x-forwarded-proto'];
169-
const protoHeader = Array.isArray(forwardedProto) ? forwardedProto[0] : forwardedProto;
170-
const proto = (protoHeader || '').split(',')[0].trim();
171-
const protocol = request.secure || proto === 'https' ? 'https' : 'http';
172-
173-
const forwardedHost = request.headers['x-forwarded-host'];
174-
const hostHeader = Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost;
175-
const host = (hostHeader || '').split(',')[0].trim() || request.headers.host || 'localhost';
176-
177-
const derivedProxyUrl = `${protocol}://${host}${proxyPath}`;
178-
resolvedOptions = { ...options, proxyUrl: derivedProxyUrl };
171+
if (frontendApiProxy && !options.proxyUrl) {
172+
const requestUrl = new URL(request.originalUrl || request.url, `http://${request.headers.host}`);
173+
const isProxyEnabled =
174+
typeof frontendApiProxy.enabled === 'function'
175+
? frontendApiProxy.enabled(requestUrl)
176+
: frontendApiProxy.enabled;
177+
if (isProxyEnabled) {
178+
const forwardedProto = request.headers['x-forwarded-proto'];
179+
const protoHeader = Array.isArray(forwardedProto) ? forwardedProto[0] : forwardedProto;
180+
const proto = (protoHeader || '').split(',')[0].trim();
181+
const protocol = request.secure || proto === 'https' ? 'https' : 'http';
182+
183+
const forwardedHost = request.headers['x-forwarded-host'];
184+
const hostHeader = Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost;
185+
const host = (hostHeader || '').split(',')[0].trim() || request.headers.host || 'localhost';
186+
187+
const derivedProxyUrl = `${protocol}://${host}${proxyPath}`;
188+
resolvedOptions = { ...options, proxyUrl: derivedProxyUrl };
189+
}
179190
}
180191

181192
try {

packages/express/src/types.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { createClerkClient } from '@clerk/backend';
22
import type { AuthenticateRequestOptions, SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend/internal';
3+
import type { ShouldProxyFn } from '@clerk/shared/proxy';
34
import type { PendingSessionOptions } from '@clerk/shared/types';
45
import type { Request as ExpressRequest } from 'express';
56

@@ -12,10 +13,12 @@ export type ExpressRequestWithAuth = ExpressRequest & {
1213
*/
1314
export interface FrontendApiProxyOptions {
1415
/**
15-
* Enable Frontend API proxy handling. When true, requests to the proxy path
16-
* will be proxied to Clerk's Frontend API and the proxyUrl will be auto-derived.
16+
* Enable proxy handling. Can be:
17+
* - `true` - enable for all domains
18+
* - `false` - disable for all domains
19+
* - A function: (url: URL) => boolean - enable based on the request URL
1720
*/
18-
enabled: boolean;
21+
enabled: boolean | ShouldProxyFn;
1922
/**
2023
* The path prefix for proxy requests.
2124
*

0 commit comments

Comments
 (0)