<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Eric Rodríguez</title>
    <description>The latest articles on Forem by Eric Rodríguez (@ericrodriguez10).</description>
    <link>https://forem.com/ericrodriguez10</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3677578%2Fcebf54ee-c6c0-436d-8570-7412b62d4321.jpeg</url>
      <title>Forem: Eric Rodríguez</title>
      <link>https://forem.com/ericrodriguez10</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ericrodriguez10"/>
    <language>en</language>
    <item>
      <title>Day 58: Don't let GenAI bankrupt your Serverless App</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Wed, 15 Apr 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-58-dont-let-genai-bankrupt-your-serverless-app-2gnj</link>
      <guid>https://forem.com/ericrodriguez10/day-58-dont-let-genai-bankrupt-your-serverless-app-2gnj</guid>
      <description>&lt;p&gt;When building AI-powered apps, be very careful with cron jobs.&lt;/p&gt;

&lt;p&gt;Today I wanted to make my AI Financial Agent send a daily morning email to all users by default (like traditional finance apps do).&lt;/p&gt;

&lt;p&gt;The Problem: Traditional apps send static templates. My app uses Amazon Bedrock to generate custom text. If I loop through 500 inactive users and call Bedrock 500 times just to send emails they won't read, my AWS bill will explode. 💥&lt;/p&gt;

&lt;p&gt;The Fix: I added a "FinOps" filter in my EventBridge orchestrator before it sends tasks to the SQS queue.&lt;/p&gt;

&lt;h1&gt;
  
  
  Before sending to SQS for AI processing, filter out the noise.
&lt;/h1&gt;

&lt;p&gt;valid_users = []&lt;br&gt;
for u in all_users:&lt;br&gt;
    wants_email = u.get('wants_daily_email', True) # Opt-out approach&lt;br&gt;
    is_test = str(u.get('user_id', '')).startswith('test_')&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if wants_email and not is_test:
    valid_users.append(u)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By filtering test_ accounts and respecting user Opt-Outs before the SQS queue, my AI worker Lambda only invokes Bedrock for users that actually matter. Code for engagement, architect for cost!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xqw57ahsd82wv1hmank.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xqw57ahsd82wv1hmank.png" alt=" " width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>python</category>
      <category>finops</category>
    </item>
    <item>
      <title>Day 57: Dynamic HTML Emails in AWS Lambda (FinTech UX) 🎨</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Tue, 14 Apr 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-57-dynamic-html-emails-in-aws-lambda-fintech-ux-2n3f</link>
      <guid>https://forem.com/ericrodriguez10/day-57-dynamic-html-emails-in-aws-lambda-fintech-ux-2n3f</guid>
      <description>&lt;p&gt;Financial APIs like Plaid are great, but their data structures are built for accountants, not normal humans. &lt;/p&gt;

&lt;p&gt;Today, I rewrote the notification engine for my AI Agent. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;My Lambda was sending emails that looked like a raw JSON dump. Refunds appeared as negative numbers (&lt;code&gt;-4.22€&lt;/code&gt;), and expenses were mixed with income. &lt;/p&gt;

&lt;p&gt;The Fix&lt;br&gt;
I built a dynamic HTML builder in Python that conditionally renders sections based on the day's payload:&lt;/p&gt;

&lt;p&gt;Separate transactions and fix signs&lt;br&gt;
for t in transactions:&lt;br&gt;
    amount = float(t['amount'])&lt;br&gt;
    if amount &amp;lt; 0 or is_income_keyword(t['description']):&lt;br&gt;
        income_txs.append(t)&lt;br&gt;
    else:&lt;br&gt;
        expense_txs.append(t)&lt;/p&gt;

&lt;h1&gt;
  
  
  Conditionally render HTML
&lt;/h1&gt;

&lt;p&gt;if income_txs:&lt;br&gt;
    html += "&lt;/p&gt;
&lt;h4&gt;Income / Credits&lt;/h4&gt;"&lt;br&gt;
    # Format negative API floats to positive UX strings&lt;br&gt;
    html += build_rows(abs(amount), color="#10b981") &lt;br&gt;
Now, the emails are cleanly divided, visually color-coded, and highly readable. Plus, the AWS EventBridge orchestrator now perfectly balances hourly SMS alerts with the 9:00 AM daily SQS report.

