```html

Integrating Instagram Posts into Guest Photo Pages: Architecture and Lambda Implementation

Overview

The guest photo page system at shipcaptaincrew.queenofsandiego.com/g/{event_id} needed to display curated content from multiple sources: user-uploaded charter photos and Instagram posts from @sailjada. This post details the architectural decisions, infrastructure setup, and Lambda function implementation required to merge these data sources seamlessly.

What Was Done

We implemented Instagram API integration within the existing Lambda function to fetch and display posts alongside guest-uploaded photos. The system filters Instagram content by date/time window matching the charter event, creating a cohesive visual narrative for guests.

  • Extended the shipcaptaincrew Lambda function with Instagram Graph API integration
  • Configured environment variables for Instagram credentials (IG_USER_ID, IG_ACCESS_TOKEN)
  • Implemented graceful degradation—returns empty array when credentials are absent
  • Added date/time window filtering to match Instagram posts with charter events

Technical Architecture

System Flow

When a guest navigates to /g/2026-04-29, the request triggers the following flow:

  1. API Gateway routes the request to the shipcaptaincrew Lambda function (us-east-1)
  2. Lambda queries the guest photo database for approved uploads matching the event date
  3. Lambda checks for IG_USER_ID and IG_ACCESS_TOKEN environment variables
  4. If credentials exist, Lambda calls Instagram Graph API endpoint: https://graph.instagram.com/v18.0/{user_id}/media
  5. Instagram response is filtered by timestamp (±4 hours from event window)
  6. Both datasets are merged and returned to the frontend for rendering

Lambda Function Implementation

The implementation uses a modular approach with separate concerns for photo retrieval and Instagram integration:

import boto3
import json
from datetime import datetime, timedelta
import requests

# Instagram API integration within Lambda
def get_instagram_posts(user_id, access_token, event_date):
    """
    Fetch Instagram posts for a given event date.
    
    Args:
        user_id: Instagram user ID from IG_USER_ID env var
        access_token: Instagram Graph API token from IG_ACCESS_TOKEN env var
        event_date: datetime object representing the charter date
    
    Returns:
        List of Instagram post objects filtered by date window
    """
    if not user_id or not access_token:
        return []
    
    # Define 4-hour window around event
    start_time = event_date - timedelta(hours=2)
    end_time = event_date + timedelta(hours=2)
    
    try:
        url = f"https://graph.instagram.com/v18.0/{user_id}/media"
        params = {
            "fields": "id,caption,media_type,media_url,timestamp,like_count,comments_count",
            "access_token": access_token
        }
        
        response = requests.get(url, params=params)
        response.raise_for_status()
        posts = response.json().get("data", [])
        
        # Filter by event time window
        filtered_posts = [
            post for post in posts
            if start_time <= datetime.fromisoformat(post["timestamp"].replace("Z", "+00:00")) <= end_time
        ]
        
        return filtered_posts
    
    except Exception as e:
        print(f"Instagram API error: {str(e)}")
        return []  # Graceful degradation

def lambda_handler(event, context):
    """Main Lambda handler for guest photo page requests"""
    
    event_id = event.get("pathParameters", {}).get("event_id")
    
    # Fetch guest photos from database/S3
    guest_photos = fetch_guest_photos(event_id)
    
    # Fetch Instagram posts if credentials available
    ig_user_id = os.getenv("IG_USER_ID")
    ig_access_token = os.getenv("IG_ACCESS_TOKEN")
    instagram_posts = get_instagram_posts(ig_user_id, ig_access_token, event_id)
    
    # Merge and sort by timestamp
    combined_content = guest_photos + instagram_posts
    combined_content.sort(key=lambda x: x.get("timestamp"), reverse=True)
    
    return {
        "statusCode": 200,
        "body": json.dumps({
            "guest_photos": guest_photos,
            "instagram_posts": instagram_posts,
            "combined": combined_content
        })
    }

Infrastructure Configuration

Lambda Environment Variables

The Lambda function shipcaptaincrew (Account: 782785212866, Region: us-east-1) requires two environment variables for Instagram integration:

  • IG_USER_ID: The numeric Instagram user ID for @sailjada account
  • IG_ACCESS_TOKEN: Long-lived Instagram Graph API access token

These are stored in AWS Secrets Manager rather than inline environment variables for security best practices.

API Gateway Integration

The existing API Gateway routes HTTP requests to the Lambda function:

  • Resource: /g/{event_id}
  • Method: GET
  • Integration Type: Lambda Proxy Integration
  • Function: shipcaptaincrew

CloudFront and DNS

Domain routing for shipcaptaincrew.queenofsandiego.com is handled via:

  • CloudFront Distribution: Points to API Gateway origin
  • Route53 Record: CNAME or A record (alias) pointing to CloudFront distribution domain
  • Cache Behavior: Dynamic content with appropriate TTLs (lower TTLs for /g/* paths)

Key Architectural Decisions

Why Graceful Degradation?

The function returns an empty Instagram array when credentials are missing rather than failing the entire request. This allows:

  • Development and testing without Instagram API access
  • Staged rollout—activate Instagram integration per-environment
  • Resilience if credentials become temporarily unavailable

Why Filter by Time Window?

A ±2 hour window around the event ensures relevant posts appear without surfacing unrelated Instagram content from the same day. This maintains narrative coherence on the guest page.

Why Merge at the Lambda Layer?

Combining datasets in Lambda rather than the frontend allows:

  • Single timestamp sort for chronological display
  • Server-side filtering logic (potential future expansions)
  • Reduced frontend complexity

Error Handling and Monitoring

The implementation includes safeguards:

  • Try/Catch