clerk-nextjs-skills

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Links

链接

Quick Start

快速开始

1. Install Dependencies (Using pnpm)

1. 安装依赖(使用pnpm)

bash
pnpm add @clerk/nextjs
bash
pnpm add @clerk/nextjs

For MCP server integration, also install:

如需集成MCP服务器,还需安装:

pnpm add @vercel/mcp-adapter @clerk/mcp-tools
undefined
pnpm add @vercel/mcp-adapter @clerk/mcp-tools
undefined

2. Create proxy.ts (Next.js 16)

2. 创建proxy.ts(Next.js 16)

The
proxy.ts
file replaces
middleware.ts
from Next.js 15. Create it at the root or in
/src
:
typescript
// proxy.ts (or src/proxy.ts)
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware()

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    '/(api|trpc)(.*)',
  ],
}
proxy.ts
文件替代了Next.js 15中的
middleware.ts
。在项目根目录或
/src
目录下创建该文件:
typescript
// proxy.ts (或 src/proxy.ts)
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware()

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    '/(api|trpc)(.*)',
  ],
}

3. Set Environment Variables

3. 设置环境变量

Create
.env.local
in your project root:
env
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key_here
CLERK_SECRET_KEY=your_secret_key_here
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/
在项目根目录创建
.env.local
文件:
env
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key_here
CLERK_SECRET_KEY=your_secret_key_here
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/

4. Add ClerkProvider to Layout

4. 向布局中添加ClerkProvider

typescript
// app/layout.tsx
import {
  ClerkProvider,
  SignInButton,
  SignUpButton,
  SignedIn,
  SignedOut,
  UserButton,
} from '@clerk/nextjs'
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'My App',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>
          <header className="flex justify-end items-center p-4 gap-4 h-16">
            <SignedOut>
              <SignInButton />
              <SignUpButton />
            </SignedOut>
            <SignedIn>
              <UserButton />
            </SignedIn>
          </header>
          {children}
        </body>
      </html>
    </ClerkProvider>
  )
}
typescript
// app/layout.tsx
import {
  ClerkProvider,
  SignInButton,
  SignUpButton,
  SignedIn,
  SignedOut,
  UserButton,
} from '@clerk/nextjs'
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'My App',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>
          <header className="flex justify-end items-center p-4 gap-4 h-16">
            <SignedOut>
              <SignInButton />
              <SignUpButton />
            </SignedOut>
            <SignedIn>
              <UserButton />
            </SignedIn>
          </header>
          {children}
        </body>
      </html>
    </ClerkProvider>
  )
}

5. Run Your App

5. 运行应用

bash
pnpm dev
Visit
http://localhost:3000
and click "Sign Up" to create your first user.
bash
pnpm dev
访问
http://localhost:3000
并点击“Sign Up”创建你的第一个用户。

Key Concepts

核心概念

proxy.ts vs middleware.ts

proxy.ts 与 middleware.ts 的对比

  • Next.js 16 (App Router): Use
    proxy.ts
    for Clerk middleware
  • Next.js ≤15: Use
    middleware.ts
    with identical code (filename only differs)
  • Clerk's
    clerkMiddleware()
    function is the same regardless of filename
  • The
    matcher
    configuration ensures proper route handling and performance
  • Next.js 16(App Router):使用
    proxy.ts
    作为Clerk中间件
  • Next.js ≤15:使用
    middleware.ts
    ,代码完全相同(仅文件名不同)
  • Clerk的
    clerkMiddleware()
    函数与文件名无关,功能一致
  • matcher
    配置确保路由处理正确且性能最优

Protecting Routes

保护路由

By default,
clerkMiddleware()
does not protect routes—all are public. Use
auth.protect()
to require authentication:
typescript
// Protect specific route
import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { userId } = await auth()
  
  if (!userId) {
    // Redirect handled by clerkMiddleware
  }
  
  return <div>Protected content for {userId}</div>
}
Or protect all routes in
proxy.ts
:
typescript
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware(async (auth, req) => {
  await auth.protect()
})
默认情况下,
clerkMiddleware()
不会保护路由——所有路由都是公开的。使用
auth.protect()
要求用户身份认证:
typescript
// 保护特定路由
import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { userId } = await auth()
  
  if (!userId) {
    // 重定向由clerkMiddleware处理
  }
  
  return <div>Protected content for {userId}</div>
}
或者在
proxy.ts
中保护所有路由:
typescript
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware(async (auth, req) => {
  await auth.protect()
})

