Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat(tanstack): Pass Cloudflare Workers env context to getEnvVariable
On Cloudflare Workers, process.env and import.meta.env are not available.
This adds cloudflare:workers module env resolution to the TanStack Start
package, following the pattern used in @clerk/react-router where loader
context is passed to getEnvVariable().

- Add cloudflareEnv.ts with initCloudflareWorkerEnv() + getCloudflareWorkerEnv()
- Initialize CF env in clerkMiddleware (no-op on non-CF runtimes)
- Pass CF env context through commonEnvs() → getEnvVariable(name, context)
- Update getPublicEnvVariables to accept optional context

Related: #8197
Alternative approach to PR #8196 (shared-level fix)
  • Loading branch information
ChatJPTeasdale committed Mar 28, 2026
commit 34b89fa6cf4569f4d3c05f29e5ec9928618f3bcf
7 changes: 7 additions & 0 deletions .changeset/tanstack-cloudflare-env-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/tanstack-react-start': patch
---

feat(tanstack): Pass Cloudflare Workers env context to getEnvVariable

Adds `cloudflare:workers` module env resolution to the TanStack Start package, following the pattern used in `@clerk/react-router` where loader context is passed to `getEnvVariable()`. On Cloudflare Workers, `process.env` and `import.meta.env` are not available at runtime. This fix initializes the CF Workers env in `clerkMiddleware` and passes it through `commonEnvs()` → `getEnvVariable(name, context)` so `CLERK_SECRET_KEY` and other env vars are resolved correctly.
4 changes: 4 additions & 0 deletions packages/tanstack-react-start/src/server/clerkMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createMiddleware } from '@tanstack/react-start';

import { canUseKeyless } from '../utils/feature-flags';
import { clerkClient } from './clerkClient';
import { initCloudflareWorkerEnv } from './cloudflareEnv';
import { resolveKeysWithKeylessFallback } from './keyless/utils';
import { loadOptions } from './loadOptions';
import type { ClerkMiddlewareOptions, ClerkMiddlewareOptionsCallback } from './types';
Expand All @@ -16,6 +17,9 @@ export const clerkMiddleware = (
options?: ClerkMiddlewareOptions | ClerkMiddlewareOptionsCallback,
): AnyRequestMiddleware => {
return createMiddleware().server(async ({ request, next }) => {
// Initialize Cloudflare Workers env if available (no-op on non-CF runtimes)
await initCloudflareWorkerEnv();

const clerkRequest = createClerkRequest(patchRequest(request));

// Resolve options: if function, call it with context object; otherwise use as-is
Expand Down
40 changes: 40 additions & 0 deletions packages/tanstack-react-start/src/server/cloudflareEnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Attempts to resolve Cloudflare Worker environment variables.
*
* On Cloudflare Workers (e.g., TanStack Start with @cloudflare/vite-plugin),
* env vars are not available on `process.env` or `import.meta.env`. They are
* accessible via the `cloudflare:workers` module.
*
* Returns the env object if available, or undefined in non-CF environments.
* The result is cached after the first call.
*
* This follows the same pattern used in `@clerk/astro` (see PRs #7889, #8136),
* adapted for synchronous access after async initialization.
*/

let cachedEnv: Record<string, string> | null | undefined;

/**
* Initialize the Cloudflare Workers env cache.
* Call this once at startup (e.g., in middleware) before reading env vars.
*/
export async function initCloudflareWorkerEnv(): Promise<void> {
if (cachedEnv !== undefined) {
return;
}
try {
const moduleName = 'cloudflare:workers';
const mod = await import(/* @vite-ignore */ moduleName);
cachedEnv = mod.env ?? null;
} catch {
cachedEnv = null;
}
}

/**
* Returns the cached Cloudflare Workers env, or undefined if not available.
* Must call `initCloudflareWorkerEnv()` first.
*/
export function getCloudflareWorkerEnv(): Record<string, string> | undefined {
return cachedEnv ?? undefined;
}
22 changes: 13 additions & 9 deletions packages/tanstack-react-start/src/server/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import { apiUrlFromPublishableKey } from '@clerk/shared/apiUrlFromPublishableKey
import { getEnvVariable } from '@clerk/shared/getEnvVariable';

import { getPublicEnvVariables } from '../utils/env';
import { getCloudflareWorkerEnv } from './cloudflareEnv';

export const commonEnvs = () => {
const publicEnvs = getPublicEnvVariables();
export const commonEnvs = (context?: Record<string, any>) => {
// On Cloudflare Workers, resolve env from the runtime.
// Falls back to undefined on non-CF environments.
const cfEnv = context ?? getCloudflareWorkerEnv();
const publicEnvs = getPublicEnvVariables(cfEnv);

return {
// Public environment variables
Expand All @@ -23,17 +27,17 @@ export const commonEnvs = () => {
TELEMETRY_DEBUG: publicEnvs.telemetryDebug,

// Server-only environment variables
API_VERSION: getEnvVariable('CLERK_API_VERSION') || 'v1',
SECRET_KEY: getEnvVariable('CLERK_SECRET_KEY'),
MACHINE_SECRET_KEY: getEnvVariable('CLERK_MACHINE_SECRET_KEY'),
ENCRYPTION_KEY: getEnvVariable('CLERK_ENCRYPTION_KEY'),
CLERK_JWT_KEY: getEnvVariable('CLERK_JWT_KEY'),
API_URL: getEnvVariable('CLERK_API_URL') || apiUrlFromPublishableKey(publicEnvs.publishableKey),
API_VERSION: getEnvVariable('CLERK_API_VERSION', cfEnv) || 'v1',
SECRET_KEY: getEnvVariable('CLERK_SECRET_KEY', cfEnv),
MACHINE_SECRET_KEY: getEnvVariable('CLERK_MACHINE_SECRET_KEY', cfEnv),
ENCRYPTION_KEY: getEnvVariable('CLERK_ENCRYPTION_KEY', cfEnv),
CLERK_JWT_KEY: getEnvVariable('CLERK_JWT_KEY', cfEnv),
API_URL: getEnvVariable('CLERK_API_URL', cfEnv) || apiUrlFromPublishableKey(publicEnvs.publishableKey),

SDK_METADATA: {
name: PACKAGE_NAME,
version: PACKAGE_VERSION,
environment: getEnvVariable('NODE_ENV'),
environment: getEnvVariable('NODE_ENV', cfEnv),
},
} as const;
};
4 changes: 2 additions & 2 deletions packages/tanstack-react-start/src/utils/env.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getEnvVariable } from '@clerk/shared/getEnvVariable';
import { isTruthy } from '@clerk/shared/underscore';

export const getPublicEnvVariables = () => {
export const getPublicEnvVariables = (context?: Record<string, any>) => {
const getValue = (name: string): string => {
return getEnvVariable(`VITE_${name}`) || getEnvVariable(name);
return getEnvVariable(`VITE_${name}`, context) || getEnvVariable(name, context);
};

return {
Expand Down
Loading