From 12decdeb27e75a39745948897385cab952080167 Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 6 Jul 2026 10:11:06 -0500 Subject: [PATCH 1/2] chore(nuxt): Deprecate createRouteMatcher --- .changeset/nuxt-deprecate-route-matcher.md | 33 +++++++++++++++++++ .../client/__tests__/routeMatcher.test.ts | 26 +++++++++++++++ .../nuxt/src/runtime/client/routeMatcher.ts | 22 +++++++++++++ .../server/__tests__/routeMatcher.test.ts | 31 +++++++++++++++++ .../nuxt/src/runtime/server/routeMatcher.ts | 22 +++++++++++++ 5 files changed, 134 insertions(+) create mode 100644 .changeset/nuxt-deprecate-route-matcher.md create mode 100644 packages/nuxt/src/runtime/client/__tests__/routeMatcher.test.ts create mode 100644 packages/nuxt/src/runtime/server/__tests__/routeMatcher.test.ts diff --git a/.changeset/nuxt-deprecate-route-matcher.md b/.changeset/nuxt-deprecate-route-matcher.md new file mode 100644 index 00000000000..3a5fb9d955d --- /dev/null +++ b/.changeset/nuxt-deprecate-route-matcher.md @@ -0,0 +1,33 @@ +--- +'@clerk/nuxt': patch +--- + +Deprecate `createRouteMatcher()` in favor of Nuxt's native route matching. + +To protect API routes, match paths natively inside `clerkMiddleware()`: + +```ts +import { clerkMiddleware } from '@clerk/nuxt/server'; + +export default clerkMiddleware(event => { + const { isAuthenticated } = event.context.auth(); + const { pathname } = getRequestURL(event); + + if (!isAuthenticated && pathname.startsWith('/api/admin')) { + throw createError({ statusCode: 401, statusMessage: 'Unauthorized' }); + } +}); +``` + +To protect pages, use Nuxt's built-in route middleware: create a named middleware that checks the user's authentication status and opt pages into it with `definePageMeta({ middleware: 'auth' })`. Child routes inherit the middleware applied to their parent, so a single declaration can protect a whole section. + +```ts +// app/middleware/auth.ts +export default defineNuxtRouteMiddleware(() => { + const { isSignedIn } = useAuth(); + + if (!isSignedIn.value) { + return navigateTo('/sign-in'); + } +}); +``` diff --git a/packages/nuxt/src/runtime/client/__tests__/routeMatcher.test.ts b/packages/nuxt/src/runtime/client/__tests__/routeMatcher.test.ts new file mode 100644 index 00000000000..7fd4551311c --- /dev/null +++ b/packages/nuxt/src/runtime/client/__tests__/routeMatcher.test.ts @@ -0,0 +1,26 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { createRouteMatcher } from '../routeMatcher'; + +const { mockDeprecated } = vi.hoisted(() => ({ + mockDeprecated: vi.fn(), +})); + +vi.mock('@clerk/shared/deprecated', () => ({ + deprecated: mockDeprecated, +})); + +describe('createRouteMatcher', () => { + beforeEach(() => { + mockDeprecated.mockClear(); + }); + + it('should emit a deprecation warning when called', () => { + createRouteMatcher(['/dashboard(.*)']); + + expect(mockDeprecated).toHaveBeenCalledWith( + 'createRouteMatcher', + "Use Nuxt's built-in route middleware to protect pages instead: create a named middleware that checks the user's authentication status and opt pages into it with `definePageMeta({ middleware: 'auth' })`.", + ); + }); +}); diff --git a/packages/nuxt/src/runtime/client/routeMatcher.ts b/packages/nuxt/src/runtime/client/routeMatcher.ts index cf3ec0471fe..1758f1f5002 100644 --- a/packages/nuxt/src/runtime/client/routeMatcher.ts +++ b/packages/nuxt/src/runtime/client/routeMatcher.ts @@ -1,3 +1,4 @@ +import { deprecated } from '@clerk/shared/deprecated'; import { createPathMatcher, type PathMatcherParam } from '@clerk/shared/pathMatcher'; import type { RouteMiddleware } from 'nuxt/app'; @@ -14,8 +15,29 @@ export type RouteMatcherParam = PathMatcherParam; * ['/foo', '/bar(.*)'] * @example * [/^\/foo\/.*$/] + * + * @deprecated This function will be removed in the next major version. Use Nuxt's built-in + * route middleware to protect pages instead: create a named middleware that checks the user's + * authentication status and opt pages into it with `definePageMeta()`. Child routes inherit + * the middleware applied to their parent, so a single declaration can protect a whole section. + * + * ```ts + * // app/middleware/auth.ts + * export default defineNuxtRouteMiddleware(() => { + * const { isSignedIn } = useAuth(); + * + * if (!isSignedIn.value) { + * return navigateTo('/sign-in'); + * } + * }); + * ``` */ export const createRouteMatcher = (routes: RouteMatcherParam) => { + deprecated( + 'createRouteMatcher', + "Use Nuxt's built-in route middleware to protect pages instead: create a named middleware that checks the user's authentication status and opt pages into it with `definePageMeta({ middleware: 'auth' })`.", + ); + const matcher = createPathMatcher(routes); return (to: RouteLocation) => matcher(to.path); }; diff --git a/packages/nuxt/src/runtime/server/__tests__/routeMatcher.test.ts b/packages/nuxt/src/runtime/server/__tests__/routeMatcher.test.ts new file mode 100644 index 00000000000..20bb76ff391 --- /dev/null +++ b/packages/nuxt/src/runtime/server/__tests__/routeMatcher.test.ts @@ -0,0 +1,31 @@ +import { getRequestURL } from 'h3'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { createRouteMatcher } from '../routeMatcher'; + +const { mockDeprecated } = vi.hoisted(() => ({ + mockDeprecated: vi.fn(), +})); + +vi.mock('@clerk/shared/deprecated', () => ({ + deprecated: mockDeprecated, +})); + +vi.mock('#imports', () => ({ + getRequestURL, +})); + +describe('createRouteMatcher', () => { + beforeEach(() => { + mockDeprecated.mockClear(); + }); + + it('should emit a deprecation warning when called', () => { + createRouteMatcher(['/api/admin(.*)']); + + expect(mockDeprecated).toHaveBeenCalledWith( + 'createRouteMatcher', + "Match API route paths natively inside `clerkMiddleware()` instead, for example: `getRequestURL(event).pathname.startsWith('/api/admin')`. To protect pages, use Nuxt's built-in route middleware.", + ); + }); +}); diff --git a/packages/nuxt/src/runtime/server/routeMatcher.ts b/packages/nuxt/src/runtime/server/routeMatcher.ts index b949cf19133..7509cf09e13 100644 --- a/packages/nuxt/src/runtime/server/routeMatcher.ts +++ b/packages/nuxt/src/runtime/server/routeMatcher.ts @@ -1,3 +1,4 @@ +import { deprecated } from '@clerk/shared/deprecated'; import type { PathMatcherParam } from '@clerk/shared/pathMatcher'; import { createPathMatcher } from '@clerk/shared/pathMatcher'; import type { H3Event } from 'h3'; @@ -15,8 +16,29 @@ export type RouteMatcherParam = PathMatcherParam; * ['/foo', '/bar(.*)'] * @example * [/^\/foo\/.*$/] + * + * @deprecated This function will be removed in the next major version. Match API route paths natively + * inside `clerkMiddleware()` instead, and protect pages with Nuxt's built-in route middleware: + * + * ```ts + * import { clerkMiddleware } from '@clerk/nuxt/server'; + * + * export default clerkMiddleware(event => { + * const { isAuthenticated } = event.context.auth(); + * const { pathname } = getRequestURL(event); + * + * if (!isAuthenticated && pathname.startsWith('/api/admin')) { + * throw createError({ statusCode: 401, statusMessage: 'Unauthorized' }); + * } + * }); + * ``` */ export const createRouteMatcher = (routes: RouteMatcherParam) => { + deprecated( + 'createRouteMatcher', + "Match API route paths natively inside `clerkMiddleware()` instead, for example: `getRequestURL(event).pathname.startsWith('/api/admin')`. To protect pages, use Nuxt's built-in route middleware.", + ); + const matcher = createPathMatcher(routes); return (event: H3Event) => { const url = getRequestURL(event); From 6f1ee33d2e80cf0d7cf7f967542d60782e6d8f51 Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 6 Jul 2026 10:42:04 -0500 Subject: [PATCH 2/2] chore(nuxt): Trim changeset --- .changeset/nuxt-deprecate-route-matcher.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.changeset/nuxt-deprecate-route-matcher.md b/.changeset/nuxt-deprecate-route-matcher.md index 3a5fb9d955d..f297891d51a 100644 --- a/.changeset/nuxt-deprecate-route-matcher.md +++ b/.changeset/nuxt-deprecate-route-matcher.md @@ -7,8 +7,6 @@ Deprecate `createRouteMatcher()` in favor of Nuxt's native route matching. To protect API routes, match paths natively inside `clerkMiddleware()`: ```ts -import { clerkMiddleware } from '@clerk/nuxt/server'; - export default clerkMiddleware(event => { const { isAuthenticated } = event.context.auth(); const { pathname } = getRequestURL(event); @@ -19,15 +17,4 @@ export default clerkMiddleware(event => { }); ``` -To protect pages, use Nuxt's built-in route middleware: create a named middleware that checks the user's authentication status and opt pages into it with `definePageMeta({ middleware: 'auth' })`. Child routes inherit the middleware applied to their parent, so a single declaration can protect a whole section. - -```ts -// app/middleware/auth.ts -export default defineNuxtRouteMiddleware(() => { - const { isSignedIn } = useAuth(); - - if (!isSignedIn.value) { - return navigateTo('/sign-in'); - } -}); -``` +To protect pages, use Nuxt's built-in route middleware with `definePageMeta({ middleware: 'auth' })`.