Environment Variable Validation

环境变量验证

Check for required Clerk keys before runtime:
typescript
// lib/clerk-config.ts
export function validateClerkEnv() {
  const required = [
    'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
    'CLERK_SECRET_KEY',
  ]
  
  const missing = required.filter(key => !process.env[key])
  
  if (missing.length > 0) {
    throw new Error(`Missing required Clerk environment variables: ${missing.join(', ')}`)
  }
}
在运行前检查所需的Clerk密钥:
typescript
// lib/clerk-config.ts
export function validateClerkEnv() {
  const required = [
    'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
    'CLERK_SECRET_KEY',
  ]
  
  const missing = required.filter(key => !process.env[key])
  
  if (missing.length > 0) {
    throw new Error(`Missing required Clerk environment variables: ${missing.join(', ')}`)
  }
}

Accessing User Data

访问用户数据

Use Clerk hooks in client components:
typescript
// app/components/user-profile.tsx
'use client'

import { useUser } from '@clerk/nextjs'

export function UserProfile() {
  const { user, isLoaded } = useUser()
  
  if (!isLoaded) return <div>Loading...</div>
  
  if (!user) return <div>Not signed in</div>
  
  return (
    <div>
      <h1>{user.fullName}</h1>
      <p>{user.primaryEmailAddress?.emailAddress}</p>
    </div>
  )
}
Or in server components/actions:
typescript
// app/actions.ts
'use server'

import { auth, clerkClient } from '@clerk/nextjs/server'

export async function getUserData() {
  const { userId } = await auth()
  
  if (!userId) {
    throw new Error('Unauthorized')
  }
  
  const clerk = await clerkClient()
  const user = await clerk.users.getUser(userId)
  
  return user
}
在客户端组件中使用Clerk钩子:
typescript
// app/components/user-profile.tsx
'use client'

import { useUser } from '@clerk/nextjs'

export function UserProfile() {
  const { user, isLoaded } = useUser()
  
  if (!isLoaded) return <div>Loading...</div>
  
  if (!user) return <div>Not signed in</div>
  
  return (
    <div>
      <h1>{user.fullName}</h1>
      <p>{user.primaryEmailAddress?.emailAddress}</p>
    </div>
  )
}
或者在服务器组件/动作中使用:
typescript
// app/actions.ts
'use server'

import { auth, clerkClient } from '@clerk/nextjs/server'

export async function getUserData() {
  const { userId } = await auth()
  
  if (!userId) {
    throw new Error('Unauthorized')
  }
  
  const clerk = await clerkClient()
  const user = await clerk.users.getUser(userId)
  
  return user
}

Migrating from middleware.ts (Next.js 15) to proxy.ts (Next.js 16)

从Next.js 15的middleware.ts迁移到Next.js 16的proxy.ts

Step-by-Step Migration

