The evolution of data at the edge tells the story of modern web architecture. From storing simple cache headers in XML to running full SQL databases at 3,000+ global locations, Akamai's journey mirrors the web's transformation from static delivery to dynamic applications.

In my previous article on Akamai's edge evolution, I covered the compute journey from ESI to WebAssembly. Today, let's dive deep into the equally fascinating data story—how we went from basic metadata to sophisticated edge databases that can power entire applications.

Chapter 1: The Metadata Era (1998-2008)

XML Configuration Files: The Foundation

Akamai's earliest edge data wasn't really "data" in the modern sense—it was configuration metadata stored in XML files distributed to edge servers:

<!-- Early Akamai edge configuration -->
<akamai-config version="1.0">
  <cache-rules>
    <rule pattern="*.jpg" ttl="86400" />
    <rule pattern="*.css" ttl="3600" />
    <rule pattern="*.html" ttl="300" />
  </cache-rules>
  
  <origin-settings>
    <hostname>origin.example.com</hostname>
    <port>80</port>
    <connection-timeout>30</connection-timeout>
  </origin-settings>
  
  <error-handling>
    <404-response>/errors/404.html</404-response>
    <500-response>/errors/500.html</500-response>
  </error-handling>
</akamai-config>

This XML metadata was revolutionary for its time:

  • Global consistency: Same configuration across 1,000+ edge servers
  • Real-time updates: Configuration changes propagated in minutes
  • Cache intelligence: Rules for what to cache and for how long

But it was fundamentally static. The edge could read configuration but couldn't store or modify dynamic data.

HTTP Headers as Data Transport

Early dynamic behavior relied on HTTP headers carrying data between edge and origin:

# Request to origin with edge-added context
GET /api/user/profile HTTP/1.1
Host: api.example.com
X-Akamai-Country: US
X-Akamai-State: CA
X-Akamai-City: San-Francisco
X-Akamai-Client-IP: 198.51.100.1
X-Akamai-Request-ID: 2a3b4c5d-6e7f-8901-2345-6789abcdef01

# Response with edge caching instructions
HTTP/1.1 200 OK
Edge-Control: cache-maxage=300
Content-Type: application/json
Vary: X-Akamai-Country

{"user": "john", "currency": "USD", "language": "en-US"}

Headers became our primitive key-value store, but with severe limitations:

  • Size constraints: HTTP header limits
  • No persistence: Gone after the request
  • Security concerns: Exposed in plaintext

Chapter 2: Property Variables - The First Real Data (2008-2015)

Introduction of Property Variables

Property Manager introduced the concept of variables—the first true edge data storage:

// Property Manager Rule (JSON format)
{
  "name": "Store User Preferences",
  "criteria": [
    {
      "name": "requestCookie",
      "options": {
        "cookieName": "user_prefs"
      }
    }
  ],
  "behaviors": [
    {
      "name": "setVariable", 
      "options": {
        "variableName": "PMUSER_CURRENCY",
        "valueSource": "EXTRACT",
        "extractLocation": "COOKIE",
        "cookieName": "currency",
        "defaultValue": "USD"
      }
    },
    {
      "name": "setVariable",
      "options": {
        "variableName": "PMUSER_LANGUAGE", 
        "valueSource": "EXTRACT",
        "extractLocation": "COOKIE",
        "cookieName": "lang",
        "defaultValue": "en"
      }
    }
  ]
}

EdgeJava Integration

EdgeJava applications could read and manipulate these variables:

public class PersonalizationEdgelet extends EdgeletRequestHandler {
    @Override
    public void onRequest(EdgeletRequest req, EdgeletResponse resp) {
        // Read edge variables
        String currency = req.getVariable("PMUSER_CURRENCY");
        String language = req.getVariable("PMUSER_LANGUAGE");
        String country = req.getHeader("X-Akamai-EdgeScape-Country-Code");
        
        // Business logic based on edge data
        if ("premium".equals(req.getVariable("PMUSER_TIER"))) {
            resp.setHeader("X-Cache-Key", 
                String.format("premium-%s-%s-%s", country, currency, language));
        }
        
        // Modify origin request
        if ("JP".equals(country) && !"ja".equals(language)) {
            req.setVariable("PMUSER_LANGUAGE", "ja");
            resp.setHeader("X-Force-Language", "ja");
        }
    }
}

Limitations of Property Variables

While revolutionary, Property variables had constraints:

  • Scope limitation: Only available during request processing
  • No persistence: Couldn't store data between requests
  • Limited operations: Basic string manipulation only
  • Configuration-bound: Required Property Manager changes

Chapter 3: The EdgeWorkers Revolution (2019+)

JavaScript at the Edge with Enhanced Data Access

EdgeWorkers brought JavaScript to the edge with improved data handling:

