Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Reference architecture patterns for Clerk authentication. Use when designing application architecture, planning auth flows, or implementing enterprise-grade authentication. Trigger with phrases like "clerk architecture", "clerk design", "clerk system design", "clerk integration patterns".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 38% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 46% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 106% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 97% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 71% | 0% |
Reference architectures for implementing Clerk in common application patterns: Next.js full-stack, microservices with shared auth, multi-tenant SaaS, and mobile + web with shared backend.
Browser
│
├─▸ Next.js Middleware (clerkMiddleware)
│ └─▸ Validates session token on every request
│
├─▸ Server Components (auth(), currentUser())
│ └─▸ Direct access to user data, no network call
│
├─▸ Client Components (useUser(), useAuth())
│ └─▸ Real-time auth state via ClerkProvider
│
├─▸ API Routes (auth() for userId, getToken() for JWT)
│ └─▸ Call external services with Clerk JWT
│
└─▸ Webhooks (/api/webhooks/clerk)
└─▸ Sync user data to databasetypescript// app/layout.tsx — entry point import { ClerkProvider } from '@clerk/nextjs' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <ClerkProvider> <html><body>{children}</body></html> </ClerkProvider> ) }
typescript// middleware.ts — auth boundary import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' const isPublic = createRouteMatcher(['/', '/sign-in(.*)', '/sign-up(.*)', '/api/webhooks(.*)']) export default clerkMiddleware(async (auth, req) => { if (!isPublic(req)) await auth.protect() })
Browser ─▸ API Gateway / BFF (Next.js + Clerk)
│
├─▸ Service A (Node.js) ──── verifies JWT
├─▸ Service B (Python) ──── verifies JWT
└─▸ Service C (Go) ──────── verifies JWTtypescript// BFF: Generate service-specific JWT // app/api/proxy/[service]/route.ts import { auth } from '@clerk/nextjs/server' export async function GET(req: Request, { params }: { params: { service: string } }) { const { userId, getToken } = await auth() if (!userId) return Response.json({ error: 'Unauthorized' }, { status: 401 }) // Get JWT with service-specific claims const token = await getToken({ template: params.service }) const serviceUrls: Record<string, string> = { billing: process.env.BILLING_SERVICE_URL!, analytics: process.env.ANALYTICS_SERVICE_URL!, notifications: process.env.NOTIFICATION_SERVICE_URL!, } const response = await fetch(`${serviceUrls[params.service]}/api/data`, { headers: { Authorization: `Bearer ${token}` }, }) return Response.json(await response.json()) }
typescript// Downstream service: Verify Clerk JWT // services/billing/src/middleware.ts (Express) import { clerkMiddleware, requireAuth } from '@clerk/express' app.use(clerkMiddleware()) app.get('/api/data', requireAuth(), (req, res) => { // req.auth.userId is available res.json({ userId: req.auth.userId }) })
Tenant A (org_abc) ──┐
Tenant B (org_def) ──┤──▸ Shared App ──▸ Shared DB (tenant-scoped queries)
Tenant C (org_ghi) ──┘typescript// lib/tenant.ts — tenant-scoped data access import { auth } from '@clerk/nextjs/server' export async function getTenantData<T>(query: (orgId: string) => Promise<T>): Promise<T> { const { orgId } = await auth() if (!orgId) throw new Error('No organization selected') return query(orgId) } // Usage: export async function getProjects() { return getTenantData((orgId) => db.project.findMany({ where: { organizationId: orgId } }) ) }
typescript// middleware.ts — enforce org context on tenant routes import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' const isTenantRoute = createRouteMatcher(['/app(.*)']) export default clerkMiddleware(async (auth, req) => { if (isTenantRoute(req)) { const { orgId } = await auth.protect() if (!orgId) { // Redirect to org selector if no org is active return Response.redirect(new URL('/select-org', req.url)) } } })
typescript// app/select-org/page.tsx import { OrganizationSwitcher } from '@clerk/nextjs' export default function SelectOrg() { return ( <div className="flex min-h-screen items-center justify-center"> <div> <h1>Select Your Organization</h1> <OrganizationSwitcher afterSelectOrganizationUrl="/app/dashboard" hidePersonal={true} /> </div> </div> ) }
Web App (Next.js + @clerk/nextjs) ──┐
Mobile App (React Native + @clerk/clerk-expo) ──┤──▸ Backend API (Express + @clerk/express)
└──▸ Databasetypescript// Backend API: Express with Clerk // server.ts import express from 'express' import { clerkMiddleware, requireAuth, getAuth } from '@clerk/express' const app = express() // Apply Clerk middleware globally app.use(clerkMiddleware()) // Public endpoint app.get('/api/public', (req, res) => { res.json({ message: 'Public endpoint' }) }) // Protected endpoint (works with both web and mobile clients) app.get('/api/profile', requireAuth(), async (req, res) => { const { userId } = getAuth(req) const user = await db.user.findUnique({ where: { clerkId: userId } }) res.json({ user }) }) app.listen(3001)
@clerk/express| Pattern | Common Issue | Solution | |---------|-------------|----------| | Full-stack | Middleware redirect loop | Add sign-in route to public routes | | Microservices | JWT template not configured | Create JWT template in Dashboard per service | | Multi-tenant | No org selected | Redirect to org selector before tenant routes | | Mobile + Web | Token not sent from mobile | Include Authorization: Bearer <token> in mobile fetch |
prisma// prisma/schema.prisma model User { id String @id @default(cuid()) clerkId String @unique email String @unique name String? createdAt DateTime @default(now()) posts Post[] orgMemberships OrgMembership[] } model OrgMembership { id String @id @default(cuid()) userId String orgId String // Clerk organization ID role String // org:admin, org:member, etc. user User @relation(fields: [userId], references: [id]) @@unique([userId, orgId]) }
Proceed to clerk-multi-env-setup for multi-environment configuration.
Other measured skills in the registry, with their headline benchmark lift.