&lt;p&gt;Never underestimate the power of formatting your data before passing it to the user (or to the LLM!).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw3bato7ba8gigf7w76an.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw3bato7ba8gigf7w76an.png" alt=" " width="800" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>ux</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Day 56: Beating LLM Latency with Amazon SQS Decoupling ⚡</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Mon, 13 Apr 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-56-beating-llm-latency-with-amazon-sqs-decoupling-1fac</link>
      <guid>https://forem.com/ericrodriguez10/day-56-beating-llm-latency-with-amazon-sqs-decoupling-1fac</guid>
      <description>&lt;p&gt;If you are building apps with LLMs, you already know the pain: generation takes seconds, and users hate waiting. &lt;/p&gt;

&lt;p&gt;Today, I fixed the latency of my AI Financial Agent by implementing the Asynchronous Worker Pattern** using Amazon SQS.&lt;/p&gt;

&lt;p&gt;The Problem&lt;br&gt;
My AWS Lambda was running synchronously:&lt;br&gt;
Fetch Bank Data -&amp;gt; Run Heavy AI Prompt -&amp;gt; Generate Email -&amp;gt; Return UI Data. &lt;br&gt;
This caused 5+ second load times and occasional API timeouts.&lt;/p&gt;

&lt;p&gt;The Solution&lt;br&gt;
I split my Python lambda_handler to detect the event source. &lt;/p&gt;

&lt;p&gt;If the request comes from API Gateway (React Frontend), it bypasses the heavy AI email generation completely and returns the dashboard data instantly.&lt;/p&gt;

&lt;p&gt;If the request comes from my EventBridge daily cronjob, it acts as a Fan-Out orchestrator:&lt;/p&gt;

&lt;p&gt;Scans DynamoDB and queues the heavy work&lt;br&gt;
for user in users:&lt;br&gt;
    sqs.send_message(&lt;br&gt;
        QueueUrl=SQS_QUEUE_URL, &lt;br&gt;
        MessageBody=json.dumps({"task": "daily_report", "user_id": user['user_id']})&lt;br&gt;
    )&lt;/p&gt;

&lt;p&gt;Then, SQS automatically invokes the Lambda in the background to handle the heavy Bedrock processing and SES email delivery.&lt;/p&gt;

&lt;p&gt;By decoupling the architecture, UI latency dropped by over 70%. Stop making your users wait for background tasks! Have you implemented SQS in your serverless apps yet?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs517cn19w59cj5la0jgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs517cn19w59cj5la0jgg.png" alt=" " width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>serverless</category>
      <category>python</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Day 55: Single Table Design for User Profiles in DynamoDB</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Sat, 11 Apr 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-55-single-table-design-for-user-profiles-in-dynamodb-13pn</link>
      <guid>https://forem.com/ericrodriguez10/day-55-single-table-design-for-user-profiles-in-dynamodb-13pn</guid>
      <description>&lt;p&gt;Hardcoding variables is a developer habit. Building user configuration is a Product mindset. 🛠️&lt;/p&gt;

&lt;p&gt;Up until today, my Serverless AI Financial Agent suffered from the classic "Minimum Viable Product" disease: hardcoded assumptions. The AI assumed a fixed €15 daily budget for everyone to maintain their savings streak, and it lazily parsed usernames directly from their Cognito JWT Token emails (e.g., turning &lt;code&gt;ericridri11@gmail.com&lt;/code&gt; into "Ericridri11"). &lt;/p&gt;

&lt;p&gt;It worked for a proof of concept, but as a user experience? It was terrible.&lt;/p&gt;

&lt;p&gt;For Day 55 of my #100DaysOfCloud challenge, it was time to mature the architecture and build a Stateful User Configuration Panel. The users need to take the wheel.&lt;/p&gt;

&lt;p&gt;The Challenge: Database Bloat&lt;/p&gt;

&lt;p&gt;The immediate thought of any developer is: &lt;em&gt;"I need to save user preferences. Let's spin up a &lt;code&gt;UserPreferences&lt;/code&gt; table in the database."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In relational databases (SQL), that's standard practice. But in the cloud, specifically with NoSQL databases like Amazon DynamoDB, compute is cheap but IOPS (Input/Output Operations Per Second) and maintaining multiple tables cost money and operational overhead. &lt;/p&gt;

