Architecture
Comprehensive guide to the SonicJS system architecture, covering the Cloudflare Workers platform, request lifecycle, plugin system, and data flow patterns.
Overview
SonicJS is a modern, TypeScript-first headless CMS built specifically for Cloudflare's edge platform. It leverages Cloudflare Workers, D1 database, R2 storage, and KV cache to deliver exceptional performance at the edge.
Key Architectural Principles
- Edge-First - Runs entirely on Cloudflare's global edge network
- Zero Cold Starts - V8 isolates provide instant startup
- TypeScript-Native - Fully typed for developer experience
- Plugin-Driven - Extensible through a robust plugin system
- Performance-Optimized - Three-tier caching for sub-millisecond response times
Technology Stack
Cloudflare Workers
Serverless execution at the edge with V8 isolates
D1 Database
SQLite with global replication and Drizzle ORM
R2 Storage
S3-compatible object storage for media files
KV Cache
Global key-value store for distributed caching
Hono Framework
Ultra-fast web framework for routing and middleware
Drizzle ORM
Type-safe database queries with automatic migrations
Cloudflare Workers
V8 Isolates Model
SonicJS runs in V8 isolates, providing:
- Instant startup (no cold starts)
- Memory isolation between requests
- Automatic scaling to handle traffic spikes
- Global distribution across 300+ locations
Worker Entry Point
// Worker bindings configuration
type Bindings = {
DB: D1Database // Cloudflare D1 database
KV: KVNamespace // Key-value cache
MEDIA_BUCKET: R2Bucket // Object storage
ASSETS: Fetcher // Static assets
EMAIL_QUEUE?: Queue // Email queue (optional)
ENVIRONMENT?: string // Environment flag
}
type Variables = {
user?: {
userId: string
email: string
role: string
exp: number
iat: number
}
requestId?: string
startTime?: number
}
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()
Resource Access Pattern
Accessing Cloudflare Resources
// Accessing D1, KV, and R2 in routes
app.get('/api/data', async (c) => {
// Access D1 database
const db = c.env.DB
const result = await db.prepare('SELECT * FROM content').all()
// Access KV cache
const kv = c.env.KV
const cached = await kv.get('key', 'json')
// Access R2 storage
const bucket = c.env.MEDIA_BUCKET
const file = await bucket.get('image.jpg')
return c.json(result)
})
System Architecture
High-Level Overview
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Cloudflare Edge Network β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Middleware Pipeline β β
β β Bootstrap β Logging β Auth β Permissions β Plugin β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Route Handler β β
β β API Routes | Admin Routes | Auth Routes | Plugins β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Service Layer β β
β β Cache | Content | Auth | Plugin Services β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Data Layer β β
β β Memory Cache | KV Cache | D1 Database β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Layer Responsibilities
1. Middleware Pipeline
- Request preprocessing
- Authentication and authorization
- Security headers
- Performance monitoring
- Logging
2. Route Handler
- Request routing
- Parameter validation
- Response formatting
- Error handling
3. Service Layer
- Business logic
- Data transformation
- Cache management
- External integrations
4. Data Layer
- Data persistence
- Cache storage
- Query execution
Request Lifecycle
Complete Request Flow
- Request arrives at Cloudflare edge
- Bootstrap Middleware - Run migrations, sync collections, bootstrap plugins
- Logging Middleware - Generate request ID, record start time
- Security Middleware - Check suspicious patterns, add security headers
- Authentication Middleware - Extract JWT, verify token, set user context
- Permission Middleware - Check user permissions and roles
- Plugin Middleware - Execute plugin hooks
- Route Handler - Execute route logic, check cache, process business logic
- Response Processing - Add cache headers, timing metadata, log metrics
- Return to client
Middleware Configuration
// Application middleware setup
app.use('*', bootstrapMiddleware())
app.use('*', loggingMiddleware())
app.use('*', securityLoggingMiddleware())
app.use('*', logger())
app.use('*', cors())
app.use('*', securityHeaders())
// Route-specific middleware
app.use('/admin/*', async (c, next) => {
return await requireAuth()(c, next)
})
app.use('/admin/*', requireRole(['admin', 'editor']))
// Routes
app.route('/api', apiRoutes)
app.route('/admin', adminRoutes)
app.route('/auth', authRoutes)
Middleware Pipeline
Execution Order
- Bootstrap (Priority: 0) - System initialization
- Logging (Priority: 1) - Request tracking
- Security (Priority: 2) - Headers, validation
- CORS (Priority: 3) - Cross-origin
- Authentication (Priority: 10) - JWT verification
- Authorization (Priority: 11) - Permission checks
- Plugin Middleware (Priority: 50+) - Custom logic
- Route Handler (Priority: 100)
Bootstrap Middleware
Ensures system initialization on first request:
Bootstrap Process
export function bootstrapMiddleware() {
return async (c: Context<{ Bindings: Bindings }>, next: Next) => {
if (bootstrapComplete) {
return next()
}
try {
// 1. Run database migrations
const migrationService = new MigrationService(c.env.DB)
await migrationService.runPendingMigrations()
// 2. Sync collection configurations
await syncCollections(c.env.DB)
// 3. Bootstrap core plugins
const bootstrapService = new PluginBootstrapService(c.env.DB)
if (await bootstrapService.isBootstrapNeeded()) {
await bootstrapService.bootstrapCorePlugins()
await bootstrapService.installDemoPlugins()
}
bootstrapComplete = true
} catch (error) {
console.error('[Bootstrap] Error:', error)
}
return next()
}
}
Authentication Middleware
JWT-based authentication with KV caching:
Auth Middleware
export const requireAuth = () => {
return async (c: Context, next: Next) => {
// Get token from header or cookie
let token = c.req.header('Authorization')?.replace('Bearer ', '')
if (!token) {
token = getCookie(c, 'auth_token')
}
if (!token) {
return c.json({ error: 'Authentication required' }, 401)
}
// Try KV cache first (5-minute TTL)
const kv = c.env?.KV
let payload: JWTPayload | null = null
if (kv) {
const cacheKey = `auth:${token.substring(0, 20)}`
const cached = await kv.get(cacheKey, 'json')
if (cached) {
payload = cached as JWTPayload
}
}
// Verify token if not cached
if (!payload) {
payload = await AuthManager.verifyToken(token)
// Cache for 5 minutes
if (payload && kv) {
await kv.put(cacheKey, JSON.stringify(payload), {
expirationTtl: 300
})
}
}
if (!payload) {
return c.json({ error: 'Invalid or expired token' }, 401)
}
c.set('user', payload)
return await next()
}
}
Plugin System
Architecture
SonicJS features a powerful plugin architecture:
Core Components:
- Plugin Registry - Registers and manages plugins
- Hook System - Event-driven architecture with priority-based execution
- Plugin Manager - Orchestrates plugin lifecycle
Plugin Types:
- Core Plugins (auto-installed): auth, media, cache, database-tools, seed-data
- Demo Plugins (optional): workflow, FAQ, demo-login
- Custom Plugins - Your own extensions
Plugin Lifecycle
// Plugin lifecycle
install() -> activate() -> [running] -> deactivate() -> uninstall()
// Hook registration
export class HookSystemImpl implements HookSystem {
register(hookName: string, handler: HookHandler, priority: number = 10): void {
if (!this.hooks.has(hookName)) {
this.hooks.set(hookName, [])
}
const hooks = this.hooks.get(hookName)!
const hook: PluginHook = { name: hookName, handler, priority }
// Insert in priority order
const insertIndex = hooks.findIndex(h => h.priority! > priority)
if (insertIndex === -1) {
hooks.push(hook)
} else {
hooks.splice(insertIndex, 0, hook)
}
}
async execute(hookName: string, data: any, context?: any): Promise<any> {
const hooks = this.hooks.get(hookName)
if (!hooks || hooks.length === 0) {
return data
}
let result = data
for (const hook of hooks) {
result = await hook.handler(result, context)
}
return result
}
}
Caching Strategy
Three-Tiered System
SonicJS implements intelligent caching across three tiers:
Tier 1: In-Memory Cache
- Fastest access (< 1ms)
- 50MB size limit per worker
- LRU eviction policy
Tier 2: Cloudflare KV
- Global distribution
- Eventually consistent
- 5-60 minute TTLs
Tier 3: D1 Database
- Source of truth
- Strong consistency
- Full query capabilities
Cache Flow
Request
β
Memory Cache? ββYesβββ Return (< 1ms)
β No
KV Cache? ββYesβββ Populate Memory β Return (10-50ms)
β No
Database βββ Populate KV β Populate Memory β Return (100-200ms)
Cache Service
export class CacheService {
async get<T>(key: string): Promise<T | null> {
this.stats.totalRequests++
// Try memory cache first (Tier 1)
if (this.config.memoryEnabled) {
const memoryValue = this.memoryCache.get<T>(key)
if (memoryValue !== null) {
this.stats.memoryHits++
return memoryValue
}
this.stats.memoryMisses++
}
// Try KV cache (Tier 2)
if (this.config.kvEnabled && this.kvNamespace) {
const kvValue = await this.kvNamespace.get(key, 'json')
if (kvValue !== null) {
this.stats.kvHits++
// Populate memory cache
if (this.config.memoryEnabled) {
this.memoryCache.set(key, kvValue as T, this.config.ttl)
}
return kvValue as T
}
this.stats.kvMisses++
}
return null
}
async set<T>(key: string, value: T): Promise<void> {
// Store in memory (Tier 1)
if (this.config.memoryEnabled) {
this.memoryCache.set(key, value, this.config.ttl)
}
// Store in KV (Tier 2)
if (this.config.kvEnabled && this.kvNamespace) {
await this.kvNamespace.put(key, JSON.stringify(value), {
expirationTtl: this.config.ttl
})
}
}
}
Performance
Edge Performance Metrics
- First request (cold): 50-100ms
- Cached request (memory): 1-5ms
- Cached request (KV): 10-50ms
- Database query: 100-200ms
Optimization Strategies
- Cache Aggressively - Use three-tier caching for all frequently accessed data
- Minimize Database Queries - Batch operations when possible
- Use KV for Authentication - Cache JWT verification results
- Optimize Middleware Order - Fast checks first (auth cache before DB)
- Lazy Load Plugins - Only load plugins when needed
Performance Best Practices
- Enable all three cache tiers for maximum performance
- Set appropriate TTLs based on data volatility
- Use pagination for large datasets
- Implement proper cache invalidation strategies