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
shipcaptaincrewLambda 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:
- API Gateway routes the request to the
shipcaptaincrewLambda function (us-east-1) - Lambda queries the guest photo database for approved uploads matching the event date
- Lambda checks for
IG_USER_IDandIG_ACCESS_TOKENenvironment variables - If credentials exist, Lambda calls Instagram Graph API endpoint:
https://graph.instagram.com/v18.0/{user_id}/media - Instagram response is filtered by timestamp (±4 hours from event window)
- 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 accountIG_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