> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ollie.shop/llms.txt
> Use this file to discover all available pages before exploring further.

# Local Development

> Set up your local development environment for fast iteration. Test functions locally, debug issues, and optimize performance before deployment.

{/* 
Local Development Keywords: development environment, local testing, debugging, TypeScript setup, testing strategies, function simulation, development workflow

Development Environment Setup:
- Node.js 18+ required for TypeScript compilation
- TypeScript for type safety and modern JavaScript features
- Package managers: npm, yarn, or pnpm supported
- IDE recommendations: VS Code, WebStorm, Cursor
- Extension recommendations: TypeScript, ESLint, Prettier

Project Structure Best Practices:
- index.ts: Main function entry point with handler export
- package.json: Dependencies and project metadata
- lib/: Shared utility functions and business logic
- types/: TypeScript type definitions
- tests/: Unit tests and integration tests
- .env: Environment variables for local testing

Handler Development Patterns:
1. Request Function Handler:
 - export const handler = async (event: any) => Promise<Response>
 - Event contains: url, method, headers, body, queryStringParameters
 - Return format: { statusCode: number, headers?: object, body: string }

2. Response Function Handler:
 - export const handler = async (event: any) => void
 - Event contains: originalRequest, response, metadata
 - No return value required - side effects only

3. Task Function Handler:
 - export const handler = async (event: any) => void
 - Event contains: schedule info, execution context
 - Long-running operations supported

Local Testing Strategies:
1. Unit Testing
 - Test handler functions in isolation
 - Mock external API calls and dependencies
 - Use Jest, Vitest, or Node.js built-in test runner
 - Test different request scenarios and edge cases

2. Integration Testing
 - Test complete request/response flows
 - Use real external APIs with test data
 - Validate Hub integration patterns
 - Test error handling and timeout scenarios

3. Manual Testing
 - Simulate Hub integration requests locally
 - Test with realistic data payloads
 - Validate response formats and timing
 - Debug with console.log and debugger

Environment Variable Management:
- Use .env files for local configuration
- Separate .env.development and .env.production
- Never commit sensitive credentials
- Use placeholder values for team development
- Document required environment variables

Debugging Techniques:
1. Console Logging
 - Strategic console.log statements
 - JSON.stringify for complex objects
 - Timestamp logs for performance analysis
 - Error context and stack traces

2. IDE Debugging
 - Set breakpoints in TypeScript code
 - Step through function execution
 - Inspect variables and call stack
 - Attach debugger to running processes

3. Network Debugging
 - Inspect HTTP requests and responses
 - Use tools like Postman or cURL
 - Monitor external API calls
 - Validate request headers and payloads

Performance Optimization:
1. Cold Start Optimization
 - Minimize dependencies and imports
 - Use dynamic imports for optional features
 - Optimize initialization code
 - Reduce package bundle size

2. Runtime Optimization
 - Efficient data processing algorithms
 - Minimize external API calls
 - Use appropriate data structures
 - Implement caching when beneficial

3. Memory Management
 - Avoid memory leaks in long-running functions
 - Clean up resources after use
 - Monitor memory usage during development
 - Use streams for large data processing

Development Workflow:
1. Local Development
 - Write function code with TypeScript
 - Create unit tests for business logic
 - Test with simulated event data
 - Use environment variables for configuration

2. Testing and Validation
 - Run unit tests and integration tests
 - Manually test with realistic scenarios
 - Validate error handling and edge cases
 - Check performance and memory usage

3. Pre-deployment Checks
 - Verify package.json dependencies
 - Ensure TypeScript compiles without errors
 - Test with production-like data
 - Review security and error handling

Common Development Issues:
1. TypeScript Compilation Errors
 - Missing type definitions
 - Incorrect import/export syntax
 - Configuration issues in tsconfig.json
 - Dependency version conflicts

2. Package.json Problems
 - Missing dependencies
 - Incorrect version specifications
 - Invalid JSON syntax
 - Wrong entry point configuration

3. Environment Variable Issues
 - Missing required variables
 - Incorrect variable names
 - Type conversion problems
 - Local vs production differences

Best Practices:
- Use TypeScript for type safety
- Write comprehensive tests
- Handle errors gracefully
- Log important operations
- Document complex business logic
- Use consistent code formatting
- Review code before deployment

Tools and Extensions:
- VS Code TypeScript support
- ESLint for code quality
- Prettier for formatting
- Git for version control
- npm/yarn for dependency management

Related Concepts: function deployment, testing strategies, debugging techniques, performance optimization, development workflow
*/}