分步迁移

  1. Rename the file from
    middleware.ts
    to
    proxy.ts
    (location remains same: root or
    /src
    )
  2. Keep the code identical - No functional changes needed:
    typescript
    // Before (middleware.ts)
    import { clerkMiddleware } from '@clerk/nextjs/server'
    export default clerkMiddleware()
    export const config = { ... }
    
    // After (proxy.ts) - Same code
    import { clerkMiddleware } from '@clerk/nextjs/server'
    export default clerkMiddleware()
    export const config = { ... }
  3. Update Next.js version:
    bash
    pnpm add next@latest
  4. Verify environment variables are still in
    .env.local
    (no changes needed)
  5. Test the migration:
    bash
    pnpm dev
  1. 重命名文件:将
    middleware.ts
    改为
    proxy.ts
    (位置不变:根目录或
    /src
  2. 代码保持不变 - 无需修改功能代码:
    typescript
    // 之前(middleware.ts)
    import { clerkMiddleware } from '@clerk/nextjs/server'
    export default clerkMiddleware()
    export const config = { ... }
    
    // 之后(proxy.ts)- 代码完全相同
    import { clerkMiddleware } from '@clerk/nextjs/server'
    export default clerkMiddleware()
    export const config = { ... }
  3. 更新Next.js版本
    bash
    pnpm add next@latest
  4. 验证环境变量仍在
    .env.local
    中(无需修改)
  5. 测试迁移结果
    bash
    pnpm dev

Troubleshooting Migration

迁移故障排除

  • If routes aren't protected, ensure
    proxy.ts
    is in the correct location (root or
    /src
    )
  • Check that
    .env.local
    has all required Clerk keys
  • Clear
    .next
    cache if middleware changes don't take effect:
    rm -rf .next && pnpm dev
  • Verify Next.js version is 16.0+:
    pnpm list next
  • 如果路由未被保护,确保
    proxy.ts
    在正确位置(根目录或
    /src
  • 检查
    .env.local
    是否包含所有必需的Clerk密钥
  • 如果中间件更改未生效,清除
    .next
    缓存:
    rm -rf .next && pnpm dev
  • 验证Next.js版本为16.0+:
    pnpm list next

Building an MCP Server with Clerk

使用Clerk构建MCP服务器

See CLERK_MCP_SERVER_SETUP.md for complete MCP server integration.
完整的MCP服务器集成请查看CLERK_MCP_SERVER_SETUP.md

Quick MCP Setup Summary

MCP快速设置摘要

  1. Install MCP dependencies:
    bash
    pnpm add @vercel/mcp-adapter @clerk/mcp-tools
  2. Create MCP route at
    app/[transport]/route.ts
    :
    typescript
    import { verifyClerkToken } from '@clerk/mcp-tools/next'
    import { createMcpHandler, withMcpAuth } from '@vercel/mcp-adapter'
    import { auth, clerkClient } from '@clerk/nextjs/server'
    
    const clerk = await clerkClient()
    
    const handler = createMcpHandler((server) => {
      server.tool(
        'get-clerk-user-data',
        'Gets data about the Clerk user that authorized this request',
        {},
        async (_, { authInfo }) => {
          const userId = authInfo!.extra!.userId! as string
          const userData = await clerk.users.getUser(userId)
          return {
            content: [{ type: 'text', text: JSON.stringify(userData) }],
          }
        },
      )
    })
    
    const authHandler = withMcpAuth(
      handler,
      async (_, token) => {
        const clerkAuth = await auth({ acceptsToken: 'oauth_token' })
        return verifyClerkToken(clerkAuth, token)
      },
      {
        required: true,
        resourceMetadataPath: '/.well-known/oauth-protected-resource/mcp',
      },
    )
    
    export { authHandler as GET, authHandler as POST }
  3. Expose OAuth metadata endpoints (see references for complete setup)
  4. Update proxy.ts to exclude
    .well-known
    endpoints:
    typescript
    import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
    
    const isPublicRoute = createRouteMatcher([
      '/.well-known/oauth-authorization-server(.*)',
      '/.well-known/oauth-protected-resource(.*)',
    ])
    
    export default clerkMiddleware(async (auth, req) => {
      if (isPublicRoute(req)) return
      await auth.protect()
    })
  5. Enable Dynamic Client Registration in Clerk Dashboard
  1. 安装MCP依赖
    bash
    pnpm add @vercel/mcp-adapter @clerk/mcp-tools
  2. app/[transport]/route.ts
    创建MCP路由
    typescript
    import { verifyClerkToken } from '@clerk/mcp-tools/next'
    import { createMcpHandler, withMcpAuth } from '@vercel/mcp-adapter'
    import { auth, clerkClient } from '@clerk/nextjs/server'
    
    const clerk = await clerkClient()
    
    const handler = createMcpHandler((server) => {
      server.tool(
        'get-clerk-user-data',
        'Gets data about the Clerk user that authorized this request',
        {},
        async (_, { authInfo }) => {
          const userId = authInfo!.extra!.userId! as string
          const userData = await clerk.users.getUser(userId)
          return {
            content: [{ type: 'text', text: JSON.stringify(userData) }],
          }
        },
      )
    })
    
    const authHandler = withMcpAuth(
      handler,
      async (_, token) => {
        const clerkAuth = await auth({ acceptsToken: 'oauth_token' })
        return verifyClerkToken(clerkAuth, token)
      },
      {
        required: true,
        resourceMetadataPath: '/.well-known/oauth-protected-resource/mcp',
      },
    )
    
    export { authHandler as GET, authHandler as POST }
  3. 暴露OAuth元数据端点(完整设置请查看参考文档)
  4. 更新proxy.ts以排除
    .well-known
    端点
    typescript
    import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
    
    const isPublicRoute = createRouteMatcher([
      '/.well-known/oauth-authorization-server(.*)',
      '/.well-known/oauth-protected-resource(.*)',
    ])
    
    export default clerkMiddleware(async (auth, req) => {
      if (isPublicRoute(req)) return
      await auth.protect()
    })
  5. Clerk控制台中启用动态客户端注册

Best Practices

最佳实践

1. Environment Variable Management

1. 环境变量管理

  • Always use
    .env.local
    for development (never commit sensitive keys)
  • Validate environment variables on application startup
  • Use
    NEXT_PUBLIC_
    prefix ONLY for non-sensitive keys that are safe to expose
  • For production, set environment variables in your deployment platform (Vercel, etc.)
  • 开发环境始终使用
    .env.local
    (切勿提交敏感密钥)
  • 在应用启动时验证环境变量
  • 仅对安全可暴露的非敏感密钥使用
    NEXT_PUBLIC_
    前缀
  • 生产环境中,在部署平台(如Vercel)中设置环境变量

2. Route Protection Strategies

2. 路由保护策略

typescript
// Option A: Protect all routes
export default clerkMiddleware(async (auth, req) => {
  await auth.protect()
})

// Option B: Protect specific routes
import { createRouteMatcher } from '@clerk/nextjs/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/api/user(.*)'])