&lt;p&gt;How do we store custom user data without bloating our infrastructure?&lt;/p&gt;

&lt;p&gt;The Solution: Single Table Design 🧠&lt;/p&gt;

&lt;p&gt;Instead of provisioning a completely new DynamoDB table, I implemented an advanced NoSQL pattern called &lt;strong&gt;Single Table Design&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;I overloaded my existing &lt;code&gt;FinanceAgent-Transactions&lt;/code&gt; table. By querying a specific sort key formatted as &lt;code&gt;PROFILE#{user_id}&lt;/code&gt;, I can retrieve a metadata record that acts as a container for user preferences, living right alongside their financial transactions.&lt;/p&gt;

&lt;p&gt;With this single record, I can now store:&lt;br&gt;
✅ Display Name (No more email parsing).&lt;br&gt;
✅ Custom Daily Budget Goal (Dynamic streak calculation).&lt;br&gt;
✅ Monthly Salary (For better FinOps forecasting).&lt;br&gt;
✅ AI Personality Tone (Users can choose "Brutal", "Sarcastic", or "Polite").&lt;/p&gt;

&lt;p&gt;Fun fact on the "Polite" tone: The app's core identity is "Tough Love". If a user selects "Polite", the system prompt instructs the AI to subtly mock them for being emotionally fragile and unable to handle the truth. The AI never loses!* 🤖&lt;/p&gt;

&lt;p&gt;The Lambda Router&lt;/p&gt;

&lt;p&gt;Since my entire backend runs on a single AWS Lambda Function URL, I had to upgrade my Python code to act as a basic RESTful router. &lt;/p&gt;

&lt;p&gt;I added logic to intercept the HTTP method from the &lt;code&gt;requestContext&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the user hits "Save" on the React Frontend, a &lt;code&gt;POST&lt;/code&gt; request is fired. The Lambda parses the JSON payload and updates the &lt;code&gt;PROFILE&lt;/code&gt; record in DynamoDB.&lt;/li&gt;
&lt;li&gt;When a standard &lt;code&gt;GET&lt;/code&gt; request is made to load the dashboard, the Lambda fetches this profile first, and dynamically injects the user's real name and requested tone directly into the Amazon Nova Micro system prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a snippet of how I implemented the Profile extraction in Python using &lt;code&gt;boto3&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;def get_user_profile(user_id, default_name):&lt;br&gt;
    profile_id = f"PROFILE#{user_id}"&lt;br&gt;
    try:&lt;br&gt;
        response = table.get_item(Key={'user_id': user_id, 'transaction_date': profile_id})&lt;br&gt;
        if 'Item' in response:&lt;br&gt;
            return response['Item']&lt;br&gt;
    except Exception as e:&lt;br&gt;
        logger.error("Error fetching profile", extra={"details": str(e)})&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Return default schema if no profile exists yet
