clerk-reference-architecture
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>
Allowed Tools
No tools specified
Provided by Plugin
clerk-pack
Claude Code skill pack for Clerk authentication (24 skills)
Installation
This skill is included in the clerk-pack plugin:
/plugin install clerk-pack@claude-code-plugins-plus
Click to copy
Instructions
# 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
```typescript
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({ children }) {
return (
{children}
)
}
// middleware.ts
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()
})
// lib/db.ts - User sync via webhooks
import { prisma } from './prisma'
export async function syncUserFromClerk(clerkUser: any) {
await prisma.user.upsert({
where: { clerkId: clerkUser.id },
update: {
email: clerkUser.email_addresses[0]?.email_address,
name: `${clerkUser.first_name} ${clerkUser.last_name}`,
imageUrl: clerkUser.image_url
},
create: {
clerkId: clerkUser.id,
email: clerkUser.email_addresses[0]?.email_address,
name: `${clerkUser.first_name} ${clerkUser.last_name}`,
imageUrl: clerkUser.image_url
}
})
}
```
## 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
```typescript
// API Gateway - Verify Clerk JWT
// gateway/src/middleware/auth.ts
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY
})
export async function verifyToken(req: Request): Promise {
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
}
}
// Microservice - Trust gateway-verified user
// services/order/src/handlers/create-order.ts
export async function createOrder(req: AuthenticatedRequest) {
const { userId } = req.auth // Set by gateway
return await db.order.create({
data: {
userId,
items: req.body.items,
status: 'pending'
}
})
}
```
## 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
```typescript
// middleware.ts - Organization context
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
export default clerkMiddleware(async (auth, req) => {
const { userId, orgId, orgRole } = await auth()
if (!userId) {
return auth.redirectToSignIn()
}
// Require organization for app routes
if (req.nextUrl.pathname.startsWith('/app') && !orgId) {
return Response.redirect(new URL('/select-organization', req.url))
}
})
// lib/db-context.ts - Tenant-scoped queries
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')
}
// Return prisma client with tenant filter
return prisma.$extends({
query: {
$allOperations({ operation, args, query }) {
// Inject orgId into all queries
if ('where' in args) {
args.where = { ...args.where, organizationId: orgId }
}
if ('data' in args) {
args.data = { ...args.data, organizationId: orgId }
}
return query(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
```typescript
// Backend API - Platform-agnostic auth
// api/src/middleware/verify-clerk.ts
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')
}
// Works with tokens from any Clerk SDK
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
1. **Always sync users to local database** - Don't rely solely on Clerk API
2. **Use webhooks for real-time sync** - Don't poll for changes
3. **Implement organization context early** - Retrofitting is painful
4. **Cache aggressively** - Reduce Clerk API calls
5. **Plan for SSO** - Enterprise customers will need it
## Resources
- [Clerk Architecture Guide](https://clerk.com/docs/architecture)
- [Multi-tenant Patterns](https://clerk.com/docs/organizations/overview)
- [Backend Integration](https://clerk.com/docs/backend-requests/overview)
## Next Steps
Proceed to `clerk-multi-env-setup` for multi-environment configuration.