| name | clerk-reference-architecture |
| description | 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".
|
| allowed-tools | Read, Write, Edit, Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Clerk Reference Architecture
Overview
Reference architectures for implementing Clerk in various application types.
Prerequisites
- Understanding of web application architecture
- Familiarity with authentication patterns
- Knowledge of your tech stack
Architecture 1: Next.js Full-Stack Application
+------------------+ +------------------+ +------------------+
| Next.js App | | Clerk Service | | Database |
| (App Router) | | | | (Postgres) |
+------------------+ +------------------+ +------------------+
| | |
| ClerkProvider | |
| (wraps all pages) | |
v v v
+------------------+ +------------------+ +------------------+
| Middleware |<--->| Auth API | | Prisma Client |
| (clerkMiddleware)| | (JWT verify) | | |
+------------------+ +------------------+ +------------------+
| | |
v v v
+------------------+ +------------------+ +------------------+
| Protected | | Webhooks |---->| User Sync |
| Server Actions | | (user events) | | (user table) |
+------------------+ +------------------+ +------------------+
Implementation
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({ children }) {
return (
<ClerkProvider>
<html><body>{children}</body></html>
</ClerkProvider>
)
}
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher(['/', '/sign-in(.*)', '/sign-up(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) await auth.protect()
})
import { prisma } from './prisma'
export async function syncUserFromClerk(clerkUser: any) {
await prisma..({
: { : clerkUser. },
: {
: clerkUser.[]?.,
: ,
: clerkUser.
},
: {
: clerkUser.,
: clerkUser.[]?.,
: ,
: clerkUser.
}
})
}
Architecture 2: Microservices with Shared Auth
+------------------+
| API Gateway |
| (JWT Verify) |
+------------------+
|
| Bearer Token
v
+-------+-------+-------+-------+
| | | | |
v v v v v
+-----+ +-----+ +-----+ +-----+ +-----+
|User | |Order| |Pay | |Inv | |Notif|
|Svc | |Svc | |Svc | |Svc | |Svc |
+-----+ +-----+ +-----+ +-----+ +-----+
| | | | |
v v v v v
+------------------------------------------+
| Shared User Store |
| (Synced via Webhooks) |
+------------------------------------------+
Implementation
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY
})
export async function verifyToken(req: Request): Promise<JWTPayload | null> {
const token = req.headers.get('authorization')?.replace('Bearer ', '')
if (!token) return null
try {
const { sub: userId } = await clerk.verifyToken(token)
return { userId }
} catch {
return null
}
}
export async function createOrder(req: AuthenticatedRequest) {
const { userId } = req.auth
db..({
: {
userId,
: req..,
:
}
})
}
Architecture 3: Multi-Tenant SaaS
+------------------+ +------------------+
| Tenant A | | Tenant B |
| (Org: acme) | | (Org: globex) |
+------------------+ +------------------+
| |
v v
+------------------------------------------+
| Clerk Organizations |
| - Member management |
| - Role-based permissions |
| - SSO per organization |
+------------------------------------------+
|
v
+------------------------------------------+
| Application Layer |
| - Organization context in all queries |
| - Data isolation by orgId |
+------------------------------------------+
|
v
+------------------------------------------+
| Database (Multi-tenant) |
| - All tables have organizationId |
| - RLS policies enforce isolation |
+------------------------------------------+
Implementation
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
export default clerkMiddleware(async (auth, req) => {
const { userId, orgId, orgRole } = await auth()
if (!userId) {
return auth.redirectToSignIn()
}
if (req.nextUrl.pathname.startsWith('/app') && !orgId) {
return Response.redirect(new URL('/select-organization', req.url))
}
})
import { auth } from '@clerk/nextjs/server'
import { prisma } from './prisma'
export async function getTenantPrisma() {
const { orgId } = await auth()
if (!orgId) {
throw new Error('Organization context required')
}
prisma.$extends({
: {
$allOperations({ operation, args, query }) {
( args) {
args. = { ...args., : orgId }
}
( args) {
args. = { ...args., : orgId }
}
(args)
}
}
})
}
Architecture 4: Mobile + Web with Shared Backend
+-------------+ +-------------+ +-------------+
| Web App | | iOS App | | Android App |
| (Next.js) | | (Swift) | | (Kotlin) |
+-------------+ +-------------+ +-------------+
| | |
v v v
+------------------------------------------+
| Clerk SDKs |
| - @clerk/nextjs |
| - clerk-expo (React Native) |
| - Native SDKs |
+------------------------------------------+
| | |
+-------+-------+-------+-------+
|
v
+------------------------------------------+
| Shared API Backend |
| - Verify JWT from any platform |
| - Consistent user model |
+------------------------------------------+
Implementation
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY
})
export async function verifyRequest(req: Request) {
const token = req.headers.get('authorization')?.replace('Bearer ', '')
if (!token) {
throw new Error('No token provided')
}
const { sub: userId, metadata } = await clerk.verifyToken(token)
return {
userId,
platform: metadata?.platform || 'unknown'
}
}
Architecture Decision Matrix
| Use Case | Architecture | Key Components |
|---|
| Simple SaaS | Full-stack Next.js | ClerkProvider, Middleware |
| Microservices | API Gateway + Services | JWT verification, User sync |
| Multi-tenant | Organization-based | Org context, RLS |
| Mobile + Web | Shared backend | Platform-agnostic JWT |
| Enterprise | SSO + RBAC | SAML/OIDC, Roles |
Output
- Architecture pattern selected
- Component diagram defined
- Implementation patterns ready
- Data flow documented
Best Practices
- Always sync users to local database - Don't rely solely on Clerk API
- Use webhooks for real-time sync - Don't poll for changes
- Implement organization context early - Retrofitting is painful
- Cache aggressively - Reduce Clerk API calls
- Plan for SSO - Enterprise customers will need it
Resources
Next Steps
Proceed to clerk-multi-env-setup for multi-environment configuration.