return {
    'user_id': user_id, 
    'transaction_date': profile_id, 
    'display_name': default_name, 
    'daily_budget': 15.00, 
    'ai_tone': 'brutal'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Lesson&lt;/p&gt;

&lt;p&gt;Building the engine is only half the battle. Giving the driver a steering wheel is what makes it a real application. Think about your data access patterns before provisioning new cloud resources. Overloading NoSQL tables is an absolute superpower for keeping costs down while scaling up features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10rehzanzce6k97g0dlc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10rehzanzce6k97g0dlc.png" alt=" " width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>dynamodb</category>
      <category>serverless</category>
      <category>react</category>
    </item>
    <item>
      <title>Day 55: Single Table Design for User Profiles in DynamoDB</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Sat, 11 Apr 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-55-single-table-design-for-user-profiles-in-dynamodb-1696</link>
      <guid>https://forem.com/ericrodriguez10/day-55-single-table-design-for-user-profiles-in-dynamodb-1696</guid>
      <description>&lt;p&gt;Hardcoding variables is a developer habit. Building user configuration is a Product mindset. 🛠️&lt;/p&gt;

&lt;p&gt;Up until today, my Serverless AI Financial Agent suffered from the classic "Minimum Viable Product" disease: hardcoded assumptions. The AI assumed a fixed €15 daily budget for everyone to maintain their savings streak, and it lazily parsed usernames directly from their Cognito JWT Token emails (e.g., turning &lt;code&gt;ericridri11@gmail.com&lt;/code&gt; into "Ericridri11"). &lt;/p&gt;

&lt;p&gt;It worked for a proof of concept, but as a user experience? It was terrible.&lt;/p&gt;

&lt;p&gt;For Day 55 of my #100DaysOfCloud challenge, it was time to mature the architecture and build a Stateful User Configuration Panel. The users need to take the wheel.&lt;/p&gt;

&lt;p&gt;The Challenge: Database Bloat&lt;/p&gt;

&lt;p&gt;The immediate thought of any developer is: &lt;em&gt;"I need to save user preferences. Let's spin up a &lt;code&gt;UserPreferences&lt;/code&gt; table in the database."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In relational databases (SQL), that's standard practice. But in the cloud, specifically with NoSQL databases like Amazon DynamoDB, compute is cheap but IOPS (Input/Output Operations Per Second) and maintaining multiple tables cost money and operational overhead. &lt;/p&gt;

&lt;p&gt;How do we store custom user data without bloating our infrastructure?&lt;/p&gt;

&lt;p&gt;The Solution: Single Table Design 🧠&lt;/p&gt;

&lt;p&gt;Instead of provisioning a completely new DynamoDB table, I implemented an advanced NoSQL pattern called &lt;strong&gt;Single Table Design&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;I overloaded my existing &lt;code&gt;FinanceAgent-Transactions&lt;/code&gt; table. By querying a specific sort key formatted as &lt;code&gt;PROFILE#{user_id}&lt;/code&gt;, I can retrieve a metadata record that acts as a container for user preferences, living right alongside their financial transactions.&lt;/p&gt;

&lt;p&gt;With this single record, I can now store:&lt;br&gt;
✅ Display Name (No more email parsing).&lt;br&gt;
✅ Custom Daily Budget Goal (Dynamic streak calculation).&lt;br&gt;
✅ Monthly Salary (For better FinOps forecasting).&lt;br&gt;
✅ AI Personality Tone (Users can choose "Brutal", "Sarcastic", or "Polite").&lt;/p&gt;

&lt;p&gt;Fun fact on the "Polite" tone: The app's core identity is "Tough Love". If a user selects "Polite", the system prompt instructs the AI to subtly mock them for being emotionally fragile and unable to handle the truth. The AI never loses!* 🤖&lt;/p&gt;

&lt;p&gt;The Lambda Router&lt;/p&gt;

&lt;p&gt;Since my entire backend runs on a single AWS Lambda Function URL, I had to upgrade my Python code to act as a basic RESTful router. &lt;/p&gt;

&lt;p&gt;I added logic to intercept the HTTP method from the &lt;code&gt;requestContext&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the user hits "Save" on the React Frontend, a &lt;code&gt;POST&lt;/code&gt; request is fired. The Lambda parses the JSON payload and updates the &lt;code&gt;PROFILE&lt;/code&gt; record in DynamoDB.&lt;/li&gt;
&lt;li&gt;When a standard &lt;code&gt;GET&lt;/code&gt; request is made to load the dashboard, the Lambda fetches this profile first, and dynamically injects the user's real name and requested tone directly into the Amazon Nova Micro system prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a snippet of how I implemented the Profile extraction in Python using &lt;code&gt;boto3&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;def get_user_profile(user_id, default_name):&lt;br&gt;
    profile_id = f"PROFILE#{user_id}"&lt;br&gt;
    try:&lt;br&gt;
        response = table.get_item(Key={'user_id': user_id, 'transaction_date': profile_id})&lt;br&gt;
        if 'Item' in response:&lt;br&gt;
            return response['Item']&lt;br&gt;
    except Exception as e:&lt;br&gt;
        logger.error("Error fetching profile", extra={"details": str(e)})&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Return default schema if no profile exists yet
return {
    'user_id': user_id, 
    'transaction_date': profile_id, 
    'display_name': default_name, 
    'daily_budget': 15.00, 
    'ai_tone': 'brutal'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Lesson&lt;/p&gt;

&lt;p&gt;Building the engine is only half the battle. Giving the driver a steering wheel is what makes it a real application. Think about your data access patterns before provisioning new cloud resources. Overloading NoSQL tables is an absolute superpower for keeping costs down while scaling up features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10rehzanzce6k97g0dlc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10rehzanzce6k97g0dlc.png" alt=" " width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>dynamodb</category>
      <category>serverless</category>
      <category>react</category>
    </item>
    <item>
      <title>Day 54: Giving an LLM Long-Term Memory with DynamoDB</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Fri, 10 Apr 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-54-giving-an-llm-long-term-memory-with-dynamodb-16n5</link>
      <guid>https://forem.com/ericrodriguez10/day-54-giving-an-llm-long-term-memory-with-dynamodb-16n5</guid>
      <description>&lt;p&gt;One of the biggest limitations of stateless serverless applications using LLMs (like Amazon Bedrock or OpenAI) is amnesia. Every API call starts from zero.&lt;/p&gt;

&lt;p&gt;Today, I built a persistent memory engine for my AI Financial Agent so it remembers a user's behavior from previous months.&lt;/p&gt;

&lt;p&gt;The Architecture:&lt;br&gt;
I created a DynamoDB table with user_id (from Cognito JWT) as the Partition Key and month_year as the Sort Key.&lt;/p&gt;

&lt;p&gt;Before calling Bedrock, my Python Lambda fetches last month's summary:&lt;/p&gt;

&lt;p&gt;def get_user_memory(user_id, prev_month_str):&lt;br&gt;
    # DynamoDB get_item logic...&lt;br&gt;
I then inject this into the LLM prompt:&lt;/p&gt;

&lt;p&gt;Plaintext&lt;br&gt;
PREVIOUS MONTH MEMORY:&lt;br&gt;
"{past_memory}"&lt;br&gt;
Analyze the current transactions and compare them to the past memory.&lt;/p&gt;

&lt;p&gt;The crucial part? I force the LLM to output a memory_for_next_month string in its JSON response.&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "score_feedback": "You spent 50€ less on eating out than last month. Good job.",&lt;br&gt;
  "memory_for_next_month": "User successfully cut back on dining expenses but increased tech spending."&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Lambda intercepts this key and saves it back to DynamoDB using the current month's timestamp. It creates an infinite loop of context, turning a basic API wrapper into a continuous AI companion!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foi5tj40gvjsjsdphm1um.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foi5tj40gvjsjsdphm1um.png" alt=" " width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>ai</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Day 53: CI/CD for React on AWS S3 &amp; CloudFront (No Access Keys!) 🚀</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Thu, 09 Apr 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-53-cicd-for-react-on-aws-s3-cloudfront-no-access-keys-34lm</link>
      <guid>https://forem.com/ericrodriguez10/day-53-cicd-for-react-on-aws-s3-cloudfront-no-access-keys-34lm</guid>
      <description>&lt;p&gt;Deploying React to S3 manually gets old fast. If you are still dragging and dropping folders into the AWS Console, it's time to stop.&lt;/p&gt;

&lt;p&gt;Today, I built a GitHub Actions pipeline that builds my React app, syncs it to S3, and clears the CloudFront cache automatically. Best part? Zero static AWS credentials.&lt;/p&gt;

&lt;p&gt;The Workflow&lt;/p&gt;

&lt;p&gt;Assuming you already have an OIDC Identity Provider set up in AWS IAM (which you should, to avoid storing Access Keys in GitHub Secrets), here is the workflow I wrote today:&lt;/p&gt;

&lt;p&gt;YAML&lt;br&gt;
name: Deploy Frontend&lt;br&gt;
on:&lt;br&gt;
  push:&lt;br&gt;
    branches: [ main ]&lt;br&gt;
    paths: [ 'src/**', 'App.tsx', 'package.json' ]&lt;/p&gt;

&lt;p&gt;permissions:&lt;br&gt;
  id-token: write&lt;br&gt;
  contents: read&lt;/p&gt;

&lt;p&gt;jobs:&lt;br&gt;
  deploy:&lt;br&gt;
    runs-on: ubuntu-latest&lt;br&gt;
    steps:&lt;br&gt;
      - uses: actions/checkout@v4&lt;br&gt;
      - uses: actions/setup-node@v4&lt;br&gt;
        with: { node-version: '20' }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  - run: npm ci
  - run: npm run build
    env:
      VITE_AWS_REGION: "eu-north-1"
      VITE_USER_POOL_ID: "your-pool-id"

  - name: Configure AWS Credentials via OIDC
    uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::1234567890:role/GitHubDeployRole
      aws-region: eu-north-1

  - name: Sync to S3
    run: aws s3 sync dist/ s3://my-react-bucket --delete

  - name: Invalidate CloudFront
    run: aws cloudfront create-invalidation --distribution-id E1ABCD2345 --paths "/*"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Why the --delete flag?&lt;/p&gt;

&lt;p&gt;Using aws s3 sync with --delete is crucial. It removes old JavaScript chunks from S3 that were generated in previous builds, preventing your bucket from growing infinitely with dead code.&lt;/p&gt;

&lt;p&gt;My deployments now take 45 seconds and require zero clicks in the AWS console. Automate everything!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3srsm7r4nyqi7da6y9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3srsm7r4nyqi7da6y9s.png" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>devops</category>
      <category>github</category>
    </item>
    <item>
      <title>Day 52: How to build a Split-Screen Login &amp; Parse JWTs in AWS Lambda 🔐</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Wed, 08 Apr 2026 14:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-52-how-to-build-a-split-screen-login-parse-jwts-in-aws-lambda-47b1</link>
      <guid>https://forem.com/ericrodriguez10/day-52-how-to-build-a-split-screen-login-parse-jwts-in-aws-lambda-47b1</guid>
      <description>&lt;p&gt;I hated the default Amazon Cognito login screen. It looked like a dashboard from 2015.&lt;/p&gt;

&lt;p&gt;Today, I decided to build a custom Split-Screen Auth UI for my React app and update my AWS Lambda backend to actually read the JSON Web Tokens (JWT) sent by the frontend. Here is exactly how I did it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Frontend: Split-Screen Auth with Amplify&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of using the standard  wrapper which forces a centered card, I used Authenticator.Provider. This gives you access to the useAuthenticator hook so you can render the login form exactly where you want it.&lt;/p&gt;

&lt;p&gt;I built a two-column layout using Tailwind CSS. Left side: Branding. Right side: Login.&lt;/p&gt;

&lt;p&gt;import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react';&lt;/p&gt;

&lt;p&gt;const CustomAuthWrapper = () =&amp;gt; {&lt;br&gt;
  const { authStatus } = useAuthenticator((context) =&amp;gt; [context.authStatus]);&lt;/p&gt;

&lt;p&gt;if (authStatus === 'authenticated') return ;&lt;/p&gt;

&lt;p&gt;return (&lt;br&gt;
    &lt;/p&gt;
&lt;br&gt;
      {/* Left Column: Branding */}&lt;br&gt;
      &lt;br&gt;
         &lt;h1&gt;FinAI Agent&lt;/h1&gt;
&lt;br&gt;
      
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  {/* Right Column: Auth Form */}
  &amp;lt;div className="w-1/2 flex items-center justify-center"&amp;gt;
       &amp;lt;Authenticator /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;);&lt;br&gt;
};&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Injecting the Bearer Token&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To make the backend aware of who is calling it, we need to send the Cognito JWT. I updated my fetch calls in React to grab the session token dynamically:&lt;/p&gt;

&lt;p&gt;JavaScript&lt;br&gt;
import { fetchAuthSession } from 'aws-amplify/auth';&lt;/p&gt;

&lt;p&gt;const { tokens } = await fetchAuthSession();&lt;br&gt;
const jwtToken = tokens?.idToken?.toString();&lt;/p&gt;

&lt;p&gt;const response = await fetch(API_URL, {&lt;br&gt;
    headers: { 'Authorization': &lt;code&gt;Bearer ${jwtToken}&lt;/code&gt; }&lt;br&gt;
});&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Backend: Parsing JWT in Python (AWS Lambda)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;API Gateway handles the heavy lifting of verifying the signature, but I still needed my Lambda function to know who the user was to query DynamoDB correctly. I wrote a small base64 decoding snippet to extract the sub (user ID) and email.&lt;/p&gt;

&lt;p&gt;Python&lt;br&gt;
import base64&lt;br&gt;
import json&lt;/p&gt;

&lt;p&gt;def lambda_handler(event, context):&lt;br&gt;
    headers = event.get('headers', {})&lt;br&gt;
    auth_header = headers.get('authorization', headers.get('Authorization', ''))&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if auth_header.startswith('Bearer '):
    token = auth_header.split(' ')[1]
    payload_b64 = token.split('.')[1]
    payload_b64 += '=' * (-len(payload_b64) % 4) # Add padding

    payload = json.loads(base64.b64decode(payload_b64).decode('utf-8'))

    user_id = payload.get('sub')
    user_email = payload.get('email')

    print(f"Authenticated: {user_email} (ID: {user_id})")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;My Serverless app is now officially Multi-Tenant! Every user gets their own DynamoDB sandbox, and the AI greets them by their real name. 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5gw478hgudyn31utxmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5gw478hgudyn31utxmo.png" alt=" " width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>python</category>
      <category>security</category>
    </item>
    <item>
      <title>Day 51: I stopped building Login pages manually 🛑🔑</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Tue, 07 Apr 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-51-i-stopped-building-login-pages-manually-2ilc</link>
      <guid>https://forem.com/ericrodriguez10/day-51-i-stopped-building-login-pages-manually-2ilc</guid>
      <description>&lt;p&gt;I am officially done writing &lt;/p&gt; tags for login and registration.

&lt;p&gt;Today (Day 51 of my cloud journey), I connected my React application to Amazon Cognito using AWS Amplify UI.&lt;/p&gt;

&lt;p&gt;With just a few lines of configuration pointing to my User Pool ID and Client ID, I wrapped my app in the  component.&lt;/p&gt;

&lt;p&gt;What it gave me out of the box:&lt;/p&gt;

&lt;p&gt;A clean UI for Sign-In and Sign-Up.&lt;/p&gt;

&lt;p&gt;Automatic verification code emails (OTP).&lt;/p&gt;

&lt;p&gt;Secure JWT session management in local storage.&lt;/p&gt;

&lt;p&gt;A signOut hook to instantly kill the session.&lt;/p&gt;

&lt;p&gt;If you are a frontend developer building on AWS, using the Amplify UI library is practically a superpower. My dashboard is now completely locked down.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ynughr08k9cb1gifurl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ynughr08k9cb1gifurl.png" alt=" " width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>aws</category>
      <category>security</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Day 50 🎉: Securing my GenAI API with Amazon Cognito</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Mon, 06 Apr 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-50-securing-my-genai-api-with-amazon-cognito-5fdp</link>
      <guid>https://forem.com/ericrodriguez10/day-50-securing-my-genai-api-with-amazon-cognito-5fdp</guid>
      <description>&lt;p&gt;I hit Day 50 of my #100DaysOfCloud challenge today! To celebrate, I decided to tackle the scariest part of any GenAI side project: Billing Spikes from unprotected APIs.&lt;/p&gt;

&lt;p&gt;My AI Financial Agent uses Amazon Bedrock under the hood. Until today, the API was open. To fix this, I set up Amazon Cognito.&lt;/p&gt;

&lt;p&gt;Instead of building a custom login system (and probably hashing passwords wrong), I let AWS handle it.&lt;/p&gt;

&lt;p&gt;My Cognito Setup:&lt;/p&gt;

&lt;p&gt;Authentication: Email &amp;amp; Password.&lt;/p&gt;

&lt;p&gt;App Client: Public Client (for my React SPA).&lt;/p&gt;

&lt;p&gt;Security: AWS handles the heavy lifting of issuing JWTs (ID, Access, and Refresh tokens).&lt;/p&gt;

&lt;p&gt;Tomorrow, I will connect this User Pool to my React frontend using AWS Amplify, and configure my backend to validate the tokens. No Token = No AI generation = Safe Budget!&lt;/p&gt;

&lt;p&gt;If you are building with GenAI, put authentication in front of your APIs on Day 1 (or Day 50, better late than never!).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxdw8lk1hbwqpuzy52bs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxdw8lk1hbwqpuzy52bs.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>serverless</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Day 49: Adding a Monetization Layer to my Serverless App 💸</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Sat, 04 Apr 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-49-adding-a-monetization-layer-to-my-serverless-app-34jh</link>
      <guid>https://forem.com/ericrodriguez10/day-49-adding-a-monetization-layer-to-my-serverless-app-34jh</guid>
      <description>&lt;p&gt;If you are building a SaaS or Fintech application, eventually you need to figure out how to monetize it. Today, I added a Cross-Selling Recommendation Engine to my Serverless AI Financial Agent.&lt;/p&gt;

&lt;p&gt;The Concept:&lt;br&gt;
My backend already calculates a Financial Score (0-100) based on the user's spending habits. I created a new Python function that evaluates this score alongside their available cashflow to recommend specific financial products.&lt;/p&gt;

&lt;p&gt;Python&lt;br&gt;
def generate_financial_offers(score, income, expenses):&lt;br&gt;
    net_surplus = income - expenses&lt;br&gt;
    offers = []&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# High Score + Cashflow = Credit Card Upsell
if score &amp;gt;= 70 and net_surplus &amp;gt; 500:
    offers.append({
        "type": "CREDIT_CARD",
        "title": "FinAI Premium Rewards",
        "description": "Pre-qualified for our 2% cashback card."
    })
# Low Score + Debt = Consolidation Loan
elif score &amp;lt; 50 and net_surplus &amp;lt; 0:
    offers.append({
        "type": "LOAN",
        "title": "Debt Consolidation Loan",
        "description": "Consolidate your debt today with a 5.9% APR loan."
    })

return offers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This logic runs inside AWS Lambda and attaches the offers directly to the JSON response consumed by React. Next up, I'll be building out the UI components to display these offers!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frk9zrb7ogka6ww4jsh5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frk9zrb7ogka6ww4jsh5c.png" alt=" " width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>aws</category>
      <category>fintech</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Day 48: Deploy AWS Lambda without ClickOps (Using GitHub Actions &amp; OIDC)</title>
      <dc:creator>Eric Rodríguez</dc:creator>
      <pubDate>Fri, 03 Apr 2026 14:00:00 +0000</pubDate>
      <link>https://forem.com/ericrodriguez10/day-48-deploy-aws-lambda-without-clickops-using-github-actions-oidc-1iee</link>
      <guid>https://forem.com/ericrodriguez10/day-48-deploy-aws-lambda-without-clickops-using-github-actions-oidc-1iee</guid>
      <description>&lt;p&gt;Uploading .zip files to the AWS console gets old really fast. Today, I completely automated my serverless backend deployments.&lt;/p&gt;

&lt;p&gt;Instead of taking the easy route and storing AWS Access Keys in my GitHub Secrets (a major security anti-pattern), I implemented OIDC (OpenID Connect) authentication.&lt;/p&gt;

&lt;p&gt;How it works:&lt;/p&gt;

&lt;p&gt;Trust: AWS IAM is configured to trust GitHub's OIDC provider.&lt;/p&gt;

&lt;p&gt;Assume Role: My GitHub Action workflow uses aws-actions/configure-aws-credentials to assume a specific IAM role.&lt;/p&gt;

&lt;p&gt;Deploy: On every git push, the action packages my Python code and updates the Lambda function via the AWS CLI.&lt;/p&gt;

&lt;p&gt;The funny part? The automation worked so well that my Lambda executed faster than my third-party banking API (Plaid) could generate test data, throwing a PRODUCT_NOT_READY timeout error! A quick time.sleep(8) fixed the race condition.&lt;/p&gt;

&lt;p&gt;YAML&lt;/p&gt;

&lt;h1&gt;
  
  
  The magic step in the workflow
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActions-LambdaDeployRole
aws-region: eu-north-1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, my deployments take 15 seconds, and I have zero static credentials lying around. Escaping ClickOps feels great.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwhgg1wclededat5ae8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwhgg1wclededat5ae8i.png" alt=" " width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>serverless</category>
      <category>security</category>
    </item>
  </channel>
</rss>
