Skip to content

feat(backend): Add Frontend API proxy helpers#7602

Merged
brkalow merged 26 commits intomainfrom
brkalow/clerk-proxy-helper
Feb 13, 2026
Merged

feat(backend): Add Frontend API proxy helpers#7602
brkalow merged 26 commits intomainfrom
brkalow/clerk-proxy-helper

Conversation

@brkalow
Copy link
Member

@brkalow brkalow commented Jan 15, 2026

Description

Adds Frontend API proxy support to clerkMiddleware for both Next.js and Express. This enables scenarios where direct communication with Clerk's API is blocked or needs to go through the application server.

API

Next.js

import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware({
  // Enable proxy with defaults (path: '/__clerk')
  frontendApiProxy: {
    enabled: true,
  },
});

// Or with multi-domain support using a function
export default clerkMiddleware({
  frontendApiProxy: {
    enabled: (url) => url.hostname === 'app.example.com',
  },
});

// Custom proxy path
export default clerkMiddleware({
  frontendApiProxy: {
    enabled: true,
    path: '/custom-clerk-proxy',
  },
});

Express

import express from 'express';
import { clerkMiddleware } from '@clerk/express';

const app = express();

// Enable proxy with defaults (path: '/__clerk')
app.use(clerkMiddleware({ frontendApiProxy: { enabled: true } }));

// Custom proxy path
app.use(clerkMiddleware({ frontendApiProxy: { enabled: true, path: '/my-proxy' } }));

// Disable proxy
app.use(clerkMiddleware({ frontendApiProxy: { enabled: false } }));

Key Features

  • Embedded in clerkMiddleware: No separate middleware needed - proxy handling is built into clerkMiddleware
  • Auto-derived proxyUrl: The proxyUrl for handshake redirects is automatically derived from the frontendApiProxy config
  • Multi-domain support (Next.js): Use a function for enabled to conditionally enable proxy based on request URL
  • Streaming body support: Request and response bodies are streamed for efficient handling
  • Header rewriting: Redirect responses are automatically rewritten to use the proxy URL

Type of change

  • 🌟 New feature

Testing

  • Unit tests added for proxy functionality in both Next.js and Express
  • Tests cover: proxy path matching, proxyUrl derivation, multi-domain support, header handling

Summary by CodeRabbit

  • New Features

    • Frontend API proxy added across backend, Next.js, and Express with configurable proxy path, automatic proxy URL derivation, and App Router handler helpers.
  • Behavior

    • Proxy streams requests/responses, preserves queries/headers, manages X-Forwarded headers, strips hop-by-hop headers, and rewrites internal redirects.
  • Tests

    • Extensive tests for routing, header propagation, error handling, redirects, and multi-domain scenarios.
  • Documentation

    • Changeset documenting the Frontend API proxy feature.

…s, and @clerk/express

Implement clerkProxy helper that abstracts away the complexity of proxying Clerk's Frontend API (FAPI) requests. This enables scenarios where direct communication with Clerk's API is blocked or needs to go through the application server.

- Core proxy implementation in @clerk/backend/src/proxy.ts with environment-aware URL derivation
- Next.js integration via clerkMiddleware frontendApiProxy option and route handlers
- Express middleware for handling proxy requests with body streaming support
- FAPI URL constants added to @clerk/shared for environment detection

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Jan 15, 2026

🦋 Changeset detected

Latest commit: adaf188

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 20 packages
Name Type
@clerk/backend Minor
@clerk/nextjs Minor
@clerk/express Minor
@clerk/shared Minor
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/fastify Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/chrome-extension Patch
@clerk/clerk-js Patch
@clerk/expo-passkeys Patch
@clerk/expo Patch
@clerk/localizations Patch
@clerk/msw Patch
@clerk/react Patch
@clerk/ui Patch
@clerk/vue Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Jan 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Feb 13, 2026 4:10pm

Request Review

- Add comprehensive tests for @clerk/backend/proxy including FAPI URL derivation, path matching, and request forwarding
- Add tests for @clerk/nextjs/proxy route handlers and exports
- Add tests for @clerk/express/proxy middleware and request conversion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Next.js proxy tests mocked the underlying @clerk/backend/proxy,
making them essentially test that wrapper A calls function B - no real
behavior was verified. The backend proxy tests provide actual coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@brkalow brkalow changed the title feat: Add Frontend API proxy helpers feat(backend): Add Frontend API proxy helpers Jan 15, 2026
Allow the `enabled` option in `frontendApiProxy` to accept a function
`(url: URL) => boolean` for conditional proxy based on the request URL.

This enables scenarios where an application has multiple domains and
only some require proxying (e.g., `foo.replit.app` proxied while
`foo.com` uses direct FAPI access).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add X-Forwarded-Host and X-Forwarded-Proto headers for proxy awareness
- Preserve existing X-Forwarded-* headers from upstream proxies
- Rewrite Location headers for FAPI redirects to go through the proxy
- Add tests for new header handling and redirect rewriting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…t.js