// EdgeWorkers with Property Variables
export async function onClientRequest(request) {
    // Access Akamai-provided data
    const country = request.getHeader('x-akamai-edgescapecountry-code');
    const device = request.device.deviceType;
    
    // Read property variables
    const userTier = request.getVariable('PMUSER_TIER');
    const experimentGroup = request.getVariable('PMUSER_EXPERIMENT');
    
    // Complex edge logic
    const cacheKey = generateCacheKey(country, device, userTier);
    request.setHeader('X-Cache-Key', cacheKey);
    
    // Dynamic routing based on data
    if (userTier === 'premium' && country === 'US') {
        request.route({
            origin: 'premium-us-origin.example.com',
            path: '/premium' + request.path
        });
    }
}

export async function onOriginResponse(request, response) {
    // Store computed values for downstream processing
    const processingTime = Date.now() - request.getVariable('START_TIME');
    
    // Add performance hints
    if (processingTime > 1000) {
        response.setHeader('Server-Timing', `origin;dur=${processingTime}`);
    }
    
    // Cache decisions based on response data
    const contentType = response.getHeader('content-type');
    if (contentType.includes('application/json')) {
        response.setHeader('Edge-Control', 'cache-maxage=60');
    }
}

function generateCacheKey(country, device, tier) {
    // Hash-based cache segmentation
    const segments = [country, device, tier].join('-');
    return btoa(segments).substring(0, 16);
}

SubRequests for Edge Data Enrichment

EdgeWorkers introduced SubRequests for fetching external data:

import { httpRequest } from 'http-request';

export async function onClientRequest(request) {
    const userId = extractUserId(request);
    
    if (userId) {
        try {
            // Fetch user data from edge-optimized API
            const userResponse = await httpRequest(
                `https://api-cache.example.com/users/${userId}`,
                {
                    method: 'GET',
                    timeout: 100, // Fast timeout for edge performance
                    headers: {
                        'Authorization': request.getHeader('Authorization'),
                        'X-Edge-Request': 'true'
                    }
                }
            );
            
            if (userResponse.ok) {
                const userData = await userResponse.json();
                
                // Store user context in variables
                request.setVariable('USER_TIER', userData.tier);
                request.setVariable('USER_REGION', userData.region);
                request.setVariable('USER_FEATURES', userData.features.join(','));
                
                // Personalized routing
                if (userData.tier === 'enterprise') {
                    request.route({
                        origin: 'enterprise.example.com'
                    });
                }
            }
        } catch (error) {
            // Graceful degradation
            console.log('User lookup failed:', error.message);
            request.setVariable('USER_TIER', 'standard');
        }
    }
}

Chapter 4: EdgeKV - True Edge Database (2021+)

The Database Revolution

EdgeKV marked Akamai's entry into true edge databases—persistent, queryable data storage across the global edge network:

import { EdgeKV } from './edgekv.js';

export async function onClientRequest(request) {
    const edgeKV = new EdgeKV({namespace: 'user_preferences'});
    const userId = extractUserId(request);
    
    try {
        // Read user preferences from edge database
        const userPrefs = await edgeKV.getJson({
            group: 'users',
            item: userId
        });
        
        if (userPrefs) {
            // Apply user preferences to request
            request.setVariable('USER_CURRENCY', userPrefs.currency);
            request.setVariable('USER_LANGUAGE', userPrefs.language);
            request.setVariable('USER_THEME', userPrefs.theme);
            
            // Content customization based on stored preferences
            if (userPrefs.theme === 'dark') {
                request.setHeader('X-Theme-Override', 'dark');
            }
        }
        
    } catch (error) {
        console.log('EdgeKV read failed:', error.message);
    }
}

export async function onOriginResponse(request, response) {
    const edgeKV = new EdgeKV({namespace: 'analytics'});
    const country = request.getHeader('x-akamai-edgescapecountry-code');
    
    // Store real-time analytics at edge
    try {
        const statsKey = `${country}-${new Date().toISOString().split('T')[0]}`;
        const currentStats = await edgeKV.getJson({
            group: 'daily_stats',
            item: statsKey
        }) || { requests: 0, bytes: 0 };
        
        // Update counters
        currentStats.requests++;
        currentStats.bytes += parseInt(response.getHeader('content-length') || '0');
        
        await edgeKV.putJson({
            group: 'daily_stats',
            item: statsKey
        }, currentStats);
        
    } catch (error) {
        console.log('EdgeKV analytics update failed:', error.message);
    }
}

Advanced EdgeKV Patterns

EdgeKV enabled sophisticated edge application patterns:

// Feature Flag Management at Edge
export async function onClientRequest(request) {
    const flagKV = new EdgeKV({namespace: 'feature_flags'});
    const userId = extractUserId(request);
    const userAgent = request.getHeader('user-agent');
    
    try {
        // Get global feature flags
        const globalFlags = await flagKV.getJson({
            group: 'global',
            item: 'active_flags'
        });
        
        // Get user-specific overrides
        const userFlags = await flagKV.getJson({
            group: 'users',
            item: userId
        }) || {};
        
        // Merge flags with user overrides taking precedence
        const activeFlags = { ...globalFlags, ...userFlags };
        
        // Apply feature flags
        if (activeFlags.new_checkout_flow) {
            request.setVariable('CHECKOUT_VERSION', 'v2');
        }
        
        if (activeFlags.premium_features && userTier === 'premium') {
            request.setVariable('ENABLE_PREMIUM', 'true');
        }
        
        // A/B testing based on stored user segments
        const segment = await flagKV.getText({
            group: 'segments',
            item: userId
        });
        
        if (segment === 'experiment_group_a') {
            request.setHeader('X-Experiment', 'new_ui');
        }
        
    } catch (error) {
        // Fallback to safe defaults
        request.setVariable('CHECKOUT_VERSION', 'v1');
    }
}

// Session Management at Edge
export async function onClientRequest(request) {
    const sessionKV = new EdgeKV({namespace: 'sessions'});
    const sessionId = extractSessionId(request);
    
    if (sessionId) {
        try {
            const session = await sessionKV.getJson({
                group: 'active_sessions',
                item: sessionId
            });
            
            if (session && session.expires > Date.now()) {
                // Valid session - add user context
                request.setVariable('USER_ID', session.userId);
                request.setVariable('SESSION_VALID', 'true');
                
                // Update last access time
                session.lastAccess = Date.now();
                await sessionKV.putJson({
                    group: 'active_sessions',
                    item: sessionId
                }, session, { ttl: 3600 }); // 1 hour TTL
                
            } else {
                // Expired or invalid session
                request.setVariable('SESSION_VALID', 'false');
                
                // Clean up expired session
                if (session) {
                    await sessionKV.delete({
                        group: 'active_sessions',
                        item: sessionId
                    });
                }
            }
        } catch (error) {
            console.log('Session validation failed:', error.message);
            request.setVariable('SESSION_VALID', 'false');
        }
    }
}

Chapter 5: HarperDB at the Edge - The Database Revolution (2024+)

The Partnership That Changed Everything

Akamai's partnership with HarperDB brings full SQL databases to the edge—a quantum leap from simple key-value storage:

-- HarperDB at Akamai Edge: Full SQL capabilities
CREATE TABLE user_profiles (
    id UUID PRIMARY KEY,
    email VARCHAR(255) UNIQUE,
    preferences JSONB,
    tier VARCHAR(50),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE user_sessions (
    session_id VARCHAR(128) PRIMARY KEY,
    user_id UUID REFERENCES user_profiles(id),
    ip_address INET,
    user_agent TEXT,
    expires_at TIMESTAMP,
    data JSONB
);

CREATE TABLE feature_flags (
    flag_name VARCHAR(100) PRIMARY KEY,
    is_enabled BOOLEAN DEFAULT false,
    rules JSONB,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Indexes for edge performance
CREATE INDEX idx_sessions_user_id ON user_sessions(user_id);
CREATE INDEX idx_sessions_expires ON user_sessions(expires_at);
CREATE INDEX idx_profiles_tier ON user_profiles(tier);

EdgeWorkers + HarperDB Integration

EdgeWorkers can now query full databases at the edge:

import { HarperDB } from './harperdb-edge.js';

export async function onClientRequest(request) {
    const db = new HarperDB({
        endpoint: 'edge-local', // Local HarperDB instance
        timeout: 50 // Ultra-fast edge queries
    });
    
    const sessionId = extractSessionId(request);
    const userId = extractUserId(request);
    
    try {
        // Complex SQL query at the edge
        const result = await db.query(`
            SELECT 
                up.id, up.email, up.tier, up.preferences,
                us.data as session_data,
                ff.is_enabled as new_ui_enabled
            FROM user_profiles up
            LEFT JOIN user_sessions us ON up.id = us.user_id 
                AND us.session_id = $1 
                AND us.expires_at > NOW()
            LEFT JOIN feature_flags ff ON ff.flag_name = 'new_ui'
            WHERE up.id = $2
        `, [sessionId, userId]);
        
        if (result.length > 0) {
            const user = result[0];
            
            // Rich user context from database
            request.setVariable('USER_TIER', user.tier);
            request.setVariable('USER_EMAIL', user.email);
            request.setVariable('NEW_UI_ENABLED', user.new_ui_enabled);
            
            // JSON preferences parsed at edge
            const prefs = JSON.parse(user.preferences || '{}');
            request.setVariable('USER_CURRENCY', prefs.currency || 'USD');
            request.setVariable('USER_LANGUAGE', prefs.language || 'en');
            
            // Personalized routing based on database data
            if (user.tier === 'enterprise') {
                request.route({
                    origin: 'enterprise-api.example.com',
                    headers: {
                        'X-User-Tier': 'enterprise',
                        'X-User-ID': user.id
                    }
                });
            }
        }
        
    } catch (error) {
        console.log('HarperDB query failed:', error.message);
        // Graceful fallback
        request.setVariable('USER_TIER', 'standard');
    }
}

// Real-time analytics with aggregations
export async function onOriginResponse(request, response) {
    const db = new HarperDB({ endpoint: 'edge-local' });
    
    try {
        // Insert real-time metrics with SQL
        await db.query(`
            INSERT INTO request_metrics (
                timestamp, 
                country, 
                status_code, 
                response_time, 
                bytes_transferred,
                user_tier
            ) VALUES (
                NOW(), 
                $1, 
                $2, 
                $3, 
                $4, 
                $5
            )
        `, [
            request.getHeader('x-akamai-edgescapecountry-code'),
            response.status,
            Date.now() - request.getVariable('START_TIME'),
            response.getHeader('content-length') || 0,
            request.getVariable('USER_TIER')
        ]);
        
        // Real-time aggregation query
        const metrics = await db.query(`
            SELECT 
                COUNT(*) as total_requests,
                AVG(response_time) as avg_response_time,
                COUNT(CASE WHEN status_code >= 400 THEN 1 END) as error_count
            FROM request_metrics 
            WHERE timestamp > NOW() - INTERVAL '5 minutes'
                AND country = $1
        `, [request.getHeader('x-akamai-edgescapecountry-code')]);
        
        // Add real-time metrics to response
        if (metrics.length > 0) {
            const stats = metrics[0];
            response.setHeader('X-Edge-Stats', JSON.stringify({
                requests: stats.total_requests,
                avgResponseTime: Math.round(stats.avg_response_time),
                errorRate: (stats.error_count / stats.total_requests * 100).toFixed(2)
            }));
        }
        
    } catch (error) {
        console.log('Metrics logging failed:', error.message);
    }
}

Advanced HarperDB Edge Patterns

// Complex business logic with joins and aggregations
export async function onClientRequest(request) {
    const db = new HarperDB({ endpoint: 'edge-local' });
    const userId = extractUserId(request);
    
    try {
        // Complex recommendation query at the edge
        const recommendations = await db.query(`
            WITH user_behavior AS (
                SELECT 
                    category,
                    COUNT(*) as view_count,
                    AVG(rating) as avg_rating
                FROM user_interactions 
                WHERE user_id = $1 
                    AND created_at > NOW() - INTERVAL '30 days'
                GROUP BY category
            ),
            trending_items AS (
                SELECT 
                    item_id,
                    category,
                    COUNT(*) as popularity_score
                FROM user_interactions 
                WHERE created_at > NOW() - INTERVAL '7 days'
                GROUP BY item_id, category
                ORDER BY popularity_score DESC
                LIMIT 20
            )
            SELECT 
                ti.item_id,
                ti.category,
                ti.popularity_score,
                ub.avg_rating,
                p.title,
                p.price
            FROM trending_items ti
            JOIN products p ON ti.item_id = p.id
            LEFT JOIN user_behavior ub ON ti.category = ub.category
            WHERE ub.view_count > 3 OR ub.view_count IS NULL
            ORDER BY 
                (ti.popularity_score * COALESCE(ub.avg_rating, 4.0)) DESC
            LIMIT 10
        `, [userId]);
        
        // Store recommendations in request context
        request.setVariable('RECOMMENDATIONS', JSON.stringify(recommendations));
        
        // Modify response based on recommendations
        if (recommendations.length > 0) {
            const topCategory = recommendations[0].category;
            request.setHeader('X-Recommended-Category', topCategory);
        }
        
    } catch (error) {
        console.log('Recommendation query failed:', error.message);
    }
}

// Real-time fraud detection at edge
export async function onClientRequest(request) {
    const db = new HarperDB({ endpoint: 'edge-local' });
    const clientIP = request.getHeader('x-forwarded-for');
    const userAgent = request.getHeader('user-agent');
    
    try {
        // Check for suspicious patterns
        const suspiciousActivity = await db.query(`
            SELECT 
                COUNT(DISTINCT user_id) as unique_users,
                COUNT(*) as total_requests,
                COUNT(DISTINCT user_agent) as unique_agents
            FROM request_log 
            WHERE ip_address = $1 
                AND timestamp > NOW() - INTERVAL '5 minutes'
        `, [clientIP]);
        
        if (suspiciousActivity.length > 0) {
            const activity = suspiciousActivity[0];
            
            // Fraud detection logic
            if (activity.total_requests > 100 || 
                activity.unique_users > 10 ||
                activity.unique_agents > 5) {
                
                // Log suspicious activity
                await db.query(`
                    INSERT INTO fraud_alerts (
                        ip_address, 
                        user_agent, 
                        alert_type, 
                        severity,
                        metadata
                    ) VALUES ($1, $2, $3, $4, $5)
                `, [
                    clientIP,
                    userAgent,
                    'rate_limit_exceeded',
                    'high',
                    JSON.stringify(activity)
                ]);
                
                // Rate limit response
                return new Response('Rate limit exceeded', {
                    status: 429,
                    headers: {
                        'Retry-After': '60',
                        'X-Fraud-Detection': 'triggered'
                    }
                });
            }
        }
        
        // Log normal request
        await db.query(`
            INSERT INTO request_log (
                ip_address, 
                user_agent, 
                user_id, 
                timestamp
            ) VALUES ($1, $2, $3, NOW())
        `, [clientIP, userAgent, extractUserId(request)]);
        
    } catch (error) {
        console.log('Fraud detection failed:', error.message);
    }
}

Chapter 6: Performance Revolution - Early Hints and Beyond

HTTP/2 Push and Early Hints Integration

HarperDB enables intelligent Early Hints based on stored user patterns:

export async function onClientRequest(request) {
    const db = new HarperDB({ endpoint: 'edge-local' });
    const userId = extractUserId(request);
    
    try {
        // Query user's typical resource patterns
        const resourcePattern = await db.query(`
            SELECT 
                resource_path,
                COUNT(*) as frequency,
                AVG(load_time) as avg_load_time
            FROM user_resource_usage 
            WHERE user_id = $1 
                AND last_accessed > NOW() - INTERVAL '7 days'
            GROUP BY resource_path
            HAVING COUNT(*) > 3
            ORDER BY frequency DESC, avg_load_time DESC
            LIMIT 5
        `, [userId]);
        
        // Generate Early Hints for frequently used resources
        if (resourcePattern.length > 0) {
            const hints = resourcePattern
                .map(r => `<${r.resource_path}>; rel=preload; as=script`)
                .join(', ');
            
            // Send Early Hints (HTTP 103)
            request.setHeader('Link', hints);
            request.setVariable('EARLY_HINTS_SENT', 'true');
        }
        
        // Smart preloading based on user tier
        const userTier = await db.query(`
            SELECT tier FROM user_profiles WHERE id = $1
        `, [userId]);
        
        if (userTier[0]?.tier === 'premium') {
            // Premium users get aggressive preloading
            request.setHeader('Link', 
                '</assets/premium.css>; rel=preload; as=style, ' +
                '</assets/premium.js>; rel=preload; as=script'
            );
        }
        
    } catch (error) {
        console.log('Early hints query failed:', error.message);
    }
}

Performance Analytics with HarperDB

// Real-time Core Web Vitals tracking
export async function onOriginResponse(request, response) {
    const db = new HarperDB({ endpoint: 'edge-local' });
    
    try {
        const startTime = request.getVariable('START_TIME');
        const endTime = Date.now();
        const responseTime = endTime - startTime;
        
        // Store detailed performance metrics
        await db.query(`
            INSERT INTO performance_metrics (
                user_id,
                page_path,
                country,
                device_type,
                connection_type,
                ttfb,
                response_size,
                cache_status,
                timestamp
            ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW())
        `, [
            extractUserId(request),
            request.path,
            request.getHeader('x-akamai-edgescapecountry-code'),
            request.device?.deviceType || 'unknown',
            request.getHeader('x-akamai-network-type'),
            responseTime,
            response.getHeader('content-length') || 0,
            response.getHeader('x-cache') || 'unknown'
        ]);
        
        // Real-time performance alerts
        if (responseTime > 2000) {
            await db.query(`
                INSERT INTO performance_alerts (
                    alert_type,
                    severity,
                    page_path,
                    response_time,
                    country,
                    timestamp
                ) VALUES ($1, $2, $3, $4, $5, NOW())
            `, [
                'slow_response',
                responseTime > 5000 ? 'critical' : 'warning',
                request.path,
                responseTime,
                request.getHeader('x-akamai-edgescapecountry-code')
            ]);
        }
        
        // Add performance headers for monitoring
        response.setHeader('Server-Timing', 
            `edge;dur=${responseTime}, ` +
            `db;dur=${Date.now() - endTime}`
        );
        
    } catch (error) {
        console.log('Performance metrics failed:', error.message);
    }
}

Chapter 7: Integration Ecosystem

Fermyon Spin + HarperDB + Akamai Delivery

The ultimate edge stack combines compute, data, and delivery:

// Fermyon Spin component with HarperDB integration
use anyhow::Result;
use spin_sdk::{
    http::{Request, Response, Method},
    http_component,
    postgres::{self, Decode},
};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct UserProfile {
    id: String,
    email: String,
    tier: String,
    preferences: serde_json::Value,
}

#[derive(Serialize, Deserialize)]
struct ApiResponse {
    data: serde_json::Value,
    meta: ResponseMeta,
}

#[derive(Serialize, Deserialize)]
struct ResponseMeta {
    edge_location: String,
    cache_status: String,
    processing_time: u64,
    database_hits: u32,
}

#[http_component]
fn handle_api_request(req: Request) -> Result<Response> {
    let start_time = std::time::Instant::now();
    
    match req.method() {
        &Method::Get => handle_get_request(req, start_time),
        &Method::Post => handle_post_request(req, start_time),
        &Method::Put => handle_put_request(req, start_time),
        _ => Ok(Response::builder()
            .status(405)
            .body(Some("Method not allowed".into()))?),
    }
}

fn handle_get_request(req: Request, start_time: std::time::Instant) -> Result<Response> {
    // Extract user context from Akamai headers
    let country = req.headers()
        .get("akamai-edgescape-country-code")
        .unwrap_or("US");
    
    let user_id = extract_user_id(&req)?;
    
    // Connect to HarperDB at edge
    let conn = postgres::connect("harperdb-edge-local")?;
    
    // Complex edge query with performance optimization
    let user_data: Vec<UserProfile> = conn
        .query(
            r#"
            SELECT 
                up.id, up.email, up.tier, up.preferences
            FROM user_profiles up
            WHERE up.id = $1 
                AND up.active = true
            "#,
            &[&user_id]
        )?
        .rows
        .decode()?;
    
    if let Some(user) = user_data.first() {
        // Business logic based on user tier and location
        let response_data = match (user.tier.as_str(), country) {
            ("enterprise", _) => generate_enterprise_response(&user, &conn)?,
            ("premium", "US") => generate_premium_us_response(&user, &conn)?,
            ("premium", _) => generate_premium_global_response(&user, &conn)?,
            _ => generate_standard_response(&user, &conn)?,
        };
        
        let processing_time = start_time.elapsed().as_millis() as u64;
        
        let api_response = ApiResponse {
            data: response_data,
            meta: ResponseMeta {
                edge_location: country.to_string(),
                cache_status: "MISS".to_string(), // Dynamic content
                processing_time,
                database_hits: 1,
            },
        };
        
        Ok(Response::builder()
            .status(200)
            .header("content-type", "application/json")
            .header("cache-control", "private, max-age=60") // Personalized cache
            .header("server-timing", &format!("edge;dur={}", processing_time))
            .body(Some(serde_json::to_string(&api_response)?.into()))?)
            
    } else {
        Ok(Response::builder()
            .status(404)
            .body(Some("User not found".into()))?)
    }
}

fn generate_enterprise_response(
    user: &UserProfile, 
    conn: &postgres::Connection
) -> Result<serde_json::Value> {
    // Enterprise users get real-time analytics
    let analytics: Vec<serde_json::Value> = conn
        .query(
            r#"
            SELECT 
                DATE_TRUNC('hour', timestamp) as hour,
                COUNT(*) as requests,
                AVG(response_time) as avg_response_time
            FROM request_metrics 
            WHERE user_id = $1 
                AND timestamp > NOW() - INTERVAL '24 hours'
            GROUP BY hour
            ORDER BY hour DESC
            "#,
            &[&user.id]
        )?
        .rows
        .decode()?;
    
    Ok(json!({
        "user": user,
        "analytics": analytics,
        "features": ["real_time_analytics", "custom_dashboards", "api_access"],
        "limits": {
            "api_calls_per_hour": 10000,
            "storage_gb": 1000
        }
    }))
}

fn generate_premium_us_response(
    user: &UserProfile, 
    conn: &postgres::Connection
) -> Result<serde_json::Value> {
    // Premium US users get additional features
    let recent_activity: Vec<serde_json::Value> = conn
        .query(
            r#"
            SELECT activity_type, timestamp, metadata
            FROM user_activity 
            WHERE user_id = $1 
                AND timestamp > NOW() - INTERVAL '7 days'
            ORDER BY timestamp DESC
            LIMIT 20
            "#,
            &[&user.id]
        )?
        .rows
        .decode()?;
    
    Ok(json!({
        "user": user,
        "recent_activity": recent_activity,
        "features": ["priority_support", "advanced_analytics", "beta_features"],
        "limits": {
            "api_calls_per_hour": 5000,
            "storage_gb": 100
        }
    }))
}

// Helper function to extract user ID from JWT or session
fn extract_user_id(req: &Request) -> Result<String> {
    // Implementation would decode JWT or validate session
    // For demo purposes, assume it's in a header
    req.headers()
        .get("x-user-id")
        .unwrap_or("anonymous")
        .to_string()
        .parse()
        .map_err(|_| anyhow::anyhow!("Invalid user ID"))
}

Complete Edge Application Architecture

# Complete edge application deployment
apiVersion: spin.fermyon.dev/v1
kind: SpinApplication
metadata:
  name: edge-api-with-database
spec:
  components:
    - name: user-api
      source: 
        wasm: user-api.wasm
      trigger:
        route: "/api/users/..."
      config:
        database_url: "harperdb://edge-local"
        cache_ttl: "300"
    
    - name: analytics-api  
      source:
        wasm: analytics-api.wasm
      trigger:
        route: "/api/analytics/..."
      config:
        database_url: "harperdb://edge-local"
        aggregation_window: "5m"
    
    - name: real-time-processor
      source:
        wasm: processor.wasm
      trigger:
        route: "/api/events"
      config:
        database_url: "harperdb://edge-local"
        batch_size: "100"

---
# Akamai Property Configuration
apiVersion: akamai.com/v1
kind: Property  
metadata:
  name: edge-application
spec:
  hostnames:
    - api.example.com
  
  rules:
    - name: "Route to Spin Applications"
      criteria:
        - pathMatch: "/api/*"
      behaviors:
        - edgeWorker:
            id: "spin-router-worker"
        - origin:
            hostname: "fermyon-spin.edge.akamai.com"
    
    - name: "Database Optimization"
      criteria:
        - pathMatch: "/api/users/*"
      behaviors:
        - caching:
            behavior: "NO_STORE" # Personalized data
        - prefresh: true
        - harperDBConfig:
            connectionPool: 10
            queryTimeout: "50ms"
    
    - name: "Performance Headers"
      behaviors:
        - modifyOutgoingResponseHeader:
            action: "ADD"
            headerName: "Server-Timing"
            headerValue: "akamai;dur={{builtin.AK_TIME_TAKEN}}"

Chapter 8: The Performance Impact

Before and After: Real Numbers

Let me share some real performance metrics from edge database implementations:

Traditional Architecture (Origin Database):

API Response Time: 250ms average
- Akamai Edge: 15ms
- Internet Transit: 80ms  
- Origin Processing: 45ms
- Database Query: 110ms

Edge Database Architecture (HarperDB):

API Response Time: 35ms average
- Akamai Edge: 15ms
- Local DB Query: 5ms
- Business Logic: 15ms

Performance Improvement: 86% faster response times

Cache Hit Rates with Edge Data

Edge databases dramatically improve cache efficiency:

// Smart caching with edge database context
export async function onOriginResponse(request, response) {
    const db = new HarperDB({ endpoint: 'edge-local' });
    
    try {
        // Query user's caching preferences
        const cacheSettings = await db.query(`
            SELECT 
                up.tier,
                up.preferences->>'cache_preference' as cache_pref,
                COUNT(ur.id) as request_frequency
            FROM user_profiles up
            LEFT JOIN user_requests ur ON up.id = ur.user_id 
                AND ur.created_at > NOW() - INTERVAL '1 hour'
            WHERE up.id = $1
            GROUP BY up.tier, up.preferences
        `, [extractUserId(request)]);
        
        if (cacheSettings.length > 0) {
            const settings = cacheSettings[0];
            
            // Dynamic TTL based on user behavior and tier
            let cacheTTL = 300; // Default 5 minutes
            
            if (settings.tier === 'enterprise') {
                // Enterprise users need fresh data
                cacheTTL = 60;
            } else if (settings.request_frequency > 10) {
                // Frequent users get longer cache
                cacheTTL = 900; // 15 minutes
            }
            
            // Personalized cache headers
            response.setHeader('Edge-Control', 
                `cache-maxage=${cacheTTL}, downstream-ttl=${cacheTTL/2}`);
            
            response.setHeader('Vary', 
                'X-User-Tier, X-User-ID, X-Request-Frequency');
        }
        
    } catch (error) {
        // Fallback to conservative caching
        response.setHeader('Edge-Control', 'cache-maxage=60');
    }
}

Global Consistency vs. Edge Performance

One of the biggest challenges with edge databases is maintaining consistency across 3,000+ locations. HarperDB's approach:

// Multi-region consistency with conflict resolution
export async function onClientRequest(request) {
    const db = new HarperDB({ 
        endpoint: 'edge-local',
        consistency: 'eventual' // or 'strong' for critical data
    });
    
    const userId = extractUserId(request);
    
    try {
        // Read with consistency preference
        const userData = await db.query(`
            SELECT * FROM user_profiles 
            WHERE id = $1
            -- HarperDB automatically handles read consistency
        `, [userId], {
            readPreference: 'local', // Prefer local edge data
            maxStaleness: 30000 // Accept data up to 30s old
        });
        
        if (userData.length === 0) {
            // Fallback to authoritative region
            const authData = await db.query(`
                SELECT * FROM user_profiles 
                WHERE id = $1
            `, [userId], {
                readPreference: 'primary' // Force read from primary region
            });
            
            // Cache result locally for future requests
            if (authData.length > 0) {
                await db.query(`
                    INSERT INTO user_profiles 
                    SELECT * FROM TEMP_TABLE($1)
                    ON CONFLICT (id) DO UPDATE SET
                        email = EXCLUDED.email,
                        tier = EXCLUDED.tier,
                        updated_at = EXCLUDED.updated_at
                `, [JSON.stringify(authData[0])]);
            }
        }
        
    } catch (error) {
        console.log('Distributed query failed:', error.message);
    }
}

Chapter 9: Developer Experience Revolution

From Configuration to Code

The evolution from XML configuration to full database programming represents a fundamental shift in how we build edge applications:

2003: XML Configuration

<cache-rule pattern="*.json" ttl="300" />

2024: SQL + Code

-- Dynamic TTL based on user behavior
UPDATE response_cache SET 
    ttl = CASE 
        WHEN user_tier = 'enterprise' THEN 60
        WHEN request_frequency > 10 THEN 900
        ELSE 300
    END
WHERE user_id = $1;

Edge Development Workflow

Modern edge development with databases:

# 1. Local development with HarperDB
harper-cli start --edge-mode
spin new http-rust edge-api
cd edge-api

# 2. Database schema management
harper-cli schema apply --file schema.sql

# 3. Development with live database
spin build && spin up --database harperdb://localhost:9925

# 4. Deploy to Akamai edge
akamai property update --edge-worker edge-api.wasm
harper-cli replicate --to akamai-edge-regions

Testing Edge Database Applications

// Comprehensive edge testing
import { EdgeDB } from '@akamai/edge-db-test';
import { EdgeWorker } from '@akamai/edge-worker-test';

describe('Edge API with Database', () => {
    let edgeDB, edgeWorker;
    
    beforeEach(async () => {
        // Initialize test edge database
        edgeDB = new EdgeDB({
            engine: 'harperdb',
            mode: 'test'
        });
        
        await edgeDB.loadSchema('./schema.sql');
        await edgeDB.loadFixtures('./fixtures.json');
        
        edgeWorker = new EdgeWorker({
            script: './edge-worker.js',
            database: edgeDB
        });
    });
    
    test('user profile retrieval', async () => {
        const request = new Request('/api/user/123', {
            headers: {
                'x-akamai-edgescape-country-code': 'US',
                'authorization': 'Bearer test-token'
            }
        });
        
        const response = await edgeWorker.fetch(request);
        
        expect(response.status).toBe(200);
        
        const data = await response.json();
        expect(data.user.tier).toBe('premium');
        expect(data.user.preferences.currency).toBe('USD');
        
        // Verify database interaction
        const queryLog = edgeDB.getQueryLog();
        expect(queryLog).toContain('SELECT * FROM user_profiles');
    });
    
    test('performance under load', async () => {
        const requests = Array(100).fill().map((_, i) => 
            edgeWorker.fetch(new Request(`/api/user/${i}`))
        );
        
        const start = Date.now();
        const responses = await Promise.all(requests);
        const duration = Date.now() - start;
        
        // All requests successful
        expect(responses.every(r => r.ok)).toBe(true);
        
        // Average response time under 50ms
        expect(duration / 100).toBeLessThan(50);
        
        // Database connection pool handling
        expect(edgeDB.getConnectionCount()).toBeLessThan(10);
    });
});

Chapter 10: The Future of Edge Data

What's Coming Next

The edge data evolution is far from over. Here's what's on the horizon:

1. Edge AI/ML Integration

// Future: ML models querying edge databases
export async function onClientRequest(request) {
    const db = new HarperDB({ endpoint: 'edge-local' });
    const ml = new EdgeML({ model: 'recommendation-v2' });
    
    // Real-time feature engineering from database
    const features = await db.query(`
        SELECT 
            user_tier,
            avg_session_duration,
            preferred_categories,
            recent_purchases
        FROM user_ml_features 
        WHERE user_id = $1
    `, [extractUserId(request)]);
    
    // Run inference at edge
    const recommendations = await ml.predict(features[0]);
    
    request.setVariable('ML_RECOMMENDATIONS', 
        JSON.stringify(recommendations));
}

2. Edge-Native GraphQL

# Future: GraphQL resolvers running at edge with local database
type Query {
  user(id: ID!): User @edgeCache(ttl: 300)
  recommendations(userId: ID!, limit: Int = 10): [Product] 
    @edgeCompute @database(source: "harperdb")
}

type User {
  id: ID!
  profile: UserProfile @database(table: "user_profiles")
  preferences: UserPreferences @database(table: "user_preferences")
  recentActivity: [Activity] @database(
    query: "SELECT * FROM activities WHERE user_id = $userId ORDER BY created_at DESC LIMIT 10"
  )
}

3. Blockchain Integration at Edge

// Future: Blockchain verification at edge
export async function onClientRequest(request) {
    const db = new HarperDB({ endpoint: 'edge-local' });
    const blockchain = new EdgeBlockchain({ network: 'ethereum' });
    
    const walletAddress = request.getHeader('x-wallet-address');
    
    // Verify NFT ownership at edge
    const nftOwnership = await blockchain.verifyOwnership({
        wallet: walletAddress,
        contract: '0x...',
        tokenId: extractTokenId(request)
    });
    
    if (nftOwnership.verified) {
        // Grant access based on NFT properties
        const nftMetadata = await db.query(`
            SELECT access_level, features 
            FROM nft_access_rights 
            WHERE contract_address = $1 AND token_id = $2
        `, [nftOwnership.contract, nftOwnership.tokenId]);
        
        request.setVariable('ACCESS_LEVEL', nftMetadata[0].access_level);
    }
}

Conclusion: The Data-Driven Edge

From XML metadata to SQL databases, Akamai's edge data evolution reflects the broader transformation of the internet from static content delivery to dynamic, personalized applications.

Key Takeaways

  1. Performance: Edge databases reduce API response times by 80%+
  2. Personalization: Real-time user context enables true 1:1 experiences
  3. Resilience: Edge-local data provides automatic failover and redundancy
  4. Developer Experience: SQL + modern languages = familiar programming model
  5. Global Scale: 3,000+ edge locations with consistent data access

The Compound Effect

When you combine:

  • Fermyon Spin: WebAssembly compute at the edge
  • HarperDB: Full SQL databases at the edge
  • Akamai Delivery: Global CDN with intelligent caching
  • EdgeWorkers: JavaScript orchestration layer

You get an edge computing platform that can handle anything from simple APIs to complex, data-driven applications.

Getting Started Today

Ready to build with edge databases? Here's your quick start:

# 1. Set up local development
npm install -g @akamai/edgeworkers-cli
npm install -g @harperdb/cli

# 2. Create your first edge database app
mkdir edge-app && cd edge-app
akamai ew create-app --template database-integration

# 3. Deploy to Akamai edge
akamai ew deploy --database harperdb

The edge isn't just about speed anymore—it's about bringing your entire application stack, including databases, as close to your users as possible.

The future of web architecture is distributed, intelligent, and blazingly fast. And it's available today.


Ready to dive deeper? Explore:

The edge revolution is here. Join us.