export default clerkMiddleware(async (auth, req) => {
  if (isProtectedRoute(req)) {
    await auth.protect()
  }
})

// Option C: Public routes with opt-in protection
const isPublicRoute = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)'])

export default clerkMiddleware(async (auth, req) => {
  if (!isPublicRoute(req)) {
    await auth.protect()
  }
})
typescript
// 选项A:保护所有路由
export default clerkMiddleware(async (auth, req) => {
  await auth.protect()
})

// 选项B:保护特定路由
import { createRouteMatcher } from '@clerk/nextjs/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/api/user(.*)'])

export default clerkMiddleware(async (auth, req) => {
  if (isProtectedRoute(req)) {
    await auth.protect()
  }
})

// 选项C:公共路由,按需启用保护
const isPublicRoute = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)'])

export default clerkMiddleware(async (auth, req) => {
  if (!isPublicRoute(req)) {
    await auth.protect()
  }
})

3. MCP Server Security

3. MCP服务器安全

  • Enable Dynamic Client Registration in Clerk Dashboard
  • Keep
    .well-known
    endpoints public but protect all MCP tools with OAuth
  • Use
    acceptsToken: 'oauth_token'
    in
    auth()
    to require machine tokens
  • OAuth tokens are free during public beta (pricing TBD)
  • Always verify tokens with
    verifyClerkToken()
    before exposing user data
  • 在Clerk控制台中启用动态客户端注册
  • 保持
    .well-known
    端点公开,但使用OAuth保护所有MCP工具
  • auth()
    中使用
    acceptsToken: 'oauth_token'
    以要求机器令牌
  • 公开测试版期间OAuth令牌免费(定价待定)
  • 在暴露用户数据前始终使用
    verifyClerkToken()
    验证令牌

4. Performance & Caching

4. 性能与缓存

  • Use
    clerkClient()
    for server-side user queries (cached automatically)
  • Leverage React Server Components for secure user data access
  • Cache user data when possible to reduce API calls
  • Use
    @clerk/nextjs
    hooks only in Client Components (
    'use client'
    )
  • 使用
    clerkClient()
    进行服务器端用户查询(自动缓存)
  • 利用React Server Components安全访问用户数据
  • 尽可能缓存用户数据以减少API调用
  • 仅在客户端组件(
    'use client'
    )中使用
    @clerk/nextjs
    钩子

5. Production Deployment