Develop complete, testable functions locally and deploy them to see real results. This guide walks you through building functions you can actually test with live URLs.

<CardGroup cols={2}>
  <Card title="🌐 Live Testing" icon="globe">
    Deploy functions and test them with real URLs on your hub
  </Card>

  <Card title="🔄 Data Transformation" icon="refresh">
    See your functions transform data in real-time
  </Card>

  <Card title="📁 Complete Examples" icon="folder">
    Full function structures with all necessary files
  </Card>

  <Card title="🚀 Deploy & Test" icon="rocket">
    From local development to live URL in minutes
  </Card>
</CardGroup>

## Quick Start: Your First Testable Function

Let's build a complete function that you can test with a real URL after deployment.

<Steps>
  <Step title="Set up your project">
    ```bash theme={"system"}
    # Create project directory
    mkdir weather-api && cd weather-api

    # Initialize npm and install dependencies
    npm init -y
    npm install -D typescript @types/node @types/aws-lambda ts-node

    # Create project structure
    mkdir src lib types
    ```
  </Step>

  <Step title="Create your function files">
    <CodeGroup>
      ```typescript index.ts theme={"system"}
      import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
      import { validateCity, transformWeatherData } from './lib/weather';
      import { createResponse, handleError } from './lib/utils';

      export const handler = async (
        event: APIGatewayProxyEvent
      ): Promise<APIGatewayProxyResult> => {
        try {
          console.log('Weather API called:', {
            method: event.httpMethod,
            path: event.path,
            query: event.queryStringParameters
          });

          const city = event.queryStringParameters?.city;
          
          // Validate input
          const validation = validateCity(city);
          if (!validation.isValid) {
            return createResponse(400, { 
              error: 'Invalid city name',
              details: validation.errors 
            });
          }

          // Get weather data (simulated for demo)
          const rawWeatherData = await fetchWeatherData(city!);
          
          // Transform the data
          const transformedData = transformWeatherData(rawWeatherData);
          
          return createResponse(200, {
            city: city,
            weather: transformedData,
            timestamp: new Date().toISOString(),
            source: 'Ollie Hub Weather API'
          });
          
        } catch (error) {
          console.error('Weather API error:', error);
          return handleError(error);
        }
      };

      // Simulate weather API call
      async function fetchWeatherData(city: string) {
        // Simulate API delay
        await new Promise(resolve => setTimeout(resolve, 100));
        
        // Return mock weather data
        return {
          temperature: Math.floor(Math.random() * 30) + 10,
          humidity: Math.floor(Math.random() * 40) + 40,
          conditions: ['sunny', 'cloudy', 'rainy', 'windy'][Math.floor(Math.random() * 4)],
          pressure: 1013 + Math.floor(Math.random() * 20) - 10,
          windSpeed: Math.floor(Math.random() * 20),
          rawTimestamp: Date.now()
        };
      }
      ```

      ```typescript lib/weather.ts theme={"system"}
      export interface WeatherData {
        temperature: number;
        humidity: number;
        conditions: string;
        pressure: number;
        windSpeed: number;
        rawTimestamp: number;
      }

      export interface TransformedWeather {
        temp: {
          celsius: number;
          fahrenheit: number;
          description: string;
        };
        humidity: {
          percentage: number;
          level: string;
        };
        conditions: {
          current: string;
          emoji: string;
        };
        wind: {
          speed: number;
          description: string;
        };
        comfort: {
          score: number;
          rating: string;
        };
      }

      export function validateCity(city: string | null): { isValid: boolean; errors: string[] } {
        const errors: string[] = [];
        
        if (!city) {
          errors.push('City parameter is required');
        } else if (city.trim().length < 2) {
          errors.push('City name must be at least 2 characters');
        } else if (!/^[a-zA-Z\s-']+$/.test(city)) {
          errors.push('City name contains invalid characters');
        }
        
        return {
          isValid: errors.length === 0,
          errors
        };
      }

      export function transformWeatherData(data: WeatherData): TransformedWeather {
        const celsius = data.temperature;
        const fahrenheit = Math.round((celsius * 9/5) + 32);
        
        // Generate temperature description
        const tempDescription = celsius < 10 ? 'Cold' : 
                               celsius < 20 ? 'Cool' : 
                               celsius < 25 ? 'Mild' : 
                               celsius < 30 ? 'Warm' : 'Hot';
        
        // Generate humidity level
        const humidityLevel = data.humidity < 40 ? 'Low' :
                             data.humidity < 70 ? 'Moderate' : 'High';
        
        // Generate weather emoji
        const weatherEmoji = {
          'sunny': '☀️',
          'cloudy': '☁️',
          'rainy': '🌧️',
          'windy': '💨'
        }[data.conditions] || '🌤️';
        
        // Generate wind description
        const windDescription = data.windSpeed < 5 ? 'Calm' :
                               data.windSpeed < 15 ? 'Light breeze' :
                               data.windSpeed < 25 ? 'Moderate wind' : 'Strong wind';
        
        // Calculate comfort score (0-100)
        let comfortScore = 100;
        
        // Temperature comfort (ideal 20-25°C)
        if (celsius < 15 || celsius > 30) comfortScore -= 30;
        else if (celsius < 18 || celsius > 27) comfortScore -= 15;
        
        // Humidity comfort (ideal 40-60%)
        if (data.humidity < 30 || data.humidity > 70) comfortScore -= 20;
        
        // Wind comfort (ideal < 15 km/h)
        if (data.windSpeed > 20) comfortScore -= 15;
        
        const comfortRating = comfortScore >= 80 ? 'Excellent' :
                             comfortScore >= 60 ? 'Good' :
                             comfortScore >= 40 ? 'Fair' : 'Poor';
        
        return {
          temp: {
            celsius,
            fahrenheit,
            description: tempDescription
          },
          humidity: {
            percentage: data.humidity,
            level: humidityLevel
          },
          conditions: {
            current: data.conditions,
            emoji: weatherEmoji
          },
          wind: {
            speed: data.windSpeed,
            description: windDescription
          },
          comfort: {
            score: Math.max(0, comfortScore),
            rating: comfortRating
          }
        };
      }
      ```

      ```typescript lib/utils.ts theme={"system"}
      import { APIGatewayProxyResult } from 'aws-lambda';

      export function createResponse(
        statusCode: number,
        data: any,
        headers: Record<string, string> = {}
      ): APIGatewayProxyResult {
        return {
          statusCode,
          headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
            ...headers
          },
          body: JSON.stringify(data, null, 2)
        };
      }

      export function handleError(error: any): APIGatewayProxyResult {
        const message = error instanceof Error ? error.message : 'Unknown error';
        
        console.error('Function error:', {
          message,
          stack: error.stack,
          timestamp: new Date().toISOString()
        });
        
        return createResponse(500, {
          error: 'Internal server error',
          message: 'Something went wrong. Please try again.',
          timestamp: new Date().toISOString()
        });
      }
      ```

      ```json package.json theme={"system"}
      {
        "name": "weather-api",
        "version": "1.0.0",
        "main": "index.ts",
        "scripts": {
          "test": "ts-node test.ts",
          "build": "tsc",
          "dev": "ts-node index.ts"
        },
        "dependencies": {
          "@types/aws-lambda": "^8.10.0"
        },
        "devDependencies": {
          "typescript": "^5.0.0",
          "ts-node": "^10.9.0",
          "@types/node": "^20.0.0"
        }
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Test locally">
    Create a test file to verify your function works:

    ```typescript test.ts theme={"system"}
    import { handler } from './index';
    import { APIGatewayProxyEvent } from 'aws-lambda';

    async function testWeatherAPI() {
      console.log('🧪 Testing Weather API Function\n');
      
      // Test valid city
      const validEvent: Partial<APIGatewayProxyEvent> = {
        httpMethod: 'GET',
        path: '/weather',
        queryStringParameters: { city: 'London' },
        headers: {},
        body: null
      };
      
      console.log('Testing with city: London');
      const result1 = await handler(validEvent as APIGatewayProxyEvent);
      console.log('Status:', result1.statusCode);
      console.log('Response:', JSON.parse(result1.body));
      console.log('\n---\n');
      
      // Test invalid city
      const invalidEvent: Partial<APIGatewayProxyEvent> = {
        httpMethod: 'GET',
        path: '/weather',
        queryStringParameters: { city: 'X' },
        headers: {},
        body: null
      };
      
      console.log('Testing with invalid city: X');
      const result2 = await handler(invalidEvent as APIGatewayProxyEvent);
      console.log('Status:', result2.statusCode);
      console.log('Response:', JSON.parse(result2.body));
      console.log('\n---\n');
      
      // Test missing city
      const missingEvent: Partial<APIGatewayProxyEvent> = {
        httpMethod: 'GET',
        path: '/weather',
        queryStringParameters: null,
        headers: {},
        body: null
      };
      
      console.log('Testing with missing city parameter');
      const result3 = await handler(missingEvent as APIGatewayProxyEvent);
      console.log('Status:', result3.statusCode);
      console.log('Response:', JSON.parse(result3.body));
      
      console.log('\n✅ Local testing completed!');
    }

    testWeatherAPI().catch(console.error);
    ```

    Run the test:

    ```bash theme={"system"}
    npm test
    ```
  </Step>

  <Step title="Deploy and test live">
    1. **Upload to Ollie Hub**: Zip your function folder and upload it via the web interface
    2. **Get your function URL**: Copy the URL from your function dashboard
    3. **Test with real URLs**:

    ```bash theme={"system"}
    # Replace YOUR_FUNCTION_URL with your actual function URL

    # Test valid city
    curl "YOUR_FUNCTION_URL?city=Paris"

    # Test another city to see different data
    curl "YOUR_FUNCTION_URL?city=Tokyo"

    # Test error handling
    curl "YOUR_FUNCTION_URL?city=X"

    # Test missing parameter
    curl "YOUR_FUNCTION_URL"
    ```

    **Expected response for Paris:**

    ```json theme={"system"}
    {
      "city": "Paris",
      "weather": {
        "temp": {
          "celsius": 22,
          "fahrenheit": 72,
          "description": "Mild"
        },
        "humidity": {
          "percentage": 65,
          "level": "Moderate"
        },
        "conditions": {
          "current": "sunny",
          "emoji": "☀️"
        },
        "wind": {
          "speed": 8,
          "description": "Light breeze"
        },
        "comfort": {
          "score": 85,
          "rating": "Excellent"
        }
      },
      "timestamp": "2024-01-15T10:30:00.000Z",
      "source": "Ollie Hub Weather API"
    }
    ```
  </Step>
</Steps>

## Advanced Example: User Data Processor

Build a more complex function that processes and transforms user data:

<CodeGroup>
  ```typescript index.ts theme={"system"}
  import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
  import { validateUserData, processUser, generateUserStats } from './lib/userProcessor';
  import { createResponse, handleError } from './lib/utils';

  export const handler = async (
    event: APIGatewayProxyEvent
  ): Promise<APIGatewayProxyResult> => {
    try {
      console.log('User processor called:', {
        method: event.httpMethod,
        path: event.path
      });

      if (event.httpMethod === 'GET') {
        return createResponse(200, {
          message: 'User Data Processor API',
          endpoints: {
            process: 'POST /process - Process user data',
            stats: 'GET /stats?users=number - Generate user statistics'
          },
          example: {
            url: 'POST YOUR_FUNCTION_URL',
            body: {
              name: 'John Doe',
              email: 'john@example.com',
              age: 28,
              interests: ['technology', 'sports'],
              location: 'New York'
            }
          }
        });
      }

      if (event.httpMethod === 'POST') {
        const userData = JSON.parse(event.body || '{}');
        
        // Validate user data
        const validation = validateUserData(userData);
        if (!validation.isValid) {
          return createResponse(400, {
            error: 'Invalid user data',
            details: validation.errors
          });
        }

        // Process and transform user data
        const processedUser = processUser(userData);
        
        return createResponse(200, {
          original: userData,
          processed: processedUser,
          transformation: {
            added: ['id', 'slug', 'category', 'score', 'recommendations'],
            computed: ['ageGroup', 'riskLevel', 'primaryInterest'],
            formatted: ['email', 'name', 'location']
          }
        });
      }

      // Handle stats endpoint
      if (event.path?.includes('stats')) {
        const userCount = parseInt(event.queryStringParameters?.users || '10');
        const stats = generateUserStats(userCount);
        
        return createResponse(200, {
          stats,
          generated: userCount,
          timestamp: new Date().toISOString()
        });
      }

      return createResponse(405, { error: 'Method not allowed' });
      
    } catch (error) {
      return handleError(error);
    }
  };
  ```

  ```typescript lib/userProcessor.ts theme={"system"}
  export interface UserData {
    name: string;
    email: string;
    age: number;
    interests: string[];
    location: string;
  }

  export interface ProcessedUser extends UserData {
    id: string;
    slug: string;
    emailDomain: string;
    ageGroup: string;
    category: string;
    score: number;
    riskLevel: string;
    primaryInterest: string;
    recommendations: string[];
    metadata: {
      processed: string;
      version: string;
    };
  }

  export function validateUserData(data: any): { isValid: boolean; errors: string[] } {
    const errors: string[] = [];

    if (!data.name || data.name.trim().length < 2) {
      errors.push('Name must be at least 2 characters');
    }

    if (!data.email || !data.email.includes('@')) {
      errors.push('Valid email is required');
    }

    if (!data.age || data.age < 13 || data.age > 120) {
      errors.push('Age must be between 13 and 120');
    }

    if (!Array.isArray(data.interests) || data.interests.length === 0) {
      errors.push('At least one interest is required');
    }

    if (!data.location || data.location.trim().length < 2) {
      errors.push('Location is required');
    }

    return {
      isValid: errors.length === 0,
      errors
    };
  }

  export function processUser(userData: UserData): ProcessedUser {
    // Generate unique ID
    const id = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    // Create URL-friendly slug
    const slug = userData.name.toLowerCase()
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/^-|-$/g, '');

    // Extract email domain
    const emailDomain = userData.email.split('@')[1];

    // Determine age group
    const ageGroup = userData.age < 18 ? 'minor' :
                     userData.age < 25 ? 'young-adult' :
                     userData.age < 35 ? 'adult' :
                     userData.age < 50 ? 'middle-age' :
                     userData.age < 65 ? 'mature' : 'senior';

    // Categorize user based on interests
    const techInterests = ['technology', 'programming', 'gaming', 'ai', 'software'];
    const sportsInterests = ['sports', 'fitness', 'running', 'gym', 'soccer'];
    const artsInterests = ['art', 'music', 'painting', 'writing', 'photography'];
    
    let category = 'general';
    if (userData.interests.some(i => techInterests.includes(i.toLowerCase()))) {
      category = 'tech-enthusiast';
    } else if (userData.interests.some(i => sportsInterests.includes(i.toLowerCase()))) {
      category = 'fitness-focused';
    } else if (userData.interests.some(i => artsInterests.includes(i.toLowerCase()))) {
      category = 'creative-minded';
    }

    // Calculate engagement score
    let score = 50; // Base score
    score += userData.interests.length * 10; // More interests = higher engagement
    score += userData.age < 30 ? 15 : userData.age < 50 ? 10 : 5; // Age factor
    score += emailDomain === 'gmail.com' ? 5 : 
             emailDomain.includes('.edu') ? 15 : 
             emailDomain.includes('.gov') ? 10 : 0; // Email domain factor
    
    // Determine risk level
    const riskLevel = score > 80 ? 'low' :
                     score > 60 ? 'medium' :
                     score > 40 ? 'high' : 'very-high';

    // Get primary interest
    const primaryInterest = userData.interests[0] || 'general';

    // Generate recommendations
    const recommendations = generateRecommendations(category, ageGroup, userData.interests);

    return {
      ...userData,
      id,
      slug,
      emailDomain,
      ageGroup,
      category,
      score: Math.min(100, score),
      riskLevel,
      primaryInterest,
      recommendations,
      metadata: {
        processed: new Date().toISOString(),
        version: '1.0.0'
      }
    };
  }

  function generateRecommendations(category: string, ageGroup: string, interests: string[]): string[] {
    const recommendations: string[] = [];

    // Category-based recommendations
    if (category === 'tech-enthusiast') {
      recommendations.push('Try our new AI-powered productivity tools');
      recommendations.push('Join our developer community forum');
    } else if (category === 'fitness-focused') {
      recommendations.push('Check out our personalized workout plans');
      recommendations.push('Connect with local fitness groups');
    } else if (category === 'creative-minded') {
      recommendations.push('Explore our digital art creation tools');
      recommendations.push('Join our creative challenges');
    }

    // Age-based recommendations
    if (ageGroup === 'young-adult') {
      recommendations.push('Student discount programs available');
    } else if (ageGroup === 'mature' || ageGroup === 'senior') {
      recommendations.push('Premium support and consultation services');
    }

    // Interest-based recommendations
    if (interests.includes('travel')) {
      recommendations.push('Virtual travel experiences and planning tools');
    }
    if (interests.includes('cooking')) {
      recommendations.push('Recipe recommendation engine');
    }

    return recommendations.slice(0, 3); // Limit to 3 recommendations
  }

  export function generateUserStats(userCount: number) {
    const ageGroups = ['young-adult', 'adult', 'middle-age', 'mature'];
    const categories = ['tech-enthusiast', 'fitness-focused', 'creative-minded', 'general'];
    const locations = ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix', 'Philadelphia'];

    const stats = {
      totalUsers: userCount,
      demographics: {
        ageGroups: {} as Record<string, number>,
        categories: {} as Record<string, number>,
        locations: {} as Record<string, number>
      },
      averageScore: 0,
      riskDistribution: {
        low: 0,
        medium: 0,
        high: 0,
        'very-high': 0
      },
      topInterests: [] as Array<{ interest: string; count: number }>
    };

    let totalScore = 0;
    const interestCounts: Record<string, number> = {};

    // Generate random but realistic distributions
    for (let i = 0; i < userCount; i++) {
      // Age group distribution
      const ageGroup = ageGroups[Math.floor(Math.random() * ageGroups.length)];
      stats.demographics.ageGroups[ageGroup] = (stats.demographics.ageGroups[ageGroup] || 0) + 1;

      // Category distribution
      const category = categories[Math.floor(Math.random() * categories.length)];
      stats.demographics.categories[category] = (stats.demographics.categories[category] || 0) + 1;

      // Location distribution
      const location = locations[Math.floor(Math.random() * locations.length)];
      stats.demographics.locations[location] = (stats.demographics.locations[location] || 0) + 1;

      // Score calculation
      const score = Math.floor(Math.random() * 50) + 40; // 40-90 range
      totalScore += score;

      // Risk distribution
      const risk = score > 80 ? 'low' : score > 60 ? 'medium' : score > 40 ? 'high' : 'very-high';
      stats.riskDistribution[risk as keyof typeof stats.riskDistribution]++;

      // Interest tracking
      const possibleInterests = ['technology', 'sports', 'art', 'travel', 'cooking', 'music', 'reading'];
      const userInterests = possibleInterests.slice(0, Math.floor(Math.random() * 3) + 1);
      userInterests.forEach(interest => {
        interestCounts[interest] = (interestCounts[interest] || 0) + 1;
      });
    }

    stats.averageScore = Math.round(totalScore / userCount);
    stats.topInterests = Object.entries(interestCounts)
      .sort(([,a], [,b]) => b - a)
      .slice(0, 5)
      .map(([interest, count]) => ({ interest, count }));

    return stats;
  }
  ```

  ```typescript lib/utils.ts theme={"system"}
  import { APIGatewayProxyResult } from 'aws-lambda';

  export function createResponse(
    statusCode: number,
    data: any,
    headers: Record<string, string> = {}
  ): APIGatewayProxyResult {
    return {
      statusCode,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
        ...headers
      },
      body: JSON.stringify(data, null, 2)
    };
  }

  export function handleError(error: any): APIGatewayProxyResult {
    const message = error instanceof Error ? error.message : 'Unknown error';
    
    console.error('Function error:', {
      message,
      stack: error.stack,
      timestamp: new Date().toISOString()
    });
    
    return createResponse(500, {
      error: 'Internal server error',
      message: 'Something went wrong. Please try again.',
      timestamp: new Date().toISOString(),
      requestId: Math.random().toString(36).substr(2, 9)
    });
  }
  ```
</CodeGroup>

### Test the User Processor

<Tabs>
  <Tab title="Local Testing">
    ```typescript test-user-processor.ts theme={"system"}
    import { handler } from './index';
    import { APIGatewayProxyEvent } from 'aws-lambda';

    async function testUserProcessor() {
      console.log('🧪 Testing User Data Processor\n');
      
      // Test API info
      const infoEvent: Partial<APIGatewayProxyEvent> = {
        httpMethod: 'GET',
        path: '/',
        queryStringParameters: null,
        headers: {},
        body: null
      };
      
      console.log('1. Getting API information:');
      const infoResult = await handler(infoEvent as APIGatewayProxyEvent);
      console.log('Response:', JSON.parse(infoResult.body));
      console.log('\n---\n');
      
      // Test user processing
      const userData = {
        name: 'Alice Johnson',
        email: 'alice@example.com',
        age: 28,
        interests: ['technology', 'travel', 'photography'],
        location: 'San Francisco'
      };
      
      const processEvent: Partial<APIGatewayProxyEvent> = {
        httpMethod: 'POST',
        path: '/process',
        body: JSON.stringify(userData),
        headers: { 'Content-Type': 'application/json' },
        queryStringParameters: null
      };
      
      console.log('2. Processing user data:');
      console.log('Input:', userData);
      const processResult = await handler(processEvent as APIGatewayProxyEvent);
      console.log('Output:', JSON.parse(processResult.body));
      console.log('\n---\n');
      
      // Test stats generation
      const statsEvent: Partial<APIGatewayProxyEvent> = {
        httpMethod: 'GET',
        path: '/stats',
        queryStringParameters: { users: '100' },
        headers: {},
        body: null
      };
      
      console.log('3. Generating user statistics:');
      const statsResult = await handler(statsEvent as APIGatewayProxyEvent);
      console.log('Stats:', JSON.parse(statsResult.body));
      
      console.log('\n✅ All tests completed!');
    }

    testUserProcessor().catch(console.error);
    ```
  </Tab>

  <Tab title="Live URL Testing">
    After deploying, test with these commands:

    ```bash theme={"system"}
    # Get API information
    curl YOUR_FUNCTION_URL

    # Process user data
    curl -X POST YOUR_FUNCTION_URL \
      -H "Content-Type: application/json" \
      -d '{
        "name": "John Smith",
        "email": "john@gmail.com",
        "age": 32,
        "interests": ["sports", "fitness", "technology"],
        "location": "Chicago"
      }'

    # Generate statistics for 50 users
    curl "YOUR_FUNCTION_URL?users=50"

    # Test error handling
    curl -X POST YOUR_FUNCTION_URL \
      -H "Content-Type: application/json" \
      -d '{
        "name": "X",
        "email": "invalid-email",
        "age": 5
      }'
    ```
  </Tab>

  <Tab title="Browser Testing">
    You can also test in your browser by visiting:

    ```
    YOUR_FUNCTION_URL
    YOUR_FUNCTION_URL?users=25
    ```

    For POST requests, use a tool like Postman or create a simple HTML form:

    ```html theme={"system"}
    <!DOCTYPE html>
    <html>
    <head>
        <title>Test User Processor</title>
    </head>
    <body>
        <h2>Test User Data Processor</h2>
        <form id="userForm">
            <input type="text" name="name" placeholder="Name" required><br><br>
            <input type="email" name="email" placeholder="Email" required><br><br>
            <input type="number" name="age" placeholder="Age" required><br><br>
            <input type="text" name="interests" placeholder="Interests (comma-separated)" required><br><br>
            <input type="text" name="location" placeholder="Location" required><br><br>
            <button type="submit">Process User</button>
        </form>
        
        <div id="result"></div>
        
        <script>
        document.getElementById('userForm').addEventListener('submit', async (e) => {
            e.preventDefault();
            const formData = new FormData(e.target);
            const userData = {
                name: formData.get('name'),
                email: formData.get('email'),
                age: parseInt(formData.get('age')),
                interests: formData.get('interests').split(',').map(s => s.trim()),
                location: formData.get('location')
            };
            
            try {
                const response = await fetch('YOUR_FUNCTION_URL', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(userData)
                });
                const result = await response.json();
                document.getElementById('result').innerHTML = 
                    '<pre>' + JSON.stringify(result, null, 2) + '</pre>';
            } catch (error) {
                document.getElementById('result').innerHTML = 'Error: ' + error.message;
            }
        });
        </script>
    </body>
    </html>
    ```
  </Tab>
</Tabs>

## Real-World Data Transformation Examples

<Accordion>
  <AccordionItem title="E-commerce Order Processor">
    Transform raw order data into enriched customer insights:

    ```typescript theme={"system"}
    export function processOrder(orderData: RawOrder): ProcessedOrder {
      return {
        ...orderData,
        totalWithTax: calculateTax(orderData.subtotal),
        customerSegment: determineSegment(orderData.customer),
        fulfillmentPriority: calculatePriority(orderData),
        estimatedDelivery: calculateDelivery(orderData.location),
        recommendations: generateProductRecommendations(orderData.items),
        analytics: {
          lifeTimeValue: calculateLTV(orderData.customer),
          riskScore: assessRisk(orderData),
          churnProbability: predictChurn(orderData.customer)
        }
      };
    }
    ```

    **Test URL**: `YOUR_FUNCTION_URL/process-order`
  </AccordionItem>

  <AccordionItem title="Social Media Analytics">
    Process social media posts and extract insights:

    ```typescript theme={"system"}
    export function analyzeSocialPost(post: SocialPost): PostAnalytics {
      return {
        ...post,
        sentiment: analyzeSentiment(post.content),
        topics: extractTopics(post.content),
        engagement: {
          score: calculateEngagement(post.metrics),
          predicted: predictEngagement(post),
          optimization: suggestOptimizations(post)
        },
        audience: {
          demographics: analyzeAudience(post.interactions),
          interests: inferInterests(post.interactions),
          influence: calculateInfluence(post.author)
        }
      };
    }
    ```

    **Test URL**: `YOUR_FUNCTION_URL/analyze-post`
  </AccordionItem>

  <AccordionItem title="Financial Data Aggregator">
    Transform financial transactions into insights:

    ```typescript theme={"system"}
    export function processTransactions(transactions: Transaction[]): FinancialInsights {
      return {
        summary: {
          totalIncome: calculateIncome(transactions),
          totalExpenses: calculateExpenses(transactions),
          netFlow: calculateNetFlow(transactions),
          monthlyTrends: calculateTrends(transactions)
        },
        categories: categorizesSpending(transactions),
        patterns: detectPatterns(transactions),
        recommendations: generateFinancialAdvice(transactions),
        alerts: detectAnomalies(transactions)
      };
    }
    ```

    **Test URL**: `YOUR_FUNCTION_URL/process-finances`
  </AccordionItem>
</Accordion>

## Deployment Checklist

<Steps>
  <Step title="Local testing passes">
    ✅ All functions run without errors locally\
    ✅ Data transformation works as expected\
    ✅ Error handling catches edge cases\
    ✅ TypeScript compiles without warnings
  </Step>

  <Step title="Function structure is correct">
    ✅ `index.ts` exists with exported `handler`\
    ✅ `package.json` includes all dependencies\
    ✅ Helper files are properly structured\
    ✅ No sensitive data in source code
  </Step>

  <Step title="Upload and configure">
    ✅ Function uploaded to Ollie Hub\
    ✅ Environment variables configured\
    ✅ Function type set correctly (Request/Task/Response)\
    ✅ Memory and timeout settings appropriate
  </Step>

  <Step title="Live testing">
    ✅ Function URL accessible\
    ✅ Data transformation works on live data\
    ✅ Error responses are user-friendly\
    ✅ Performance is acceptable
  </Step>
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Deploy Your Function" icon="rocket" href="/ollie-hub/development/deployment">
    Learn how to deploy your functions to Ollie Hub
  </Card>

  <Card title="Environment Variables" icon="cog" href="/ollie-hub/development/environment-variables">
    Securely manage configuration and API keys
  </Card>

  <Card title="Function Types" icon="code" href="/ollie-hub/core-concepts/functions">
    Understand Request, Response, and Task functions
  </Card>

  <Card title="Examples Gallery" icon="gallery" href="/ollie-hub/guides/examples">
    Explore more real-world function examples
  </Card>
</CardGroup>

<Tip>
  **Ready to Build?** Start with the Weather API example above - it's designed to work immediately and show you real data transformation. You'll have a working function with a testable URL in under 10 minutes!
</Tip>
