An alternative implementation of JimmyDqv's Serverless Event Platform that replaces MongoDB Atlas with Amazon Aurora DSQL as the database backend.
The original platform manages event guest lists with RSVP tracking, group relationships, dietary preferences, and an admin dashboard. This version preserves the same serverless architecture (Lambda, API Gateway, SQS, SAM) while swapping the data layer to Aurora DSQL.
| MongoDB Atlas | Aurora DSQL | |
|---|---|---|
| Pricing model | Fixed-cost M10+ cluster required for workload federation | Fully pay-per-use, no minimum |
| Authentication | OIDC federation via STS — requires Atlas Identity Provider setup, IAM role mapping, and database user creation | Native IAM auth tokens — generate a token with boto3, connect with psycopg |
| Query language | MongoDB aggregation pipelines | Standard PostgreSQL SQL |
| Consistency | Tunable (eventual by default for reads) | Strong consistency with ACID transactions and snapshot isolation |
| Availability | Depends on cluster tier | 99.99% single-region, 99.999% multi-region |
| Serverless fit | Requires always-on cluster | Designed for serverless and event-driven architectures — scales to zero |
| Schema | Schemaless documents | Relational tables with enforced types |
Client → API Gateway (HTTP API) → Lambda Functions → Aurora DSQL
↓
SQS Queue → Activity Consumer Lambda → Aurora DSQL
Four SAM stacks from the original are consolidated into a single stack:
- 10 Lambda functions — one per API endpoint for clean routing
- HTTP API Gateway — routes for guests, groups, admin, and settings
- SQS + DLQ — async activity tracking with partial batch failure support
- Aurora DSQL — three tables:
guests,settings,activities
| Method | Path | Description |
|---|---|---|
| POST | /guests |
Create a guest |
| GET | /guests/{guest_id} |
Get a guest by ID |
| PUT | /guests/{guest_id} |
Update a guest |
| DELETE | /guests/{guest_id} |
Delete a guest |
| POST | /guests/{guest_id}/rsvp |
Submit RSVP |
| GET | /groups/{group_id} |
Get all guests in a group |
| GET | /groups |
Get group summary with member counts |
| GET | /admin/stats |
Dashboard statistics |
| GET | /admin/settings |
Get event settings |
| PUT | /admin/settings |
Update event settings |
├── src/
│ ├── models.py # Dataclasses and conversion utilities
│ ├── auth.py # IAM token generation for DSQL
│ ├── connection.py # Module-scope connection manager with retry
│ ├── retry.py # OCC retry decorator (exponential backoff)
│ ├── schema.py # DDL for tables and async indexes
│ ├── guest_handlers.py # Guest CRUD, RSVP, and group handlers
│ ├── admin_handlers.py # Dashboard stats and settings handlers
│ └── activity_consumer.py # SQS consumer for async activity tracking
├── template.yaml # SAM template
└── requirements.txt # Python dependencies
- AWS CLI configured with credentials
- AWS SAM CLI
- Python 3.13
- An Aurora DSQL cluster (create one here)
Create a single-region cluster in the DSQL console. Note the cluster endpoint (e.g., abc123.dsql.us-east-1.on.aws).
Connect to your cluster using psql or the DSQL query editor and run:
CREATE TABLE IF NOT EXISTS guests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
group_id UUID,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT,
attending BOOLEAN,
dietary TEXT,
rsvp_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
event_name TEXT,
event_date TIMESTAMPTZ,
location TEXT,
rsvp_deadline TIMESTAMPTZ,
max_guests INTEGER,
settings_data TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS activities (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
guest_id UUID,
action TEXT,
timestamp TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Async indexes (non-blocking)
CREATE INDEX ASYNC IF NOT EXISTS idx_guests_group_id ON guests (group_id);
CREATE INDEX ASYNC IF NOT EXISTS idx_guests_email ON guests (email);
CREATE INDEX ASYNC IF NOT EXISTS idx_guests_attending ON guests (attending);
CREATE INDEX ASYNC IF NOT EXISTS idx_guests_rsvp_at ON guests (rsvp_at);
CREATE INDEX ASYNC IF NOT EXISTS idx_activities_guest_id ON activities (guest_id);sam build && sam deploy --guidedYou'll be prompted for:
- DsqlClusterEndpoint — your cluster endpoint
- DsqlClusterArn —
arn:aws:dsql:<region>:<account-id>:cluster/<cluster-id>
# Create a guest
curl -X POST https://<api-endpoint>/prod/guests \
-H "Content-Type: application/json" \
-d '{"first_name": "Jane", "last_name": "Doe", "email": "jane@example.com"}'
# Submit RSVP
curl -X POST https://<api-endpoint>/prod/guests/<guest-id>/rsvp \
-H "Content-Type: application/json" \
-d '{"attending": true, "dietary": "vegetarian"}'
# Check dashboard
curl https://<api-endpoint>/prod/admin/statsLambda functions authenticate to DSQL using short-lived IAM tokens — no database passwords, no secrets, no OIDC federation setup:
client = boto3.client("dsql", region_name=region)
token = client.generate_db_connect_admin_auth_token(
Hostname=cluster_endpoint, Region=region
)
conn = psycopg.connect(host=endpoint, user="admin", password=token, sslmode="require")Database connections are stored at module scope and reused across Lambda warm starts, avoiding reconnection overhead.
DSQL uses OCC instead of locks. A decorator retries transactions on serialization conflicts with exponential backoff (50ms → 100ms → 200ms):
@occ_retry(max_retries=3, base_delay_ms=50)
def update_rsvp():
...- No JSONB —
settings_datausesTEXTwith JSON serialized/deserialized in the application layer - Async indexes —
CREATE INDEX ASYNCis required; synchronous index creation is not supported - PostgreSQL 16 compatible — standard psycopg driver works out of the box
sam delete --stack-name dsql-event-platformDelete the Aurora DSQL cluster separately from the DSQL console.
Based on the Serverless Event Platform by JimmyDqv. Original architecture and design patterns adapted for Aurora DSQL.