clerk-nextjs-skills
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLinks
链接
Quick Start
快速开始
1. Install Dependencies (Using pnpm)
1. 安装依赖(使用pnpm)
bash
pnpm add @clerk/nextjsbash
pnpm add @clerk/nextjsFor MCP server integration, also install:
如需集成MCP服务器,还需安装:
pnpm add @vercel/mcp-adapter @clerk/mcp-tools
undefinedpnpm add @vercel/mcp-adapter @clerk/mcp-tools
undefined2. Create proxy.ts (Next.js 16)
2. 创建proxy.ts(Next.js 16)
The file replaces from Next.js 15. Create it at the root or in :
proxy.tsmiddleware.ts/srctypescript
// 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.tsmiddleware.ts/srctypescript
// 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 in your project root:
.env.localenv
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.localenv
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 devVisit and click "Sign Up" to create your first user.
http://localhost:3000bash
pnpm dev访问 并点击“Sign Up”创建你的第一个用户。
http://localhost:3000Key Concepts
核心概念
proxy.ts vs middleware.ts
proxy.ts 与 middleware.ts 的对比
- Next.js 16 (App Router): Use for Clerk middleware
proxy.ts - Next.js ≤15: Use with identical code (filename only differs)
middleware.ts - Clerk's function is the same regardless of filename
clerkMiddleware() - The configuration ensures proper route handling and performance
matcher
- Next.js 16(App Router):使用作为Clerk中间件
proxy.ts - Next.js ≤15:使用,代码完全相同(仅文件名不同)
middleware.ts - Clerk的函数与文件名无关,功能一致
clerkMiddleware() - 配置确保路由处理正确且性能最优
matcher
Protecting Routes
保护路由
By default, does not protect routes—all are public. Use to require authentication:
clerkMiddleware()auth.protect()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.tstypescript
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.tstypescript
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
分步迁移
-
Rename the file fromto
middleware.ts(location remains same: root orproxy.ts)/src -
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 = { ... } -
Update Next.js version:bash
pnpm add next@latest -
Verify environment variables are still in(no changes needed)
.env.local -
Test the migration:bash
pnpm dev
-
重命名文件:将改为
middleware.ts(位置不变:根目录或proxy.ts)/src -
代码保持不变 - 无需修改功能代码: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 = { ... } -
更新Next.js版本:bash
pnpm add next@latest -
验证环境变量仍在中(无需修改)
.env.local -
测试迁移结果:bash
pnpm dev
Troubleshooting Migration
迁移故障排除
- If routes aren't protected, ensure is in the correct location (root or
proxy.ts)/src - Check that has all required Clerk keys
.env.local - Clear cache if middleware changes don't take effect:
.nextrm -rf .next && pnpm dev - Verify Next.js version is 16.0+:
pnpm list next
- 如果路由未被保护,确保在正确位置(根目录或
proxy.ts)/src - 检查是否包含所有必需的Clerk密钥
.env.local - 如果中间件更改未生效,清除缓存:
.nextrm -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快速设置摘要
-
Install MCP dependencies:bash
pnpm add @vercel/mcp-adapter @clerk/mcp-tools -
Create MCP route at:
app/[transport]/route.tstypescriptimport { 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 } -
Expose OAuth metadata endpoints (see references for complete setup)
-
Update proxy.ts to excludeendpoints:
.well-knowntypescriptimport { 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() }) -
Enable Dynamic Client Registration in Clerk Dashboard
-
安装MCP依赖:bash
pnpm add @vercel/mcp-adapter @clerk/mcp-tools -
在创建MCP路由:
app/[transport]/route.tstypescriptimport { 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 } -
暴露OAuth元数据端点(完整设置请查看参考文档)
-
更新proxy.ts以排除端点:
.well-knowntypescriptimport { 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() }) -
在Clerk控制台中启用动态客户端注册
Best Practices
最佳实践
1. Environment Variable Management
1. 环境变量管理
- Always use for development (never commit sensitive keys)
.env.local - Validate environment variables on application startup
- Use prefix ONLY for non-sensitive keys that are safe to expose
NEXT_PUBLIC_ - 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 endpoints public but protect all MCP tools with OAuth
.well-known - Use in
acceptsToken: 'oauth_token'to require machine tokensauth() - OAuth tokens are free during public beta (pricing TBD)
- Always verify tokens with before exposing user data
verifyClerkToken()
- 在Clerk控制台中启用动态客户端注册
- 保持端点公开,但使用OAuth保护所有MCP工具
.well-known - 在中使用
auth()以要求机器令牌acceptsToken: 'oauth_token' - 公开测试版期间OAuth令牌免费(定价待定)
- 在暴露用户数据前始终使用验证令牌
verifyClerkToken()
4. Performance & Caching
4. 性能与缓存
- Use for server-side user queries (cached automatically)
clerkClient() - Leverage React Server Components for secure user data access
- Cache user data when possible to reduce API calls
- Use hooks only in Client Components (
@clerk/nextjs)'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 updated:
@clerk/nextjspnpm update @clerk/nextjs
- 在部署平台中设置所有环境变量
- 使用Clerk的生产实例密钥(而非开发密钥)
- 生产部署前在预发布环境测试身份认证流程
- 在Clerk控制台监控身份认证错误
- 保持更新:
@clerk/nextjspnpm update @clerk/nextjs
Troubleshooting
故障排除
Issues & Solutions
问题与解决方案
| Issue | Solution |
|---|---|
| "Missing environment variables" | Ensure |
| Middleware not protecting routes | Verify |
| Sign-in/sign-up pages not working | Check |
| User data returns null | Ensure user is authenticated: check |
| MCP server OAuth fails | Enable Dynamic Client Registration in Clerk Dashboard OAuth Applications |
| Changes not taking effect | Clear |
| "proxy.ts" not recognized | Verify Next.js version is 16.0+: |
| 问题 | 解决方案 |
|---|---|
| "缺少环境变量" | 确保 |
| 中间件未保护路由 | 验证 |
| 登录/注册页面无法工作 | 检查 |
| 用户数据返回null | 确保用户已认证:调用 |
| MCP服务器OAuth验证失败 | 在Clerk控制台的OAuth应用中启用动态客户端注册 |
| 更改未生效 | 清除 |
| "proxy.ts"未被识别 | 验证Next.js版本为16.0+: |
Common Next.js 16 Gotchas
Next.js 16常见陷阱
- File naming: Must be (not
proxy.ts) for Next.js 16middleware.ts - Location: Place at project root or in
proxy.tsdirectory, NOT in/srcapp/ - Re-exports: Config object must be exported from for matcher to work
proxy.ts - Async operations: is async-ready; use
clerkMiddleware()for route protectionawait auth.protect()
- 文件名:Next.js 16必须使用(而非
proxy.ts)middleware.ts - 位置:将放在项目根目录或
proxy.ts目录,不要放在/src中app/ - 重导出:必须从导出config对象,matcher才能生效
proxy.ts - 异步操作:支持异步;使用
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 devRelated 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等)