5. 生产部署

  • Set all environment variables in your deployment platform
  • Use Clerk's production instance keys (not development keys)
  • Test authentication flow in staging environment before production
  • Monitor Clerk Dashboard for authentication errors
  • Keep
    @clerk/nextjs
    updated:
    pnpm update @clerk/nextjs
  • 在部署平台中设置所有环境变量
  • 使用Clerk的生产实例密钥(而非开发密钥)
  • 生产部署前在预发布环境测试身份认证流程
  • 在Clerk控制台监控身份认证错误
  • 保持
    @clerk/nextjs
    更新:
    pnpm update @clerk/nextjs

Troubleshooting

故障排除

Issues & Solutions

问题与解决方案

IssueSolution
"Missing environment variables"Ensure
.env.local
has
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
and
CLERK_SECRET_KEY
Middleware not protecting routesVerify
proxy.ts
is in root or
/src
directory, not in
app/
Sign-in/sign-up pages not workingCheck
NEXT_PUBLIC_CLERK_SIGN_IN_URL
and
NEXT_PUBLIC_CLERK_SIGN_UP_URL
in
.env.local
User data returns nullEnsure user is authenticated: check
userId
is not null before calling
getUser()
MCP server OAuth failsEnable Dynamic Client Registration in Clerk Dashboard OAuth Applications
Changes not taking effectClear
.next
cache:
rm -rf .next
and restart
pnpm dev
"proxy.ts" not recognizedVerify Next.js version is 16.0+:
pnpm list next
问题解决方案
"缺少环境变量"确保
.env.local
包含
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
CLERK_SECRET_KEY
中间件未保护路由验证
proxy.ts
在根目录或
/src
目录,而非
app/
登录/注册页面无法工作检查
.env.local
中的
NEXT_PUBLIC_CLERK_SIGN_IN_URL
NEXT_PUBLIC_CLERK_SIGN_UP_URL
用户数据返回null确保用户已认证:调用
getUser()
前检查
userId
不为null
MCP服务器OAuth验证失败在Clerk控制台的OAuth应用中启用动态客户端注册
更改未生效清除
.next
缓存:
rm -rf .next
并重启
pnpm dev
"proxy.ts"未被识别验证Next.js版本为16.0+:
pnpm list next

Common Next.js 16 Gotchas

Next.js 16常见陷阱

  • File naming: Must be
    proxy.ts
    (not
    middleware.ts
    ) for Next.js 16
  • Location: Place
    proxy.ts
    at project root or in
    /src
    directory, NOT in
    app/
  • Re-exports: Config object must be exported from
    proxy.ts
    for matcher to work
  • Async operations:
    clerkMiddleware()
    is async-ready; use
    await auth.protect()
    for route protection
  • 文件名:Next.js 16必须使用
    proxy.ts
    (而非
    middleware.ts
  • 位置:将
    proxy.ts
    放在项目根目录或
    /src
    目录,不要放在
    app/
  • 重导出:必须从
    proxy.ts
    导出config对象,matcher才能生效
  • 异步操作
    clerkMiddleware()
    支持异步;使用
    await auth.protect()
    进行路由保护

Debug Mode

调试模式

Enable debug logging:
typescript
// proxy.ts
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware((auth, req) => {
  if (process.env.DEBUG_CLERK) {
    console.log('Request URL:', req.nextUrl.pathname)
    console.log('User ID:', auth.sessionClaims?.sub)
  }
})
Run with debug:
bash
DEBUG_CLERK=1 pnpm dev
启用调试日志:
typescript
// proxy.ts
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware((auth, req) => {
  if (process.env.DEBUG_CLERK) {
    console.log('Request URL:', req.nextUrl.pathname)
    console.log('User ID:', auth.sessionClaims?.sub)
  }
})
启用调试运行:
bash
DEBUG_CLERK=1 pnpm dev

Related Skills

相关技能

  • mcp-server-skills: General MCP server patterns with Vercel adapter
  • nextjs16-skills: Next.js 16 features, breaking changes, and best practices
  • authjs-skills: Alternative authentication using Auth.js (Auth0, GitHub, etc.)
  • mcp-server-skills:使用Vercel适配器的通用MCP服务器模式
  • nextjs16-skills:Next.js 16特性、重大变更及最佳实践
  • authjs-skills:使用Auth.js的替代身份认证方案(Auth0、GitHub等)

Resources

资源