- Remove separate @clerk/express/proxy entry point and middleware
- Embed proxy handling directly in Express clerkMiddleware
- Auto-derive proxyUrl from frontendApiProxy config for handshake redirects
- Add FrontendApiProxyOptions type with enabled and path options
- Align API structure between Express and Next.js SDKs
- Remove low-value constant tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change from defaulting enabled to true, to requiring explicit enabled: true
- Update tests to use enabled: true
- Update JSDoc to remove default annotation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes issue where paths like /__clerk-admin would incorrectly match
the /__clerk proxy path. Now requires either an exact match or a
trailing slash boundary.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ensures that proxy paths like /__clerk/ work correctly by stripping
trailing slashes before matching.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…g changeset

- Update exports snapshot to include new proxy exports (DEFAULT_PROXY_PATH, clerkFrontendApiProxy, createFrontendApiProxyHandlers)
- Fix proxyUrl test assertion to check for falsy value instead of key absence (handleMultiDomainAndProxy always returns proxyUrl: "")
- Add changeset for frontend API proxy feature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/nextjs/src/server/index.ts`:
- Around line 78-87: Remove the new barrel re-exports from index.ts: delete the
export block that re-exports clerkFrontendApiProxy,
createFrontendApiProxyHandlers and the types FrontendApiProxyHandlers,
FrontendApiProxyOptions, NextFrontendApiProxyOptions; instead ensure
consumers/importers use the dedicated module path './proxy' directly (or add a
new explicit entrypoint file if needed) and update any internal imports to
reference './proxy' rather than the package root to avoid expanding the barrel
and preventing possible circular dependencies.

- Fix simple-import-sort lint errors in authenticateRequest.ts and utils.ts
- Clear authenticateRequestMock between tests to prevent stale call counts
  from causing `.not.toBeCalled()` assertions to fail in proxy tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/express/src/authenticateRequest.ts`:
- Around line 132-136: The proxy path check in authenticateRequest.ts fails when
request.originalUrl contains a query string; update the proxy matching logic to
extract only the pathname (strip the query string) from request.originalUrl ||
request.url before comparing to proxyPath. In practice, compute a
requestPathName (or reuse request.path) that excludes the query string, then use
that value in the existing if checks that reference proxyEnabled, requestPath
(or requestPathName) and proxyPath (including the startsWith(proxyPath + '/')
check).
- Around line 176-188: The code that auto-derives proxyUrl when proxyEnabled and
!options.proxyUrl uses request.headers['x-forwarded-proto'] and
request.headers['x-forwarded-host'] directly, which fails for comma-separated
header chains; update the extraction in the resolvedOptions block (around
proxyEnabled, options.proxyUrl, resolvedOptions, derivedProxyUrl, proxyPath) to
split the header value on ',' and take the first item, trimming whitespace (e.g.
const protoHeader = Array.isArray(forwardedProto) ? forwardedProto[0] :
forwardedProto; const protoValue = (protoHeader || '').split(',')[0].trim(); and
same for host) before computing protocol, host and derivedProxyUrl so the first
forwarded value is used.

…aders

Split header values on ',' and take the first item to handle
multi-proxy chains (e.g. "https, http" or "app.example.com, proxy.example.com").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
request.originalUrl includes query parameters, causing proxy path matching
to fail for requests like /__clerk?_clerk_js_version=5.0.0. Parse the URL
to extract pathname before comparing, consistent with the backend's
matchProxyPath implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Removes trailing slashes from a string without using regex
* to avoid potential ReDoS concerns flagged by security scanners.
*/
function stripTrailingSlashes(str: string): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] I think this might be duped

* Enable Frontend API proxy handling. When true, requests to the proxy path
* will be proxied to Clerk's Frontend API and the proxyUrl will be auto-derived.
*/
enabled?: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] I think it would make sense to make this a required configuration value so it's easy to grok if the feature is on or off

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jacekradko required if frontendApiProxy is present right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

'@clerk/backend': minor
'@clerk/nextjs': minor
'@clerk/express': minor
---
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
---
'@clerk/shared': minor
---

Copy link
Member

@jacekradko jacekradko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just minor comments

- Remove duplicate stripTrailingSlashes from express, import from backend
- Make FrontendApiProxyOptions.enabled required for clarity
- Add @clerk/shared to changeset

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
brkalow added a commit to clerk/clerk-docs that referenced this pull request Feb 12, 2026
Documents the new built-in Frontend API proxy support added to
clerkMiddleware for Next.js and Express. Updates the proxy guide
with simplified setup examples and adds reference documentation
for the new option, including multi-domain support and App Router
route handler helpers.

Impl: clerk/javascript#7602

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…elper

# Conflicts:
#	packages/nextjs/src/server/__tests__/clerkMiddleware.test.ts
@brkalow brkalow merged commit 7772f45 into main Feb 13, 2026
39 checks passed
@brkalow brkalow deleted the brkalow/clerk-proxy-helper branch February 13, 2026 16:23
wobsoriano pushed a commit that referenced this pull request Feb 13, 2026
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants