S

Examples

Real-world examples and implementation patterns for SimplyStack

Overview

This page contains practical examples and implementation patterns for common use cases with SimplyStack. Each example includes complete code that you can copy and adapt for your projects.

💡 Getting the Most from Examples

  • • All examples assume you have a valid API key
  • • Error handling is included in each example
  • • TypeScript types are used throughout
  • • Examples are production-ready patterns

Blog Management System

A complete blog management system with content creation, SEO optimization, and publishing workflows.

Complete Blog Manager Class

// blog-manager.ts
import { SimplyStackSDK } from "@simplystack-org/sdk";
import type { BlogPost } from "@simplystack-org/sdk";

interface BlogDraft {
  title: string;
  content: string;
  excerpt?: string;
  tags?: string[];
  seoData?: {
    meta_title?: string;
    meta_description?: string;
    meta_keywords?: string[];
    canonical_url?: string;
  };
}

class BlogManager {
  private sdk: SimplyStackSDK;

  constructor(apiKey: string) {
    this.sdk = new SimplyStackSDK(apiKey);
  }

  // Create a draft blog post
  async createDraft(draft: BlogDraft): Promise<BlogPost> {
    const { data: post, error } = await this.sdk.createBlogPost({
      title: draft.title,
      content: draft.content,
      excerpt: draft.excerpt || this.extractExcerpt(draft.content),
      tags: draft.tags || [],
      status: "draft",
      ...draft.seoData,
    });

    if (error) {
      throw new Error(`Failed to create draft: ${error}`);
    }

    return post!;
  }

  // Publish a blog post with SEO optimization
  async publishPost(draft: BlogDraft): Promise<BlogPost> {
    // Auto-generate SEO if not provided
    const seoData = {
      meta_title: draft.seoData?.meta_title || `${draft.title} | Your Blog`,
      meta_description: draft.seoData?.meta_description || 
        this.extractExcerpt(draft.content, 150),
      meta_keywords: draft.seoData?.meta_keywords || 
        this.extractKeywords(draft.content, draft.tags),
      canonical_url: draft.seoData?.canonical_url,
    };

    const { data: post, error } = await this.sdk.createBlogPost({
      title: draft.title,
      content: draft.content,
      excerpt: draft.excerpt || this.extractExcerpt(draft.content),
      tags: draft.tags || [],
      status: "published",
      ...seoData,
    });

    if (error) {
      throw new Error(`Failed to publish post: ${error}`);
    }

    // Log the publication
    await this.logPostAction("published", post!.id, {
      title: post!.title,
      tags: post!.tags,
    });

    return post!;
  }

  // Get all drafts
  async getDrafts(): Promise<BlogPost[]> {
    const { data: posts, error } = await this.sdk.getBlogPosts({
      status: "draft",
    });

    if (error) {
      throw new Error(`Failed to fetch drafts: ${error}`);
    }

    return posts || [];
  }

  // Get published posts with pagination
  async getPublishedPosts(page = 1, limit = 10): Promise<{
    posts: BlogPost[];
    hasMore: boolean;
  }> {
    const { data: posts, error } = await this.sdk.getBlogPosts({
      status: "published",
      limit: limit + 1, // Get one extra to check if there are more
      offset: (page - 1) * limit,
    });

    if (error) {
      throw new Error(`Failed to fetch published posts: ${error}`);
    }

    const hasMore = (posts?.length || 0) > limit;
    const actualPosts = hasMore ? posts!.slice(0, -1) : (posts || []);

    return {
      posts: actualPosts,
      hasMore,
    };
  }

  // Search posts by tags
  async getPostsByTag(tag: string): Promise<BlogPost[]> {
    const { data: posts, error } = await this.sdk.getBlogPosts();

    if (error) {
      throw new Error(`Failed to fetch posts: ${error}`);
    }

    return (posts || []).filter(post => 
      post.tags.includes(tag)
    );
  }

  // Update and republish a post
  async updatePost(postId: string, updates: Partial<BlogDraft>): Promise<BlogPost> {
    const updateData: any = {};

    if (updates.title) updateData.title = updates.title;
    if (updates.content) updateData.content = updates.content;
    if (updates.excerpt) updateData.excerpt = updates.excerpt;
    if (updates.tags) updateData.tags = updates.tags;
    if (updates.seoData) {
      Object.assign(updateData, updates.seoData);
    }

    const { data: post, error } = await this.sdk.updateBlogPost(postId, updateData);

    if (error) {
      throw new Error(`Failed to update post: ${error}`);
    }

    // Log the update
    await this.logPostAction("updated", postId, {
      changes: Object.keys(updateData),
    });

    return post!;
  }

  // Archive a post (soft delete)
  async archivePost(postId: string): Promise<void> {
    const { error } = await this.sdk.updateBlogPost(postId, {
      status: "archived",
    });

    if (error) {
      throw new Error(`Failed to archive post: ${error}`);
    }

    await this.logPostAction("archived", postId);
  }

  // Permanently delete a post
  async deletePost(postId: string): Promise<void> {
    const { error } = await this.sdk.deleteBlogPost(postId);

    if (error) {
      throw new Error(`Failed to delete post: ${error}`);
    }

    await this.logPostAction("deleted", postId);
  }

  // Private helper methods
  private extractExcerpt(content: string, maxLength = 200): string {
    // Strip HTML tags and get plain text
    const text = content.replace(/<[^>]*>/g, '').trim();
    
    if (text.length <= maxLength) {
      return text;
    }

    return text.substring(0, maxLength).trim() + '...';
  }

  private extractKeywords(content: string, tags?: string[]): string[] {
    const keywords = tags || [];
    
    // Simple keyword extraction from content
    const text = content.replace(/<[^>]*>/g, '').toLowerCase();
    const words = text.split(/s+/);
    
    // Get frequent words (this is very basic - you might want to use a proper NLP library)
    const wordCount: Record<string, number> = {};
    words.forEach(word => {
      if (word.length > 3 && !this.isStopWord(word)) {
        wordCount[word] = (wordCount[word] || 0) + 1;
      }
    });

    const frequentWords = Object.entries(wordCount)
      .sort(([, a], [, b]) => b - a)
      .slice(0, 3)
      .map(([word]) => word);

    return [...keywords, ...frequentWords];
  }

  private isStopWord(word: string): boolean {
    const stopWords = ['the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by'];
    return stopWords.includes(word);
  }

  private async logPostAction(action: string, postId: string, metadata?: any): Promise<void> {
    try {
      await this.sdk.createLog({
        level: "info",
        message: `Blog post ${action}`,
        service: "blog-manager",
        metadata: {
          action,
          postId,
          timestamp: new Date().toISOString(),
          ...metadata,
        },
      });
    } catch (error) {
      // Don't fail the main operation if logging fails
      console.warn("Failed to log post action:", error);
    }
  }
}

export default BlogManager;

Usage Example

// Using the BlogManager
import BlogManager from './blog-manager';

const blogManager = new BlogManager(process.env.SIMPLYSTACK_API_KEY!);

async function createAndPublishPost() {
  try {
    // Create a draft first
    const draft = await blogManager.createDraft({
      title: "Building Scalable APIs with SimplyStack",
      content: `
        <h1>Introduction</h1>
        <p>Building scalable APIs has never been easier with SimplyStack...</p>
        <h2>Key Features</h2>
        <ul>
          <li>Automatic scaling</li>
          <li>Built-in authentication</li>
          <li>Real-time logging</li>
        </ul>
      `,
      tags: ["api", "development", "tutorial"],
      seoData: {
        meta_title: "Complete Guide to Building APIs | SimplyStack",
        meta_description: "Learn how to build scalable, secure APIs using SimplyStack's powerful platform",
        meta_keywords: ["API development", "backend", "scalability", "SimplyStack"],
      }
    });

    console.log("Draft created:", draft.id);

    // Publish the post
    const published = await blogManager.publishPost({
      title: draft.title,
      content: draft.content,
      tags: draft.tags,
      seoData: {
        canonical_url: `https://yourblog.com/posts/${draft.id}`,
      }
    });

    console.log("Post published:", published.id);

    // Get recent posts
    const { posts, hasMore } = await blogManager.getPublishedPosts(1, 5);
    console.log(`Found ${posts.length} recent posts, hasMore: ${hasMore}`);

  } catch (error) {
    console.error("Blog operation failed:", error);
  }
}

createAndPublishPost();

Advanced File Upload System

A comprehensive file upload system with validation, progress tracking, and metadata management.

File Upload Manager

// file-upload-manager.ts
import { SimplyStackSDK } from "@simplystack-org/sdk";
import type { StorageAsset } from "@simplystack-org/sdk";

interface UploadConfig {
  maxSize?: number; // in bytes
  allowedTypes?: string[];
  generateThumbnails?: boolean;
  addWatermark?: boolean;
}

interface UploadProgress {
  file: File;
  progress: number;
  status: 'pending' | 'uploading' | 'processing' | 'completed' | 'error';
  error?: string;
  asset?: StorageAsset;
}

class FileUploadManager {
  private sdk: SimplyStackSDK;
  private defaultConfig: UploadConfig = {
    maxSize: 10 * 1024 * 1024, // 10MB
    allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
    generateThumbnails: true,
    addWatermark: false,
  };

  constructor(apiKey: string, config?: Partial<UploadConfig>) {
    this.sdk = new SimplyStackSDK(apiKey);
    this.defaultConfig = { ...this.defaultConfig, ...config };
  }

  // Validate file before upload
  validateFile(file: File, config?: UploadConfig): { valid: boolean; error?: string } {
    const finalConfig = { ...this.defaultConfig, ...config };

    // Check file size
    if (finalConfig.maxSize && file.size > finalConfig.maxSize) {
      return {
        valid: false,
        error: `File size (${this.formatFileSize(file.size)}) exceeds maximum allowed size (${this.formatFileSize(finalConfig.maxSize)})`,
      };
    }

    // Check file type
    if (finalConfig.allowedTypes && !finalConfig.allowedTypes.includes(file.type)) {
      return {
        valid: false,
        error: `File type ${file.type} is not allowed. Allowed types: ${finalConfig.allowedTypes.join(', ')}`,
      };
    }

    return { valid: true };
  }

  // Upload single file with metadata
  async uploadFile(
    file: File,
    label?: string,
    metadata?: Record<string, any>
  ): Promise<StorageAsset> {
    // Validate file
    const validation = this.validateFile(file);
    if (!validation.valid) {
      throw new Error(validation.error);
    }

    // Prepare metadata
    const enhancedMetadata = {
      originalName: file.name,
      fileSize: file.size,
      mimeType: file.type,
      uploadedAt: new Date().toISOString(),
      uploadedViaAPI: true,
      category: this.categorizeFile(file),
      ...metadata,
    };

    // Upload file
    const { data: asset, error } = await this.sdk.uploadFile(
      file,
      label || file.name
    );

    if (error) {
      throw new Error(`Upload failed: ${error}`);
    }

    // Update with enhanced metadata
    try {
      const { data: updatedAsset } = await this.sdk.updateStorageAsset(
        asset!.id,
        {
          metadata: enhancedMetadata,
        }
      );

      const finalAsset = updatedAsset || asset!;

      // Log upload
      await this.logUpload(finalAsset, 'success');

      return finalAsset;
    } catch (updateError) {
      // If metadata update fails, return original asset
      console.warn("Failed to update metadata:", updateError);
      await this.logUpload(asset!, 'success', { metadataUpdateFailed: true });
      return asset!;
    }
  }

  // Upload multiple files with progress tracking
  async uploadMultipleFiles(
    files: File[],
    onProgress?: (progress: UploadProgress[]) => void
  ): Promise<UploadProgress[]> {
    const uploadProgresses: UploadProgress[] = files.map(file => ({
      file,
      progress: 0,
      status: 'pending' as const,
    }));

    // Report initial progress
    onProgress?.(uploadProgresses);

    // Upload files sequentially (you could make this parallel)
    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      const progress = uploadProgresses[i];

      try {
        // Update status to uploading
        progress.status = 'uploading';
        progress.progress = 0;
        onProgress?.(uploadProgresses);

        // Validate file
        const validation = this.validateFile(file);
        if (!validation.valid) {
          progress.status = 'error';
          progress.error = validation.error;
          onProgress?.(uploadProgresses);
          continue;
        }

        // Upload file
        progress.progress = 50; // Simulate progress
        onProgress?.(uploadProgresses);

        const asset = await this.uploadFile(file);

        // Complete
        progress.status = 'completed';
        progress.progress = 100;
        progress.asset = asset;
        onProgress?.(uploadProgresses);

      } catch (error) {
        progress.status = 'error';
        progress.error = error instanceof Error ? error.message : 'Upload failed';
        onProgress?.(uploadProgresses);
      }
    }

    return uploadProgresses;
  }

  // Get uploaded files by category
  async getFilesByCategory(category: string): Promise<StorageAsset[]> {
    const { data: assets, error } = await this.sdk.getStorageAssets();

    if (error) {
      throw new Error(`Failed to fetch assets: ${error}`);
    }

    return (assets || []).filter(asset => 
      asset.metadata.category === category
    );
  }

  // Get user's recent uploads
  async getRecentUploads(limit = 20): Promise<StorageAsset[]> {
    const { data: assets, error } = await this.sdk.getStorageAssets();

    if (error) {
      throw new Error(`Failed to fetch assets: ${error}`);
    }

    return (assets || [])
      .filter(asset => asset.metadata.uploadedViaAPI)
      .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
      .slice(0, limit);
  }

  // Delete file with cleanup
  async deleteFile(assetId: string): Promise<void> {
    const { error } = await this.sdk.deleteStorageAsset(assetId);

    if (error) {
      throw new Error(`Failed to delete file: ${error}`);
    }

    // Log deletion
    await this.sdk.createLog({
      level: "info",
      message: "File deleted",
      service: "file-manager",
      metadata: {
        assetId,
        deletedAt: new Date().toISOString(),
      },
    });
  }

  // Private helper methods
  private categorizeFile(file: File): string {
    if (file.type.startsWith('image/')) return 'images';
    if (file.type.startsWith('video/')) return 'videos';
    if (file.type.startsWith('audio/')) return 'audio';
    if (file.type.includes('pdf')) return 'documents';
    if (file.type.includes('text/')) return 'documents';
    return 'other';
  }

  private formatFileSize(bytes: number): string {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }

  private async logUpload(asset: StorageAsset, status: string, metadata?: any): Promise<void> {
    try {
      await this.sdk.createLog({
        level: status === 'success' ? 'info' : 'error',
        message: `File upload ${status}`,
        service: "file-manager",
        metadata: {
          assetId: asset.id,
          fileName: asset.metadata.originalName || asset.label,
          fileSize: asset.size,
          mimeType: asset.mime_type,
          category: asset.metadata.category,
          ...metadata,
        },
      });
    } catch (error) {
      console.warn("Failed to log upload:", error);
    }
  }
}

export default FileUploadManager;

React Component Example

// FileUploadComponent.tsx
import React, { useState, useCallback } from 'react';
import FileUploadManager from './file-upload-manager';

interface UploadProgress {
  file: File;
  progress: number;
  status: 'pending' | 'uploading' | 'processing' | 'completed' | 'error';
  error?: string;
  asset?: any;
}

const FileUploadComponent: React.FC = () => {
  const [dragActive, setDragActive] = useState(false);
  const [uploads, setUploads] = useState<UploadProgress[]>([]);
  const [fileManager] = useState(() => 
    new FileUploadManager(process.env.NEXT_PUBLIC_SIMPLYSTACK_API_KEY!)
  );

  const handleFiles = useCallback(async (files: FileList) => {
    const fileArray = Array.from(files);
    
    await fileManager.uploadMultipleFiles(
      fileArray,
      (progress) => setUploads([...progress])
    );
  }, [fileManager]);

  const handleDrag = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
  }, []);

  const handleDragIn = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
      setDragActive(true);
    }
  }, []);

  const handleDragOut = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setDragActive(false);
  }, []);

  const handleDrop = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setDragActive(false);
    
    if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
      handleFiles(e.dataTransfer.files);
    }
  }, [handleFiles]);

  const handleFileInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files.length > 0) {
      handleFiles(e.target.files);
    }
  }, [handleFiles]);

  return (
    <div className="max-w-2xl mx-auto p-6">
      <div
        className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
          dragActive 
            ? 'border-blue-500 bg-blue-50' 
            : 'border-gray-300 hover:border-gray-400'
        }`}
        onDragEnter={handleDragIn}
        onDragLeave={handleDragOut}
        onDragOver={handleDrag}
        onDrop={handleDrop}
      >
        <input
          type="file"
          multiple
          accept="image/*"
          onChange={handleFileInput}
          className="hidden"
          id="file-input"
        />
        <label
          htmlFor="file-input"
          className="cursor-pointer flex flex-col items-center"
        >
          <svg className="w-12 h-12 text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
          </svg>
          <span className="text-lg font-medium text-gray-700 mb-2">
            Drop files here or click to browse
          </span>
          <span className="text-sm text-gray-500">
            Supports: JPEG, PNG, GIF, WebP (max 10MB)
          </span>
        </label>
      </div>

      {uploads.length > 0 && (
        <div className="mt-6 space-y-3">
          <h3 className="text-lg font-semibold">Upload Progress</h3>
          {uploads.map((upload, index) => (
            <div key={index} className="border rounded-lg p-4">
              <div className="flex items-center justify-between mb-2">
                <span className="font-medium">{upload.file.name}</span>
                <span className={`text-sm px-2 py-1 rounded ${
                  upload.status === 'completed' ? 'bg-green-100 text-green-800' :
                  upload.status === 'error' ? 'bg-red-100 text-red-800' :
                  'bg-blue-100 text-blue-800'
                }`}>
                  {upload.status}
                </span>
              </div>
              
              {upload.status !== 'error' && (
                <div className="w-full bg-gray-200 rounded-full h-2">
                  <div
                    className="bg-blue-600 h-2 rounded-full transition-all duration-300"
                    style={{ width: `${upload.progress}%` }}
                  />
                </div>
              )}
              
              {upload.error && (
                <div className="text-red-600 text-sm mt-2">{upload.error}</div>
              )}
              
              {upload.asset && (
                <div className="text-green-600 text-sm mt-2">
                  ✓ Uploaded successfully
                  {upload.asset.metadata.publicUrl && (
                    <a 
                      href={upload.asset.metadata.publicUrl} 
                      target="_blank" 
                      rel="noopener noreferrer"
                      className="ml-2 underline"
                    >
                      View
                    </a>
                  )}
                </div>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

export default FileUploadComponent;

Advanced Logging System

A comprehensive logging system with structured logging, log levels, and analytics.

Advanced Logger Class

// advanced-logger.ts
import { SimplyStackSDK } from "@simplystack-org/sdk";
import type { LogEntry } from "@simplystack-org/sdk";

type LogLevel = 'debug' | 'info' | 'warn' | 'error';

interface LogContext {
  userId?: string;
  sessionId?: string;
  requestId?: string;
  userAgent?: string;
  ip?: string;
  service: string;
}

interface LogMetrics {
  executionTime?: number;
  memoryUsage?: number;
  cpuUsage?: number;
  customMetrics?: Record<string, number>;
}

class AdvancedLogger {
  private sdk: SimplyStackSDK;
  private defaultContext: Partial<LogContext>;
  private logQueue: Array<{ level: LogLevel; message: string; context: LogContext; metadata?: any; metrics?: LogMetrics }> = [];
  private batchSize = 10;
  private batchTimeout = 5000; // 5 seconds
  private batchTimer?: NodeJS.Timeout;

  constructor(apiKey: string, defaultContext?: Partial<LogContext>) {
    this.sdk = new SimplyStackSDK(apiKey);
    this.defaultContext = defaultContext || {};
    this.startBatchTimer();
  }

  // Log with automatic context enrichment
  async log(
    level: LogLevel,
    message: string,
    context?: Partial<LogContext>,
    metadata?: any,
    metrics?: LogMetrics
  ): Promise<void> {
    const enrichedContext: LogContext = {
      ...this.defaultContext,
      ...context,
      service: context?.service || this.defaultContext.service || 'unknown',
    };

    const enrichedMetadata = {
      timestamp: new Date().toISOString(),
      environment: process.env.NODE_ENV || 'development',
      version: process.env.APP_VERSION || '1.0.0',
      ...metadata,
      context: enrichedContext,
      metrics,
    };

    // Add to batch queue
    this.logQueue.push({
      level,
      message,
      context: enrichedContext,
      metadata: enrichedMetadata,
      metrics,
    });

    // Process batch if it's full
    if (this.logQueue.length >= this.batchSize) {
      await this.processBatch();
    }
  }

  // Convenience methods
  async debug(message: string, context?: Partial<LogContext>, metadata?: any): Promise<void> {
    await this.log('debug', message, context, metadata);
  }

  async info(message: string, context?: Partial<LogContext>, metadata?: any): Promise<void> {
    await this.log('info', message, context, metadata);
  }

  async warn(message: string, context?: Partial<LogContext>, metadata?: any): Promise<void> {
    await this.log('warn', message, context, metadata);
  }

  async error(message: string, error?: Error, context?: Partial<LogContext>, metadata?: any): Promise<void> {
    const errorMetadata = {
      ...metadata,
      error: error ? {
        name: error.name,
        message: error.message,
        stack: error.stack,
      } : undefined,
    };

    await this.log('error', message, context, errorMetadata);
  }

  // Performance logging
  async logPerformance(
    operation: string,
    duration: number,
    context?: Partial<LogContext>,
    metrics?: LogMetrics
  ): Promise<void> {
    await this.log('info', `Performance: ${operation}`, context, {
      operation,
      duration,
      type: 'performance',
    }, {
      executionTime: duration,
      ...metrics,
    });
  }

  // User action logging
  async logUserAction(
    action: string,
    userId: string,
    context?: Partial<LogContext>,
    metadata?: any
  ): Promise<void> {
    await this.log('info', `User action: ${action}`, {
      ...context,
      userId,
    }, {
      action,
      type: 'user_action',
      ...metadata,
    });
  }

  // API request logging
  async logApiRequest(
    method: string,
    endpoint: string,
    statusCode: number,
    duration: number,
    context?: Partial<LogContext>,
    metadata?: any
  ): Promise<void> {
    const level: LogLevel = statusCode >= 500 ? 'error' : statusCode >= 400 ? 'warn' : 'info';
    
    await this.log(level, `API ${method} ${endpoint} - ${statusCode}`, context, {
      method,
      endpoint,
      statusCode,
      duration,
      type: 'api_request',
      ...metadata,
    }, {
      executionTime: duration,
    });
  }

  // Search logs with filters
  async searchLogs(filters: {
    level?: LogLevel;
    service?: string;
    userId?: string;
    startDate?: Date;
    endDate?: Date;
    keyword?: string;
  }): Promise<LogEntry[]> {
    const { data: logs, error } = await this.sdk.getLogs({
      level: filters.level,
      service: filters.service,
    });

    if (error) {
      throw new Error(`Failed to search logs: ${error}`);
    }

    let filteredLogs = logs || [];

    // Additional client-side filtering
    if (filters.userId) {
      filteredLogs = filteredLogs.filter(log => 
        log.metadata?.context?.userId === filters.userId
      );
    }

    if (filters.startDate) {
      filteredLogs = filteredLogs.filter(log => 
        new Date(log.created_at) >= filters.startDate!
      );
    }

    if (filters.endDate) {
      filteredLogs = filteredLogs.filter(log => 
        new Date(log.created_at) <= filters.endDate!
      );
    }

    if (filters.keyword) {
      const keyword = filters.keyword.toLowerCase();
      filteredLogs = filteredLogs.filter(log => 
        log.message.toLowerCase().includes(keyword)
      );
    }

    return filteredLogs;
  }

  // Get log analytics
  async getLogAnalytics(timeRange: 'hour' | 'day' | 'week' | 'month'): Promise<{
    totalLogs: number;
    logsByLevel: Record<LogLevel, number>;
    topServices: Array<{ service: string; count: number }>;
    errorRate: number;
    averageResponseTime?: number;
  }> {
    const { data: logs, error } = await this.sdk.getLogs();

    if (error) {
      throw new Error(`Failed to get logs for analytics: ${error}`);
    }

    const now = new Date();
    const timeRanges = {
      hour: 60 * 60 * 1000,
      day: 24 * 60 * 60 * 1000,
      week: 7 * 24 * 60 * 60 * 1000,
      month: 30 * 24 * 60 * 60 * 1000,
    };

    const cutoff = new Date(now.getTime() - timeRanges[timeRange]);
    const recentLogs = (logs || []).filter(log => 
      new Date(log.created_at) >= cutoff
    );

    const logsByLevel: Record<LogLevel, number> = {
      debug: 0,
      info: 0,
      warn: 0,
      error: 0,
    };

    const serviceCount: Record<string, number> = {};
    let totalResponseTime = 0;
    let responseTimeCount = 0;

    recentLogs.forEach(log => {
      logsByLevel[log.level as LogLevel]++;
      
      if (log.service) {
        serviceCount[log.service] = (serviceCount[log.service] || 0) + 1;
      }

      if (log.metadata?.metrics?.executionTime) {
        totalResponseTime += log.metadata.metrics.executionTime;
        responseTimeCount++;
      }
    });

    const topServices = Object.entries(serviceCount)
      .sort(([, a], [, b]) => b - a)
      .slice(0, 10)
      .map(([service, count]) => ({ service, count }));

    const errorRate = recentLogs.length > 0 
      ? (logsByLevel.error / recentLogs.length) * 100 
      : 0;

    const averageResponseTime = responseTimeCount > 0 
      ? totalResponseTime / responseTimeCount 
      : undefined;

    return {
      totalLogs: recentLogs.length,
      logsByLevel,
      topServices,
      errorRate,
      averageResponseTime,
    };
  }

  // Batch processing
  private async processBatch(): Promise<void> {
    if (this.logQueue.length === 0) return;

    const batch = [...this.logQueue];
    this.logQueue = [];

    // Process logs in parallel (but limit concurrency)
    const promises = batch.map(logData => 
      this.sdk.createLog({
        level: logData.level,
        message: logData.message,
        service: logData.context.service,
        metadata: logData.metadata,
      }).catch(error => {
        // If individual log fails, don't fail the whole batch
        console.error('Failed to send log:', error);
      })
    );

    await Promise.allSettled(promises);
  }

  private startBatchTimer(): void {
    this.batchTimer = setInterval(async () => {
      if (this.logQueue.length > 0) {
        await this.processBatch();
      }
    }, this.batchTimeout);
  }

  // Cleanup
  async flush(): Promise<void> {
    if (this.batchTimer) {
      clearInterval(this.batchTimer);
    }
    await this.processBatch();
  }
}

export default AdvancedLogger;

Usage with Express.js Middleware

// express-logging-middleware.ts
import express from 'express';
import AdvancedLogger from './advanced-logger';

const logger = new AdvancedLogger(process.env.SIMPLYSTACK_API_KEY!, {
  service: 'api-server',
});

// Request logging middleware
export const requestLogger = (req: express.Request, res: express.Response, next: express.NextFunction) => {
  const startTime = Date.now();
  
  // Generate request ID
  const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  req.requestId = requestId;

  // Log request start
  logger.info('Request started', {
    requestId,
    service: 'api-server',
    ip: req.ip,
    userAgent: req.get('User-Agent'),
  }, {
    method: req.method,
    url: req.originalUrl,
    headers: req.headers,
    query: req.query,
    body: req.method !== 'GET' ? req.body : undefined,
  });

  // Override res.end to log response
  const originalEnd = res.end;
  res.end = function(chunk: any, encoding?: string) {
    const duration = Date.now() - startTime;
    
    // Log request completion
    logger.logApiRequest(
      req.method,
      req.originalUrl,
      res.statusCode,
      duration,
      {
        requestId,
        service: 'api-server',
        ip: req.ip,
        userAgent: req.get('User-Agent'),
      },
      {
        responseHeaders: res.getHeaders(),
        responseSize: chunk ? chunk.length : 0,
      }
    );

    originalEnd.call(this, chunk, encoding);
  };

  next();
};

// Error logging middleware
export const errorLogger = (err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  logger.error('Request error', err, {
    requestId: req.requestId,
    service: 'api-server',
    ip: req.ip,
    userAgent: req.get('User-Agent'),
  }, {
    method: req.method,
    url: req.originalUrl,
    statusCode: res.statusCode,
  });

  next(err);
};

// Performance monitoring
export const performanceMonitor = async (operation: string, fn: () => Promise<any>) => {
  const startTime = Date.now();
  const startMemory = process.memoryUsage();
  
  try {
    const result = await fn();
    const duration = Date.now() - startTime;
    const endMemory = process.memoryUsage();
    
    await logger.logPerformance(operation, duration, {
      service: 'api-server',
    }, {
      executionTime: duration,
      memoryUsage: endMemory.heapUsed - startMemory.heapUsed,
    });
    
    return result;
  } catch (error) {
    const duration = Date.now() - startTime;
    
    await logger.error(`Performance error in ${operation}`, error as Error, {
      service: 'api-server',
    }, {
      operation,
      duration,
    });
    
    throw error;
  }
};

// Usage in Express app
const app = express();

app.use(requestLogger);

app.get('/api/posts', async (req, res) => {
  try {
    const posts = await performanceMonitor('fetch-posts', async () => {
      // Your database query here
      return await fetchPostsFromDatabase();
    });
    
    res.json(posts);
  } catch (error) {
    next(error);
  }
});

app.use(errorLogger);

// Graceful shutdown
process.on('SIGTERM', async () => {
  await logger.flush();
  process.exit(0);
});

Integration Patterns

Common patterns for integrating SimplyStack into different types of applications.

🔄 Event-Driven Architecture

Use SimplyStack for event logging and state management in microservices.

Event → Log → Process → Update

📊 Analytics Pipeline

Collect user behavior data and generate insights.

Track → Store → Analyze → Report

🚀 Content Management

Build headless CMS with blog posts and media management.

Create → Store → Publish → Distribute

🔍 Monitoring & Alerting

Real-time monitoring with intelligent alerting.

Monitor → Log → Alert → Resolve

Next Steps

Ready to implement these patterns in your project? Here are some next steps:

1

Choose Your Pattern

Select the example that best matches your use case and copy the relevant code.

2

Customize for Your Needs

Adapt the examples to fit your specific requirements and business logic.

3

Test Thoroughly

Test error scenarios, edge cases, and performance under load.

4

Monitor and Iterate

Use the logging patterns to monitor your implementation and iterate based on real usage.