Skip to content

Commit 7950346

Browse files
authored
fix(nuxt): derive apiUrl from publishable key like other SDKs (#8208)
1 parent 00715a6 commit 7950346

4 files changed

Lines changed: 105 additions & 3 deletions

File tree

.changeset/nuxt-derive-api-url.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clerk/nuxt": patch
3+
---
4+
5+
Derive `apiUrl` from the publishable key using `apiUrlFromPublishableKey()`, matching the behavior of other Clerk SDKs (`@clerk/nextjs`, `@clerk/astro`, etc.). Staging publishable keys (with `.accountsstage.dev`) now automatically route to `https://api.clerkstage.dev` without requiring a manual `NUXT_PUBLIC_CLERK_API_URL` override. Explicit `apiUrl` configuration still takes priority.

packages/nuxt/src/module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default defineNuxtModule<ModuleOptions>({
7474
// Backend specific variables that are safe to share.
7575
// We want them to be overridable like the other public keys (e.g NUXT_PUBLIC_CLERK_PROXY_URL)
7676
proxyUrl: options.proxyUrl,
77-
apiUrl: 'https://api.clerk.com',
77+
apiUrl: '',
7878
apiVersion: 'v1',
7979
},
8080
},
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { vi } from 'vitest';
2+
3+
// These globals are normally injected at build time by tsup.
4+
(globalThis as any).PACKAGE_NAME = '@clerk/nuxt';
5+
(globalThis as any).PACKAGE_VERSION = '0.0.0-test';
6+
7+
vi.mock('#imports', () => {
8+
return {
9+
useRuntimeConfig: vi.fn(),
10+
};
11+
});
12+
13+
vi.mock('@clerk/backend', () => {
14+
return {
15+
createClerkClient: vi.fn().mockReturnValue({}),
16+
};
17+
});
18+
19+
import { createClerkClient } from '@clerk/backend';
20+
21+
// @ts-expect-error: Nitro import. Handled by Nuxt.
22+
import { useRuntimeConfig } from '#imports';
23+
24+
import { clerkClient } from '../clerkClient';
25+
26+
const useRuntimeConfigMock = vi.mocked(useRuntimeConfig);
27+
const createClerkClientMock = vi.mocked(createClerkClient);
28+
29+
function mockRuntimeConfig(overrides: { publishableKey?: string; apiUrl?: string } = {}) {
30+
useRuntimeConfigMock.mockReturnValue({
31+
public: {
32+
clerk: {
33+
publishableKey: overrides.publishableKey ?? 'pk_test_Y2xlcmsuY2xlcmsuY29tJA',
34+
apiUrl: overrides.apiUrl ?? '',
35+
apiVersion: 'v1',
36+
proxyUrl: '',
37+
domain: '',
38+
isSatellite: false,
39+
telemetry: {},
40+
},
41+
},
42+
clerk: {
43+
secretKey: 'sk_test_xxx',
44+
machineSecretKey: '',
45+
jwtKey: '',
46+
},
47+
} as any);
48+
}
49+
50+
describe('clerkClient', () => {
51+
beforeEach(() => {
52+
vi.clearAllMocks();
53+
});
54+
55+
it('derives staging API URL from staging publishable key', () => {
56+
// pk_test_ + base64("safe-egret-46.clerk.accountsstage.dev$")
57+
const stagingPk = 'pk_test_c2FmZS1lZ3JldC00Ni5jbGVyay5hY2NvdW50c3N0YWdlLmRldiQ';
58+
mockRuntimeConfig({ publishableKey: stagingPk });
59+
60+
clerkClient({} as any);
61+
62+
expect(createClerkClientMock).toHaveBeenCalledWith(
63+
expect.objectContaining({
64+
apiUrl: 'https://api.clerkstage.dev',
65+
}),
66+
);
67+
});
68+
69+
it('uses production API URL for production publishable key', () => {
70+
const prodPk = 'pk_test_Y2xlcmsuY2xlcmsuY29tJA';
71+
mockRuntimeConfig({ publishableKey: prodPk });
72+
73+
clerkClient({} as any);
74+
75+
expect(createClerkClientMock).toHaveBeenCalledWith(
76+
expect.objectContaining({
77+
apiUrl: 'https://api.clerk.com',
78+
}),
79+
);
80+
});
81+
82+
it('prefers explicit apiUrl over derived value', () => {
83+
const stagingPk = 'pk_test_c2FmZS1lZ3JldC00Ni5jbGVyay5hY2NvdW50c3N0YWdlLmRldiQ';
84+
mockRuntimeConfig({ publishableKey: stagingPk, apiUrl: 'https://custom.api.example.com' });
85+
86+
clerkClient({} as any);
87+
88+
expect(createClerkClientMock).toHaveBeenCalledWith(
89+
expect.objectContaining({
90+
apiUrl: 'https://custom.api.example.com',
91+
}),
92+
);
93+
});
94+
});

packages/nuxt/src/runtime/server/clerkClient.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createClerkClient } from '@clerk/backend';
2+
import { apiUrlFromPublishableKey } from '@clerk/shared/apiUrlFromPublishableKey';
23
import { isTruthy } from '@clerk/shared/underscore';
34
import type { H3Event } from 'h3';
45

@@ -7,10 +8,12 @@ import { useRuntimeConfig } from '#imports';
78

89
export function clerkClient(event: H3Event) {
910
const runtimeConfig = useRuntimeConfig(event);
11+
const publishableKey = runtimeConfig.public.clerk.publishableKey;
12+
const apiUrl = runtimeConfig.public.clerk.apiUrl || apiUrlFromPublishableKey(publishableKey);
1013

1114
return createClerkClient({
12-
publishableKey: runtimeConfig.public.clerk.publishableKey,
13-
apiUrl: runtimeConfig.public.clerk.apiUrl,
15+
publishableKey,
16+
apiUrl,
1417
apiVersion: runtimeConfig.public.clerk.apiVersion,
1518
proxyUrl: runtimeConfig.public.clerk.proxyUrl,
1619
domain: runtimeConfig.public.clerk.domain,

0 commit comments

Comments
 (0)