<?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: Joanne Skiles</title>
    <description>The latest articles on Forem by Joanne Skiles (@drjoanneskiles).</description>
    <link>https://forem.com/drjoanneskiles</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%2F850361%2Fff11174d-5670-46ba-a8b3-76393debf8be.png</url>
      <title>Forem: Joanne Skiles</title>
      <link>https://forem.com/drjoanneskiles</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/drjoanneskiles"/>
    <language>en</language>
    <item>
      <title>I Built a Local AI Podcast Editor Because I'm Done Renting My Own Workflow</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Sun, 15 Mar 2026 17:41:09 +0000</pubDate>
      <link>https://forem.com/drjoanneskiles/i-built-a-local-ai-podcast-editor-because-im-done-renting-my-own-workflow-1mpk</link>
      <guid>https://forem.com/drjoanneskiles/i-built-a-local-ai-podcast-editor-because-im-done-renting-my-own-workflow-1mpk</guid>
      <description>&lt;p&gt;Despite how much I post online, I'm pretty deliberate about where my data actually lives. Not in a tinfoil hat way, I use cloud services, I have accounts everywhere. But there's a difference between choosing to share something and having no other option.&lt;/p&gt;

&lt;p&gt;I started a podcast. Suddenly I needed to think about marketing, which means Shorts, Reels, vertical cuts, captions. The tools that do this are good. Genuinely good. But they all run on the same model: your files live on their servers, your workflow lives in their interface, and your access lives on their billing page. Cancel your subscription and you're starting over. Want to export your project? Hope they support that. Want to know what they're doing with your footage? Read the terms of service and good luck.&lt;/p&gt;

&lt;p&gt;I'm not interested in that trade. I want to own my pipeline the same way I own my code. So I built a POC called &lt;strong&gt;DarkRoom&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local-First Isn't Just a Preference, It's an Architecture
&lt;/h2&gt;

&lt;p&gt;There's a growing conversation in software about local-first design, the idea that your data should live on your device by default and only sync or share when you explicitly choose to. Tools like Obsidian, Excalidraw, and a wave of newer apps are built around this. Your device is the source of truth, not some server you're renting space on.&lt;/p&gt;

&lt;p&gt;The content creation space hasn't caught up. Almost everything assumes the cloud is the primary location and your device is just a thin client. That's great for collaboration. But it also means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're paying rent on your own data&lt;/li&gt;
&lt;li&gt;Your workflow can disappear when a company pivots, gets acquired, or raises prices&lt;/li&gt;
&lt;li&gt;You have no real visibility into how your content is used after upload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DarkRoom is an attempt to apply local-first thinking to podcast production. The processing happens on your machine. The files are yours. The workflow is just a Python server and some JSON. You can read every artifact it produces in a text editor, put it in git, move it to another machine, or delete it. No account required. No export wizard. No "your project will be deleted in 30 days."&lt;/p&gt;

&lt;p&gt;The only external dependency (which is optional) is one Claude API call per episode, text in and edit decisions out, which you pay for directly at fractions of a cent per use.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is DarkRoom?
&lt;/h2&gt;

&lt;p&gt;DarkRoom is a local-first, multi-camera podcast editor. You give it your raw camera files, it transcribes them on your machine, an AI generates an edit decision list, you review and tweak it in the browser, and FFmpeg renders the final outputs. Nothing leaves your machine except that one, optional, text-only API call.&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%2Fxum29x0693cb7x2e1qg7.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%2Fxum29x0693cb7x2e1qg7.png" alt="DarkRoom editor showing multi-camera timeline and transcript panel - Episode 3 Her Career Unplugged" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Python + Flask&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transcription&lt;/td&gt;
&lt;td&gt;OpenAI Whisper (runs locally)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI editing decisions&lt;/td&gt;
&lt;td&gt;Claude (&lt;code&gt;claude-sonnet-4-6&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rendering&lt;/td&gt;
&lt;td&gt;FFmpeg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Vanilla HTML/JS/CSS, one file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;JSON in a &lt;code&gt;projects/&lt;/code&gt; folder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No database. No Docker. No SaaS. No monthly fee. Just a Python server you run on your own machine, using open tools that have been around for years.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Actually Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Upload your cameras
&lt;/h3&gt;

&lt;p&gt;You record with 2 to 4 cameras, one per speaker, already synced. Upload each file and tag it with a speaker name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Local transcription via Whisper
&lt;/h3&gt;

&lt;p&gt;Whisper runs locally on each camera's audio. Because you're uploading one file per speaker, attribution is free. Whisper doesn't need to guess who said what. The result is a merged, time-coded transcript where every line already knows which camera it came from.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - AI generates the Edit Decision List
&lt;/h3&gt;

&lt;p&gt;This is the only moment anything leaves your machine. The transcript text gets sent to Claude and Claude returns a JSON &lt;strong&gt;Edit Decision List (EDL)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"segments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"seg_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;12.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"keep"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"camera"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"layout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"single"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"seg_002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;12.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;15.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"keep"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"camera"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"filler words"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"clips"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"clip_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Best 60-90s clip for Shorts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;94.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Strong hook, complete thought"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It tells FFmpeg what to keep, what to cut, which camera to show when, and which chunk would make the best Short. Plain JSON. Read it, edit it by hand, version control it, diff it between episodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Review in the browser
&lt;/h3&gt;

&lt;p&gt;A UI served from localhost shows camera previews, per-speaker timeline tracks, and a transcript panel synced to the timeline. Toggle cuts, swap cameras, change layouts. The EDL is just a file on disk so nothing is locked away.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Render
&lt;/h3&gt;

&lt;p&gt;Pick your targets: 16:9 full edit, 9:16 vertical, or the best 60 to 90 second Short. FFmpeg renders them into &lt;code&gt;projects/{id}/output/&lt;/code&gt;. They're yours.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Ownership Model
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your files stay local.&lt;/strong&gt; Whisper runs on your machine, video never goes anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your data is readable.&lt;/strong&gt; Every project is a folder of MP4s and JSON files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your workflow is portable.&lt;/strong&gt; No account, no export flow, no "download before you cancel."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your cost is marginal.&lt;/strong&gt; One Claude API call per episode, typically under $0.10. Pay per use, not per month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your stack is replaceable.&lt;/strong&gt; Want to swap Claude for a local LLM? That's one file. Want to parse the EDL yourself? It's JSON. You're not locked into anything.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Running It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/you/darkroom
&lt;span class="nb"&gt;cd &lt;/span&gt;darkroom
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="c"&gt;# add ANTHROPIC_API_KEY to .env&lt;/span&gt;
python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:5000&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;A few things I want to explore:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replace Claude with a local LLM.&lt;/strong&gt; The API call is already isolated in one file (&lt;code&gt;editor.py&lt;/code&gt;). Plugging in Ollama or any OpenAI-compatible endpoint would make this fully air-gapped. The tradeoff is edit quality but for someone who wants zero external dependencies, it's a straightforward swap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-captions baked in.&lt;/strong&gt; Whisper already produces the transcript. Burning subtitles into the vertical cut is a small FFmpeg step and probably the highest-value addition for short-form content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smarter clip selection.&lt;/strong&gt; Right now Claude picks the best 60 to 90 second window. With a bit more prompt work you could get multiple ranked clip candidates per episode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A proper timeline UI.&lt;/strong&gt; The current review interface is functional but minimal. A drag-and-drop timeline where you can scrub through cuts before rendering would make the whole review step a lot nicer.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Is (and Isn't)
&lt;/h2&gt;

&lt;p&gt;This is a proof of concept. The AI cuts are a starting point and you still review everything before anything renders. It is not Descript. It will not auto-post to Instagram.&lt;/p&gt;

&lt;p&gt;What it is: a working pipeline from raw multi-camera footage to a full edit, a vertical cut, and a Short, on your own hardware, for less than a dime an episode, where every file is something you own and every dependency is something you can replace.&lt;/p&gt;

&lt;p&gt;The tools already existed. Whisper, FFmpeg, Claude's API, none of this is new. DarkRoom is just the glue that connects them in a way that keeps you in control.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Repo: &lt;a href="https://github.com/chaotictoejam/darkroom" rel="noopener noreferrer"&gt;https://github.com/chaotictoejam/darkroom&lt;/a&gt;&lt;br&gt;
Stack: Python · Flask · Whisper · Claude · FFmpeg&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>localfirst</category>
      <category>python</category>
    </item>
    <item>
      <title>Building "Ghost in The Code" with Kiro: A Kiroween Hackathon Journey 👻</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Mon, 24 Nov 2025 04:02:43 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-ghost-in-the-code-with-kiro-a-kiroween-hackathon-journey-20fl</link>
      <guid>https://forem.com/aws-builders/building-ghost-in-the-code-with-kiro-a-kiroween-hackathon-journey-20fl</guid>
      <description>&lt;p&gt;&lt;strong&gt;DEMO VIDEO HERE:&lt;/strong&gt; &lt;a href="https://youtu.be/W4fzNJCMGD8" rel="noopener noreferrer"&gt;https://youtu.be/W4fzNJCMGD8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a contestant on &lt;a href="https://reinvent.awsevents.com/livestreams/" rel="noopener noreferrer"&gt;Road to re:Invent&lt;/a&gt;, there’s one thing I know for sure: the hackathon is going to use Kiro. So when the Kiroween challenge appeared, I thought, &lt;em&gt;perfect - this is how I get Kiro into my muscle memory before competition day.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of my biggest passions is making tech fun and accessible for kids. &lt;/p&gt;

&lt;p&gt;So naturally: &lt;strong&gt;Halloween + STEM = Ghost in The Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A kid-friendly web game where players help a friendly ghost debug “haunted” code to save the digital world. Each spooky “bug” maps to a real programming concept (loops, conditionals, logic puzzles), and fixing it triggers a fun little animation.&lt;/p&gt;

&lt;p&gt;But this wasn’t just a cute idea. It quickly turned into a very real engineering challenge. In a short amount of time, I needed to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple interactive coding challenges across core concept types&lt;/li&gt;
&lt;li&gt;A badge/achievement system&lt;/li&gt;
&lt;li&gt;Smooth animations and visual feedback&lt;/li&gt;
&lt;li&gt;AWS infrastructure for text-to-speech&lt;/li&gt;
&lt;li&gt;Session-based progress tracking&lt;/li&gt;
&lt;li&gt;A full TypeScript codebase with strict mode&lt;/li&gt;
&lt;li&gt;All while trying to keep the game fun, polished, and kid-friendly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So yeah, this project had a _lot _ of moving parts.&lt;/p&gt;

&lt;p&gt;And that’s exactly where Kiro came in.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I used Kiro
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Steering Kiro with Project Context
&lt;/h3&gt;

&lt;p&gt;Before writing any code, I created a &lt;strong&gt;steering document&lt;/strong&gt;. This is a markdown file that tells Kiro about my project's context, constraints, and preferences. This single file made every interaction with Kiro more productive (I am a busy person and only have a couple of hours at the end of each day to work on a project - I can't be wasting time telling Kiro not to do something 100 times.)&lt;/p&gt;

&lt;h4&gt;
  
  
  What's in My Steering Doc
&lt;/h4&gt;

&lt;p&gt;I created &lt;code&gt;.kiro/steering/project-context.md&lt;/code&gt; with five key sections:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Project Overview&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;An interactive web game where kids help a friendly ghost "debug" haunted code 
to save the digital world. Each bug represents a coding concept (loops, 
conditions, logic puzzles) with spooky animations as rewards.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one paragraph ensured Kiro always remembered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Target audience: kids&lt;/li&gt;
&lt;li&gt;Theme: friendly ghost, Halloween vibes&lt;/li&gt;
&lt;li&gt;Educational goal: teach coding concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Technology Stack&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; Frontend: Vite (for fast development and build)
&lt;span class="p"&gt;-&lt;/span&gt; Infrastructure: AWS CDK (for deployment)
&lt;span class="p"&gt;-&lt;/span&gt; Language: TypeScript (for all code where possible)
&lt;span class="p"&gt;-&lt;/span&gt; Lambda Functions: TypeScript with Node.js 20.x runtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kiro never suggested alternatives. No webpack. No CRA. No JavaScript. It stayed strictly within the stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Development Philosophy&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;This is a &lt;span class="gs"&gt;**hackathon project**&lt;/span&gt; with the following priorities:
&lt;span class="p"&gt;-&lt;/span&gt; Focus on core functionality and user experience
&lt;span class="p"&gt;-&lt;/span&gt; Code should be clean and understandable, but doesn't need to be production-grade
&lt;span class="p"&gt;-&lt;/span&gt; Prioritize speed of development and demonstrable features
&lt;span class="p"&gt;-&lt;/span&gt; It's okay to use simplified approaches and mock data where appropriate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was &lt;strong&gt;huge&lt;/strong&gt;. Kiro understood I didn't need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exhaustive test coverage (just core functionality)&lt;/li&gt;
&lt;li&gt;Enterprise-level error handling&lt;/li&gt;
&lt;li&gt;Over-engineered abstractions&lt;/li&gt;
&lt;li&gt;Perfect documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It optimized for &lt;strong&gt;speed and demonstrable features&lt;/strong&gt; - exactly what a hackathon needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Implementation Preferences&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Always use TypeScript**&lt;/span&gt; - All code should be written in TypeScript
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Follow DRY**&lt;/span&gt; - Extract common logic into reusable components
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**No Docker**&lt;/span&gt; - Avoid Docker dependencies; use local bundling
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Self-Documenting Code**&lt;/span&gt; - Clear naming, minimal comments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These rules shaped every suggestion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kiro never used &lt;code&gt;any&lt;/code&gt; types - always proper TypeScript&lt;/li&gt;
&lt;li&gt;It extracted duplicate code into hooks and utilities automatically&lt;/li&gt;
&lt;li&gt;It never suggested Docker for Lambda bundling&lt;/li&gt;
&lt;li&gt;Comments only appeared for complex logic, not obvious code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. TypeScript Guidelines&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; Use TypeScript for all new code (frontend, backend, Lambda functions, CDK infrastructure)
&lt;span class="p"&gt;-&lt;/span&gt; Enable strict mode in tsconfig.json
&lt;span class="p"&gt;-&lt;/span&gt; Define proper interfaces and types for all data structures
&lt;span class="p"&gt;-&lt;/span&gt; Use type guards for runtime type checking
&lt;span class="p"&gt;-&lt;/span&gt; Avoid &lt;span class="sb"&gt;`any`&lt;/span&gt; type - use &lt;span class="sb"&gt;`unknown`&lt;/span&gt; or proper types instead
&lt;span class="p"&gt;-&lt;/span&gt; Leverage TypeScript's type inference where appropriate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every file generated aligned with these constraints. Zero surprises.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategies That Made the Biggest Difference
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Be Explicit About Constraints&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of: "Use TypeScript"&lt;br&gt;
I wrote: "&lt;strong&gt;Always use TypeScript&lt;/strong&gt; - All code should be written in TypeScript where possible for type safety"&lt;/p&gt;

&lt;p&gt;The emphasis and explanation made Kiro prioritize this in every decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Define Your "Done" Criteria&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Quality Standards:
&lt;span class="p"&gt;-&lt;/span&gt; Tests should cover core functionality only - no need for exhaustive test coverage
&lt;span class="p"&gt;-&lt;/span&gt; Error handling should be present but can be basic
&lt;span class="p"&gt;-&lt;/span&gt; Performance optimization is secondary to getting features working
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevented Kiro from over-engineering. It knew when to stop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. State Your Anti-Patterns&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**No Docker**&lt;/span&gt; - Avoid Docker dependencies; use local bundling
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Self-Documenting Code**&lt;/span&gt; - Avoid redundant comments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kiro actively avoided these patterns, instead of me having to correct it (over and over).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Include Context About Your Audience&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Target Audience: Kids learning coding concepts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This influenced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Language in UI (simple, encouraging)&lt;/li&gt;
&lt;li&gt;Error messages (friendly, not technical)&lt;/li&gt;
&lt;li&gt;Educational content (age-appropriate)&lt;/li&gt;
&lt;li&gt;Color choices (bright, engaging)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think it really worked - my 7-year-old keeps wanting to play it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Specify Your Tech Stack Upfront&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kiro never suggested alternatives or asked "should we use X or Y?" It knew the stack and worked within it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples where I used Steering in the Project
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Example 1: DRY Principle (Learned the Hard Way)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After building several similar components with repeated code, I realized Kiro wasn't automatically suggesting refactoring. So I added to my steering doc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Follow DRY**&lt;/span&gt; - Extract common logic into reusable components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kiro started proactively refactoring patterns and even generated the &lt;code&gt;usePersistence&lt;/code&gt; hook without being asked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; If you see a repeated Kiro weakness, add a steering rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2: TypeScript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every generated file had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Challenge&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onComplete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Never:&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Always:&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CodeSubmission&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 3: No Docker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When setting up Lambda bundling, Kiro used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lambda/polly&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;bundling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODEJS_20_X&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bundlingImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npm install &amp;amp;&amp;amp; cp -r . /asset-output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Native CDK bundling (lightweight &lt;em&gt;phew&lt;/em&gt;), no Docker required - exactly as specified. (Why I avoid Docker is another story for another time)&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Create Effective Steering Docs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Start with these sections:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Project Overview&lt;/strong&gt; - What are you building? For whom?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technology Stack&lt;/strong&gt; - What tools/frameworks are you using?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development Philosophy&lt;/strong&gt; - What are your priorities and constraints?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation Preferences&lt;/strong&gt; - What patterns should Kiro follow/avoid?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality Standards&lt;/strong&gt; - What does "done" look like?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Keep it concise:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My steering doc is ~50 lines&lt;/li&gt;
&lt;li&gt;Each rule is 1-2 sentences&lt;/li&gt;
&lt;li&gt;Use bold for emphasis on critical rules&lt;/li&gt;
&lt;li&gt;Update as you learn what matters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Make it actionable:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bad: "Write good code"&lt;/li&gt;
&lt;li&gt;Good: "Follow DRY - Extract common logic into reusable components"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Include anti-patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What should Kiro NOT do?&lt;/li&gt;
&lt;li&gt;What tools should it avoid?&lt;/li&gt;
&lt;li&gt;What's out of scope?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The ROI of Steering
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;15 minutes writing the doc&lt;/li&gt;
&lt;li&gt;Hours saved in corrections&lt;/li&gt;
&lt;li&gt;Consistent TypeScript, architecture, decisions&lt;/li&gt;
&lt;li&gt;No repeated reminders to Kiro&lt;/li&gt;
&lt;li&gt;Lower cognitive load&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Automating My Workflow with Agent Hooks
&lt;/h2&gt;

&lt;p&gt;One of Kiro's most powerful features is &lt;strong&gt;agent hooks&lt;/strong&gt; - automated workflows that trigger on specific events. I set up four hooks that dramatically improved my development velocity:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Auto-run Tests on Save&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Every time I saved a TypeScript file, tests automatically ran. This gave me instant feedback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Auto-run Tests on Save"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"when"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fileEdited"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"patterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"**/*.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/*.tsx"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"then"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"runCommand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm test -- --run"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Caught bugs within seconds of writing code. No more "I'll test it later" - testing became automatic.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;ESLint on Save&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Automatic linting kept my code quality high without thinking about it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESLint on Save"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"when"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fileEdited"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"patterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"**/*.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/*.tsx"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"then"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"runCommand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx eslint {{filePath}}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Maintained consistent code style across the project. Fixed issues before they made it to commits.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Build Validation Check&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When I modified core components or configuration, a build automatically triggered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Build Validation Check"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"when"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fileEdited"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"patterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/components/**/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/engine/**/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite.config.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"then"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"runCommand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Caught breaking changes immediately. No more "it works on my machine" surprises.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;CDK Synth Validation&lt;/strong&gt; (The Smart One)
&lt;/h3&gt;

&lt;p&gt;This hook was special - it triggered Kiro itself to validate infrastructure changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CDK Synth Validation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"when"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fileEdited"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"patterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"infrastructure/**/*.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"then"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"askAgent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Infrastructure code modified. Validate CDK synthesis and check for:
    1. Synthesis errors
    2. CloudFormation issues
    3. IAM policy problems
    4. Breaking changes"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Kiro would automatically run &lt;code&gt;cdk synth&lt;/code&gt;, analyze the output, and tell me if my infrastructure changes were valid. It caught IAM permission issues and resource conflicts before I even tried to deploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Compound Effect
&lt;/h3&gt;

&lt;p&gt;These hooks created a &lt;strong&gt;continuous validation loop&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write code → Auto-lint → Auto-test → Auto-build → Get feedback&lt;/li&gt;
&lt;li&gt;All within seconds, without manual intervention&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was especially valuable during the hackathon when I was moving fast. The hooks acted as a safety net, catching issues immediately so I could maintain velocity without accumulating technical debt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up Hooks
&lt;/h3&gt;

&lt;p&gt;Creating hooks in Kiro is simple - just use the command palette and search for "Kiro Hook UI". You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trigger on file saves, agent completions, or session starts&lt;/li&gt;
&lt;li&gt;Run shell commands or ask Kiro to analyze something&lt;/li&gt;
&lt;li&gt;Use file patterns to target specific parts of your codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a hackathon project, I recommend starting with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Auto-run tests on save (catch bugs fast)&lt;/li&gt;
&lt;li&gt;Linting on save (maintain quality)&lt;/li&gt;
&lt;li&gt;Build validation for critical files (prevent breaking changes)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Spec-Driven Development: Structure Meets Speed
&lt;/h2&gt;

&lt;p&gt;I started with &lt;strong&gt;spec-driven development&lt;/strong&gt; to build the foundation, then switched to &lt;strong&gt;vibe coding&lt;/strong&gt; once the specs were complete. This approach gave me the best of both worlds (in my humble opinion).&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Spec-Driven Development?
&lt;/h3&gt;

&lt;p&gt;Spec-driven development in Kiro is a structured workflow where you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define requirements&lt;/strong&gt; - User stories with acceptance criteria&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a design&lt;/strong&gt; - Architecture, components, data models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate tasks&lt;/strong&gt; - Actionable implementation checklist&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute incrementally&lt;/strong&gt; - Kiro implements one task at a time&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  My Actual Workflow: Spec First, Then Vibe
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: Spec-Driven Foundation&lt;/strong&gt; (Days 1-2)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core game engine and state management&lt;/li&gt;
&lt;li&gt;AWS infrastructure with CDK&lt;/li&gt;
&lt;li&gt;Challenge system and validation&lt;/li&gt;
&lt;li&gt;Basic UI components&lt;/li&gt;
&lt;li&gt;Progress tracking and persistence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Vibe Coding Extensions&lt;/strong&gt; (Days 3-4)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Badge system enhancements&lt;/li&gt;
&lt;li&gt;Animation polish and new effects&lt;/li&gt;
&lt;li&gt;Voice narration with Polly&lt;/li&gt;
&lt;li&gt;Accessibility improvements&lt;/li&gt;
&lt;li&gt;UI refinements and styling&lt;/li&gt;
&lt;li&gt;GitHub Actions CI/CD (added as a spec when needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Main Game Spec
&lt;/h3&gt;

&lt;p&gt;For the core game, I created a comprehensive spec with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11 Requirements&lt;/strong&gt; covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Game interface and UX&lt;/li&gt;
&lt;li&gt;Challenge system&lt;/li&gt;
&lt;li&gt;Animation feedback&lt;/li&gt;
&lt;li&gt;Hint system&lt;/li&gt;
&lt;li&gt;Progress tracking&lt;/li&gt;
&lt;li&gt;Badge system&lt;/li&gt;
&lt;li&gt;Accessibility features&lt;/li&gt;
&lt;li&gt;Educator dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;42 Implementation Tasks&lt;/strong&gt; including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core setup (CDK, Vite, React)&lt;/li&gt;
&lt;li&gt;Game engine and state management&lt;/li&gt;
&lt;li&gt;UI components&lt;/li&gt;
&lt;li&gt;Animation system&lt;/li&gt;
&lt;li&gt;Educational content&lt;/li&gt;
&lt;li&gt;Accessibility implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Spec-Driven vs Vibe Coding: The Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Spec-Driven&lt;/th&gt;
&lt;th&gt;Vibe Coding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;15-30 min (requirements + design)&lt;/td&gt;
&lt;td&gt;Immediate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Clarity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Crystal clear roadmap&lt;/td&gt;
&lt;td&gt;Figure it out as you go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Handles complex features well&lt;/td&gt;
&lt;td&gt;Best for simple tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kiro maintains full context&lt;/td&gt;
&lt;td&gt;Can lose thread on complex work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Iteration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Structured checkpoints&lt;/td&gt;
&lt;td&gt;Continuous refinement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auto-generated&lt;/td&gt;
&lt;td&gt;Manual if needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure, core systems&lt;/td&gt;
&lt;td&gt;UI, styling, quick fixes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why This Workflow Worked Perfectly
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Start with Spec = Solid Foundation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creating the initial spec forced me to think through what features were actually needed, how components would interact, and where complexity would hide. The spec gave me a &lt;strong&gt;roadmap&lt;/strong&gt; with no analysis paralysis and only a little architectural regret (I hate committing sometimes).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Switch to Vibe = Rapid Iteration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the foundation was solid, vibe coding let me experiment with animations, try different UI approaches quickly, and add "wow factor" features on the fly.&lt;/p&gt;

&lt;p&gt;Around day 2, I had a working game from the spec. Then I switched to vibe coding and added particle burst animations, ghost STT with Polly, badge unlock celebrations, and accessibility options.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Lesson: Spec the Foundation, Vibe the Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Day 1:&lt;/strong&gt; Create a comprehensive spec for your core system. Let Kiro generate the design and task list. Execute foundational tasks methodically to get a "working but basic" state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Days 2-N:&lt;/strong&gt; Switch to vibe coding for features, experiments, polish, and quick iterations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When needed:&lt;/strong&gt; If a vibe-coded feature gets complex, pause and create a mini-spec.&lt;/p&gt;

&lt;p&gt;Starting with a spec prevents architectural walls and wasted refactoring time. Switching to vibe coding after the foundation lets you move fast and stay creative.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About MCP (Model Context Protocol)?
&lt;/h2&gt;

&lt;p&gt;Kiro supports MCP servers to extend its capabilities with external tools and data sources. I had it configured but honestly? &lt;strong&gt;I didn't use it much for this project.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The built-in features (code generation, file operations, AWS knowledge) covered 99% of what I needed. MCP is powerful for specialized workflows (like accessing proprietary APIs or custom data sources), but for a hackathon web game, the core Kiro features were more than enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When MCP might have helped:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If I needed real-time data from external APIs&lt;/li&gt;
&lt;li&gt;If I was integrating with proprietary systems&lt;/li&gt;
&lt;li&gt;If I needed specialized domain knowledge beyond AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why I didn't need it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kiro's AWS knowledge was comprehensive&lt;/li&gt;
&lt;li&gt;All my data was local (challenge JSON files)&lt;/li&gt;
&lt;li&gt;The project was self-contained&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MCP is there when you need it, but don't feel like you're missing out if you don't use it. Focus on mastering the core features first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Kiroween challenged me to build something spooky, playful, and creative. Kiro made it possible to build something polished and educational in record time.&lt;/p&gt;

&lt;p&gt;Whether you're working on a hackathon project, a side project, or something for production, a steering doc + hybrid development strategy is a game-changer.&lt;/p&gt;

&lt;p&gt;Now if you'll excuse me, I have some haunted code to debug with my son... 👻✨&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;View the code&lt;/strong&gt;: &lt;a href="https://github.com/ChaoticLabs/ghost-in-the-code" rel="noopener noreferrer"&gt;https://github.com/ChaoticLabs/ghost-in-the-code&lt;/a&gt; &lt;/p&gt;

</description>
      <category>kiro</category>
      <category>aws</category>
      <category>webdev</category>
      <category>kiroween</category>
    </item>
    <item>
      <title>Building a Serverless Dungeon Master Agent on AWS</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Sun, 28 Sep 2025 18:52:22 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-a-serverless-dungeon-master-agent-on-aws-3j7k</link>
      <guid>https://forem.com/aws-builders/building-a-serverless-dungeon-master-agent-on-aws-3j7k</guid>
      <description>&lt;p&gt;Earlier this month, I couldn't help but think, what if I could have a mini AI-Powered Dungeon Master (DM)? What if I could run game-ready DM in the cloud that remembers your character, generates quests, and scales for free-tier fun? Well, with some finagling (utilizing Amazon Bedrock Agents and serverless building blocks), I created my prototype. &lt;/p&gt;

&lt;p&gt;In this walkthrough, I’ll show you how I built this Serverless DM Agent using Amazon Bedrock, Lambda, DynamoDB, API Gateway, and S3. You’ll learn the architecture, see the code, and be able to deploy it yourself with AWS CDK.&lt;/p&gt;

&lt;p&gt;And on top of it all, it costs less than a monthly coffee subscription!&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon Bedrock Meets Serverless Architecture
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Serverless Dungeon Master Agent&lt;/strong&gt; is a proof-of-concept that combines Amazon Bedrock's AI capabilities with a fully serverless AWS architecture. Here's what makes it special:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persistent Character Memory&lt;/strong&gt; - The AI DM remembers characters stats, inventory, backstory, and every decision you've made. No more "Wait, what happened last session?"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Storytelling&lt;/strong&gt; The AI generates narratives on the fly, adapting to choices and creating unique storylines that respond to characters actions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-time Streaming&lt;/strong&gt; Responses stream back naturally, creating an immersive conversation flow that feels like chatting with a human DM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost-Effective Gaming&lt;/strong&gt; Runs around $1-5 per month for casual gameplay.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture + Core Components
&lt;/h2&gt;

&lt;p&gt;At its heart, this project is just a set of AWS services working together:&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%2Fbsrze706jv16iesz9npu.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%2Fbsrze706jv16iesz9npu.png" alt="Player → API Gateway → Lambda (session-proxy) → Bedrock Agent → Lambda (Game Actions) → DynamoDB" width="800" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock Agents&lt;/strong&gt; serve as the brain, handling the AI reasoning, memory management, and action orchestration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Functions&lt;/strong&gt; provide the serverless compute layer:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;session-proxy&lt;/code&gt;: Manages agent invocation and streams responses&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;game-actions&lt;/code&gt;: Handles character CRUD operations and session logging&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;DynamoDB&lt;/strong&gt; stores character sheets, game history, and session state with automatic scaling.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;API Gateway&lt;/strong&gt; exposes a clean REST endpoint for game interactions.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;S3 Static Website&lt;/strong&gt; Hosts the lightweight web client where players type their actions.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  How it flows
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A player types something like: “I sneak past the guards.”&lt;/li&gt;
&lt;li&gt;API Gateway forwards that request to the Lambda session proxy.&lt;/li&gt;
&lt;li&gt;Lambda calls the Bedrock Agent. The agent either narrates directly or uses an action group (for example, checking a stealth score in DynamoDB). &lt;/li&gt;
&lt;li&gt;The DM’s response streams back to the client.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No servers to manage, no overworked innkeeper behind the console. Just a clean, serverless system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features That Make It Work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Smart Memory Management
&lt;/h3&gt;

&lt;p&gt;The system maintains context across sessions, remembering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Character progression and stats&lt;/li&gt;
&lt;li&gt;Inventory changes&lt;/li&gt;
&lt;li&gt;Story decisions and consequences&lt;/li&gt;
&lt;li&gt;Campaign history and world state&lt;/li&gt;
&lt;/ul&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%2F06zzmwysnogk5xrc16qt.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%2F06zzmwysnogk5xrc16qt.png" alt="Example of remembering the last session" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adaptive Difficulty
&lt;/h3&gt;

&lt;p&gt;The AI DM adjusts challenges based on characters level and past performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extensible Game Mechanics
&lt;/h3&gt;

&lt;p&gt;Want to add dice rolling, combat systems, or custom rules? The modular Lambda architecture makes it easy to extend functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js 20+&lt;/strong&gt; and &lt;strong&gt;AWS CDK v2&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Account&lt;/strong&gt; with Amazon Bedrock access enabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock Model Access&lt;/strong&gt;: Ensure your account has access to your chosen foundation model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt; configured with appropriate permissions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.8+&lt;/strong&gt; (for local development server) - you can also use the VS Code Extension - Live Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The deployment process is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone and install&lt;/span&gt;
git clone https://github.com/chaotictoejam/serverless-dungeon-master
&lt;span class="nb"&gt;cd &lt;/span&gt;serverless-dungeon-master/dm-agent
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Deploy with CDK&lt;/span&gt;
npx cdk deploy

&lt;span class="c"&gt;# Configure and play (optional)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../web
python serve.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The AI DM is ready to run its first campaign.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The complete source code is available &lt;a href="https://github.com/chaotictoejam/serverless-dungeon-master" rel="noopener noreferrer"&gt;here&lt;/a&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full CDK infrastructure code&lt;/li&gt;
&lt;li&gt;Lambda function implementations&lt;/li&gt;
&lt;li&gt;Web client for testing&lt;/li&gt;
&lt;li&gt;Detailed setup instructions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're a developer interested in AI applications, a gamer looking for consistent campaigns, or someone curious about serverless architecture, this project demonstrates how modern cloud services can solve real-world problems in creative ways.&lt;/p&gt;

&lt;p&gt;The future of tabletop gaming might just be serverless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Deep Dive
&lt;/h2&gt;

&lt;p&gt;For those interested in the implementation details, here's how the CDK infrastructure brings this AI DM to life:&lt;/p&gt;

&lt;h3&gt;
  
  
  CDK Stack Architecture
&lt;/h3&gt;

&lt;p&gt;The entire infrastructure is defined in a single CDK stack that orchestrates five key components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DmAgentStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Configuration with flexible model selection&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fmId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tryGetContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fmId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us.anthropic.claude-3-7-sonnet-20250219-v1:0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agentName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tryGetContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agentName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DungeonMaster&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DynamoDB: Game State Persistence
&lt;/h3&gt;

&lt;p&gt;The game state table uses a composite key design for efficient player/session queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DmGameState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dmGameState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playerId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;sortKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sessionId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;billingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BillingMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAY_PER_REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pointInTimeRecoverySpecification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pointInTimeRecoveryEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-session support&lt;/strong&gt; per player&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient queries&lt;/strong&gt; by player or session&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic scaling&lt;/strong&gt; with pay-per-request billing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data protection&lt;/strong&gt; with point-in-time recovery&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bedrock Agent: The AI Brain
&lt;/h3&gt;

&lt;p&gt;The Bedrock Agent configuration defines the DM's personality and capabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DmAgent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;agentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;foundationModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fmId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;agentResourceRoleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agentServiceRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are an AI Dungeon Master. Run safe, imaginative adventures.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Style: concise narration + clear choices. Never reveal tools or raw JSON.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When you need to read or persist game state, call the GameActions tools.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Default to PG-13 content; avoid explicit or unsafe material.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;idleSessionTtlInSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;autoPrepare&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Action Groups: Game Mechanics Integration
&lt;/h3&gt;

&lt;p&gt;The agent connects to game mechanics through a structured action group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;actionGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
  &lt;span class="na"&gt;actionGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GameActions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Game state operations (DynamoDB-backed)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;actionGroupExecutor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gameActionsFn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;functionSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch a player character by playerId + sessionId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Player identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Session identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;save_character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Save/replace the player character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;character&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Character data as JSON&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;append_log&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Append a narrative log entry to world state&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log entry text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lambda Functions: Serverless Compute
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Session Proxy Lambda
&lt;/h4&gt;

&lt;p&gt;Handles agent invocation and streams responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessionProxyFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambdaNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NodejsFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SessionProxyFn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./lambda/session-proxy/index.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODEJS_20_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;AGENT_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrAgentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ALIAS_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrAgentAliasId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The session proxy implementation handles streaming responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvokeAgentCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AGENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;agentAliasId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ALIAS_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contextualInput&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bedrockAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunkEvent&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunkEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;decodedResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Game Actions Lambda
&lt;/h4&gt;

&lt;p&gt;Implements the core game mechanics with DynamoDB operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;save_character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ddb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UpdateCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;UpdateExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SET playerCharacter = :c, lastUpdated = :t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ExpressionAttributeValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;saved&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  API Gateway: HTTP Interface
&lt;/h3&gt;

&lt;p&gt;The API Gateway configuration enables CORS and connects to the session proxy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;apigwv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HttpApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DmApi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;apiName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dm-agent-api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;corsPreflight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;allowOrigins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;allowMethods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;apigwv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CorsHttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apigwv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CorsHttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPTIONS&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;allowHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRoutes&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/play&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;apigwv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;integration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;integrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HttpLambdaIntegration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PlayIntegration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionProxyFn&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Security &amp;amp; IAM Configuration
&lt;/h3&gt;

&lt;p&gt;The stack implements least-privilege access with specific IAM roles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agentServiceRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AgentServiceRole&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bedrock.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;StringEquals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aws:SourceAccount&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ACCOUNT_ID&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;agentServiceRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bedrock:InvokeModel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bedrock:InvokeModelWithResponseStream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bedrock:GetFoundationModel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment Outputs
&lt;/h3&gt;

&lt;p&gt;The stack provides essential outputs for client configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ApiUrl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiEndpoint&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AgentId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrAgentId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AgentAliasId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrAgentAliasId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Design Decisions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Single-Table DynamoDB Design&lt;/strong&gt;: Uses composite keys (playerId + sessionId) for efficient queries while supporting multiple concurrent sessions per player.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Streaming Responses&lt;/strong&gt;: The session proxy processes Bedrock's streaming responses to provide real-time feedback, crucial for maintaining immersion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modular Lambda Architecture&lt;/strong&gt;: Separates concerns between session management and game actions, enabling independent scaling and easier maintenance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pay-Per-Request Billing&lt;/strong&gt;: All services use on-demand pricing, perfect for unpredictable gaming workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-Prepare Agents&lt;/strong&gt;: Bedrock agents automatically prepare when deployed, reducing cold-start latency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lambda Function Deep Dive
&lt;/h2&gt;

&lt;p&gt;Let's examine the Lambda functions that power the game mechanics and session management:&lt;/p&gt;

&lt;h3&gt;
  
  
  Session Proxy Lambda: The Gateway
&lt;/h3&gt;

&lt;p&gt;The session proxy acts as the bridge between API Gateway and Bedrock Agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyHandlerV2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BedrockAgentRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InvokeAgentCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-bedrock-agent-runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bedrockAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BedrockAgentRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AGENT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AGENT_ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ALIAS_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALIAS_ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyHandlerV2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST,OPTIONS,GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type, Accept&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Request Processing
&lt;/h4&gt;

&lt;p&gt;The function validates required parameters and enriches the input with context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputText&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;playerId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing required parameters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sessionId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;playerId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playerId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputText&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inputText&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contextualInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Player ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Session ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Streaming Response Handling
&lt;/h4&gt;

&lt;p&gt;The core streaming logic processes Bedrock's chunked responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvokeAgentCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AGENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;agentAliasId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ALIAS_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contextualInput&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bedrockAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunkEvent&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunkEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;decodedResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CORS handling&lt;/strong&gt; for web client compatibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input validation&lt;/strong&gt; with detailed error responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context enrichment&lt;/strong&gt; to provide player/session info to the agent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming aggregation&lt;/strong&gt; for complete response assembly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt; with structured error responses&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Game Actions Lambda: The Persistence Layer
&lt;/h3&gt;

&lt;p&gt;The game actions function implements the core CRUD operations for character and world state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GetCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UpdateCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/lib-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;({}));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TABLE&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AgentParam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;key&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AgentEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;actionGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AgentParam&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Parameter Processing
&lt;/h4&gt;

&lt;p&gt;The function normalizes Bedrock Agent's parameter format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toParamMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AgentParam&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;messageVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;actionGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GameActions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;functionResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Character Management
&lt;/h4&gt;

&lt;p&gt;The get_character function retrieves player data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ddb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;playerCharacter&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;playerCharacter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;character&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;world&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;world&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The save_character function persists character updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;save_character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ddb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UpdateCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;UpdateExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SET playerCharacter = :c, lastUpdated = :t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ExpressionAttributeValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;saved&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  World State Logging
&lt;/h4&gt;

&lt;p&gt;The append_log function maintains narrative history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;append_log&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ddb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UpdateCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;UpdateExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SET #world = if_not_exists(#world, :emptyWorld), #world.#logs = list_append(if_not_exists(#world.#logs, :empty), :e), lastUpdated = :t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ExpressionAttributeNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#logs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;ExpressionAttributeValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:empty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:emptyWorld&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logged&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexible parameter handling&lt;/strong&gt; for Bedrock Agent compatibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic updates&lt;/strong&gt; using DynamoDB UpdateExpressions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional operations&lt;/strong&gt; with if_not_exists for safe initialization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List operations&lt;/strong&gt; for appending log entries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured responses&lt;/strong&gt; matching Bedrock Agent expectations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt; with graceful fallbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lambda Architecture Benefits
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Separation of Concerns&lt;/strong&gt;: The session proxy handles HTTP/streaming while game actions focus purely on data operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Independent Scaling&lt;/strong&gt;: Each function scales based on its specific workload patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error Isolation&lt;/strong&gt;: Failures in one function don't affect the other's operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testability&lt;/strong&gt;: Pure functions with clear inputs/outputs enable comprehensive testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost Efficiency&lt;/strong&gt;: Functions only run when needed, with sub-second billing granularity.&lt;/p&gt;

&lt;p&gt;This modular approach demonstrates how serverless functions can create clean, maintainable architectures for complex AI applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Challenges &amp;amp; Solutions
&lt;/h2&gt;

&lt;p&gt;Building this AI DM wasn't without its hurdles. Here are the key challenges encountered and how they were resolved:&lt;/p&gt;

&lt;h3&gt;
  
  
  Bedrock Model Access Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Initial deployment failed with "AccessDeniedException" when invoking the foundation model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: Bedrock model access isn't automatically enabled for all AWS accounts. Each foundation model requires an explicit access request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to AWS Bedrock Console → Model Access&lt;/li&gt;
&lt;li&gt;Request access to Claude 3.5 Sonnet (or your chosen model)&lt;/li&gt;
&lt;li&gt;Wait for approval (usually instant for standard models)&lt;/li&gt;
&lt;li&gt;Update CDK configuration with the correct model ID:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fmId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us.anthropic.claude-3-7-sonnet-20250219-v1:0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Verify exact model ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Always verify model availability in your target region before deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  CORS Configuration Nightmare
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Web client requests failing with CORS errors despite API Gateway CORS configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: CORS headers needed in both API Gateway AND Lambda responses for proper browser compatibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Implement CORS at multiple layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// API Gateway CORS (CDK)&lt;/span&gt;
&lt;span class="nx"&gt;corsPreflight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;allowOrigins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;allowMethods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;apigwv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CorsHttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apigwv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CorsHttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPTIONS&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;allowHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Lambda CORS headers (every response)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST,OPTIONS,GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type, Accept&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Handle OPTIONS preflight&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OPTIONS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: CORS requires configuration at both the gateway and function levels for web clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lambda Timeout Mysteries
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Bedrock Agent calls timing out with the default 3-second Lambda timeout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: AI model inference can take 10-30 seconds, especially for complex reasoning tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Increase Lambda timeout and add proper error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessionProxyFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambdaNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NodejsFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SessionProxyFn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Increased from default 3s&lt;/span&gt;
  &lt;span class="c1"&gt;// ... other config&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: AI workloads require generous timeout configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bedrock Agent Parameter Confusion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Game actions Lambda receiving inconsistent parameter formats from Bedrock Agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: Bedrock Agent can send parameters with either &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;key&lt;/code&gt; properties depending on the invocation context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Flexible parameter parsing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toParamMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AgentParam&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Always handle parameter format variations in Bedrock Agent integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  DynamoDB Conditional Update Failures
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: append_log function failing when trying to append to non-existent lists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: DynamoDB list_append requires the list to exist, but new sessions don't have world.logs initialized.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Use conditional expressions with if_not_exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;UpdateExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SET #world = if_not_exists(#world, :emptyWorld), #world.#logs = list_append(if_not_exists(#world.#logs, :empty), :e)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: DynamoDB conditional operations are essential for handling uninitialized data structures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Preparation Race Conditions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Bedrock Agent not ready immediately after CDK deployment, causing initial invocation failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: Agent preparation is asynchronous, even with &lt;code&gt;autoPrepare: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Add retry logic and use agent aliases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnAgentAlias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DmAgentAlias&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrAgentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;agentAliasName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Use alias ID instead of agent ID for invocations&lt;/span&gt;
&lt;span class="nl"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;AGENT_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrAgentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ALIAS_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrAgentAliasId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// More stable than direct agent calls&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Always use agent aliases for production workloads to ensure stability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Streaming Response Edge Cases
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Occasional empty responses or incomplete streaming from Bedrock Agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: Network issues or agent processing errors not properly handled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Add comprehensive error handling and fallbacks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BedRock Agent completion is undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunkEvent&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunkEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;decodedResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No response from agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Fallback for empty responses&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Always validate streaming responses and provide fallbacks for edge cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regional Service Availability
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Bedrock services are not available in all AWS regions, causing deployment failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: Bedrock has limited regional availability compared to core AWS services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Hardcode region or validate availability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bedrockAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BedrockAgentRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Bedrock-supported region&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Check service regional availability before choosing deployment regions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging Best Practices
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;CloudWatch Logs&lt;/strong&gt;: Essential for debugging Lambda and Bedrock Agent interactions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invoking agent with:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AGENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputText&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Agent response received:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasCompletion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Structured Error Responses&lt;/strong&gt;: Help identify issues quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Agent invocation error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AGENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;aliasId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ALIAS_ID&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Agent invocation failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UnknownError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Testing Strategy&lt;/strong&gt;: Start with simple inputs before complex game scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Test basic agent invocation with "Hello"&lt;/li&gt;
&lt;li&gt;Verify parameter passing with simple character data&lt;/li&gt;
&lt;li&gt;Test streaming with longer responses&lt;/li&gt;
&lt;li&gt;Validate error handling with invalid inputs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These challenges highlight the importance of thorough testing, proper error handling, and understanding the nuances of AI service integrations in serverless architectures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps: Migration to Amazon Bedrock Agent Core
&lt;/h2&gt;

&lt;p&gt;While the current Bedrock Agents implementation serves as a decent POC, &lt;strong&gt;Amazon Bedrock Agent Core&lt;/strong&gt; offers a more sophisticated framework for building production-grade AI agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Migrate to Bedrock Agent Core?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Control Flow&lt;/strong&gt;: Agent Core provides fine-grained control over reasoning chains, tool orchestration, and decision-making processes compared to standard Bedrock Agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Memory Management&lt;/strong&gt;: Built-in support for long-term memory, context compression, and intelligent information retrieval across extended gaming sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Agent Orchestration&lt;/strong&gt;: Native support for multiple specialized agents (combat manager, story narrator, rule enforcer) working together seamlessly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Optimization&lt;/strong&gt;: Reduced latency through optimized model routing, caching strategies, and parallel processing capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Features&lt;/strong&gt;: Enhanced monitoring, audit trails, and compliance features essential for production deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building this Serverless Dungeon Master Agent began as a “what if” idea and evolved into a working and somewhat scalable prototype. By combining Amazon Bedrock with serverless building blocks, I demonstrated how to create an AI-powered DM that remembers, adapts, and entertains—while remaining cost-effective.&lt;/p&gt;

&lt;p&gt;Whether you use it as a foundation for your own gaming experiments or as inspiration for other AI-driven applications, the takeaway is clear: serverless architectures make it possible to turn imaginative ideas into practical, low-maintenance solutions.&lt;/p&gt;

&lt;p&gt;The future of gaming, and beyond, might just be serverless.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>ai</category>
      <category>aws</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How To Connect a Squarespace Domain to a Website Hosted on GitHub Pages</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Sun, 18 May 2025 23:58:57 +0000</pubDate>
      <link>https://forem.com/drjoanneskiles/how-to-connect-a-squarespace-domain-to-a-website-hosted-on-github-pages-47i8</link>
      <guid>https://forem.com/drjoanneskiles/how-to-connect-a-squarespace-domain-to-a-website-hosted-on-github-pages-47i8</guid>
      <description>&lt;p&gt;If you’ve built a portfolio or project site hosted on GitHub Pages and want to use a custom domain you purchased through Squarespace, you're in luck; this is absolutely doable and takes just a few steps.&lt;/p&gt;

&lt;p&gt;This post will walk you through how to point your Squarespace-purchased domain to your GitHub Pages site, whether you're hosting on a user site (&lt;code&gt;yourusername.github.io&lt;/code&gt;) or a project site (&lt;code&gt;yourusername.github.io/your-project&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, you should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A working site hosted on &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A custom domain purchased via Squarespace (not a Squarespace-hosted website)&lt;/li&gt;
&lt;li&gt;Access to GitHub and Squarespace admin panels&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Determine Your GitHub Pages URL
&lt;/h2&gt;

&lt;p&gt;You'll need your GitHub Pages URL, which will look like one of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For user or organization pages:&lt;br&gt;
&lt;code&gt;https://yourusername.github.io/&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For project pages:&lt;br&gt;
&lt;code&gt;https://yourusername.github.io/project-name&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure your GitHub Pages site is already published and &lt;u&gt;publicly&lt;/u&gt; viewable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Add a CNAME File
&lt;/h2&gt;

&lt;p&gt;If you're using a custom domain, GitHub Pages needs to know about it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In your GitHub repository, create a file called CNAME (no extension).&lt;/li&gt;
&lt;li&gt;Inside it, write your custom domain (e.g. &lt;a href="http://www.yourcustomdomain.com" rel="noopener noreferrer"&gt;www.yourcustomdomain.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Commit and push the file to your repo's default branch (usually main or master)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This tells GitHub Pages to expect traffic from that domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Get Your GitHub Pages IP Addresses
&lt;/h2&gt;

&lt;p&gt;Squarespace doesn’t support CNAME flattening or ALIAS records, so you’ll need to use A records pointing to GitHub’s static IPs.&lt;/p&gt;

&lt;p&gt;As of this writing, GitHub Pages uses the following IPs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub recommends using all four A records for redundancy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Update DNS Settings in Squarespace
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Log in to Squarespace.&lt;/li&gt;
&lt;li&gt;Go to Domains and select the domain you purchased.&lt;/li&gt;
&lt;li&gt;Click DNS Settings.&lt;/li&gt;
&lt;li&gt;Add or update the following DNS records:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Host&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;TTL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;@&lt;/td&gt;
&lt;td&gt;185.199.108.153&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;@&lt;/td&gt;
&lt;td&gt;185.199.109.153&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;@&lt;/td&gt;
&lt;td&gt;185.199.110.153&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;@&lt;/td&gt;
&lt;td&gt;185.199.111.153&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;www&lt;/td&gt;
&lt;td&gt;yourusername.github.io&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Point the root domain (yourcustomdomain.com) to GitHub Pages&lt;/li&gt;
&lt;li&gt;Point &lt;a href="http://www.yourcustomdomain.com" rel="noopener noreferrer"&gt;www.yourcustomdomain.com&lt;/a&gt; to GitHub via CNAME&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚠️ Note: Squarespace might already have default records— delete any existing A or CNAME records that conflict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Wait for DNS Propagation
&lt;/h2&gt;

&lt;p&gt;DNS changes can take anywhere from a few minutes to 24–48 hours to fully propagate. You can use tools like &lt;a href="//whatsmydns.net"&gt;whatsmydns.net&lt;/a&gt; to track progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Verify the Setup
&lt;/h2&gt;

&lt;p&gt;After DNS has propagated, go to your custom domain in a browser. You should see your GitHub Pages site served through your domain!&lt;/p&gt;

&lt;p&gt;Troubleshooting Tips&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting a 404? Make sure your GitHub Pages is publishing from the correct branch.&lt;/li&gt;
&lt;li&gt;Still loading Squarespace? Clear DNS cache and check that the default Squarespace records were removed.&lt;/li&gt;
&lt;li&gt;Mixed content warnings? Make sure your GitHub site supports HTTPS (GitHub Pages offers this for free).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Connecting a Squarespace domain to your GitHub Pages site gives you the best of both worlds: a professional domain and free static hosting. With a few DNS tweaks and a CNAME file, your project will be live and looking sharp.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>github</category>
    </item>
    <item>
      <title>Getting Started with AWS: Tips That Actually Helped Me</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Wed, 09 Apr 2025 19:02:35 +0000</pubDate>
      <link>https://forem.com/aws-builders/getting-started-with-aws-tips-that-actually-helped-me-35b9</link>
      <guid>https://forem.com/aws-builders/getting-started-with-aws-tips-that-actually-helped-me-35b9</guid>
      <description>&lt;p&gt;When I first started learning AWS, I remember staring at the endless list of services and wondering, &lt;em&gt;Where do I even begin?&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;It’s easy to feel overwhelmed with all the options—EC2, S3, Lambda, CloudFormation, IAM, Route 53, etc. The good news? You don’t have to master everything at once. AWS is powerful, but the learning journey doesn’t need to be intimidating.&lt;/p&gt;

&lt;p&gt;Here are some practical AWS learning tips that helped me—and I hope they’ll help you, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start with the Basics
&lt;/h2&gt;

&lt;p&gt;Before diving into every shiny new service, it’s important to build a solid foundation. Understanding the core AWS services will give you the context and confidence to explore more advanced tools later on. These core services include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EC2&lt;/strong&gt; (Elastic Compute Cloud): Think of this as your virtual machine in the cloud. If you understand how a typical server works, you can grasp EC2 fairly quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; (Simple Storage Service): This is AWS's highly scalable object storage service. It’s great for storing static assets, backups, and even hosting static websites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; (Identity and Access Management): One of the most crucial services to understand early. IAM helps you manage permissions securely across your AWS account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt;: AWS's serverless compute service that lets you run code without provisioning or managing servers. Great for automating small tasks or building microservices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Taking the time to truly understand how these services work and how they connect to one another can save you hours (if not days) down the road.&lt;/p&gt;

&lt;p&gt;Helpful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/free" rel="noopener noreferrer"&gt;AWS Free Tier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html" rel="noopener noreferrer"&gt;What is Amazon EC2?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html" rel="noopener noreferrer"&gt;Getting started with S3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Get Hands-On as Early as Possible
&lt;/h2&gt;

&lt;p&gt;Watching tutorials and reading documentation are helpful, but nothing beats actually using the services. AWS offers a very interactive experience through its Console, and getting hands-on will reinforce your learning.&lt;/p&gt;

&lt;p&gt;A good beginner project? Deploying a simple Lambda function. Here’s an example in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# A simple AWS Lambda function in Python
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello from Lambda!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function can be tested directly in the AWS Console. It might seem simple, but running your own code in the cloud is empowering and builds your confidence to try more complex things.&lt;/p&gt;

&lt;p&gt;Also try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting a static website using S3&lt;/li&gt;
&lt;li&gt;Creating an EC2 instance and connecting to it via SSH&lt;/li&gt;
&lt;li&gt;Writing a basic IAM policy&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Take Advantage of the Free Tier (Just Don’t Forget to Clean Up)
&lt;/h2&gt;

&lt;p&gt;The AWS Free Tier is your best friend as a beginner. It gives you access to many services for free for the first 12 months (and some forever).&lt;/p&gt;

&lt;p&gt;You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run EC2 instances (750 hours/month for t2.micro or t3.micro)&lt;/li&gt;
&lt;li&gt;Store 5GB in S3&lt;/li&gt;
&lt;li&gt;Execute 1M Lambda requests&lt;/li&gt;
&lt;li&gt;Store 25 GB on DynamoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BUT… always remember to shut things down when you're done. AWS doesn’t always send big alerts for charges. Use the &lt;a href="https://console.aws.amazon.com/billing/home" rel="noopener noreferrer"&gt;Billing Dashboard&lt;/a&gt; and set up alerts through the &lt;a href="https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-create.html" rel="noopener noreferrer"&gt;AWS Budgets&lt;/a&gt; feature.&lt;/p&gt;

&lt;p&gt;Trust me, the first surprise bill is not a fun milestone.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Certifications Are Great—But Don’t Let Them Be the Only Goal
&lt;/h2&gt;

&lt;p&gt;AWS certifications like &lt;a href="https://aws.amazon.com/certification/certified-solutions-architect-associate/" rel="noopener noreferrer"&gt;Solutions Architect Associate&lt;/a&gt; are valuable, especially for structuring your learning.&lt;/p&gt;

&lt;p&gt;But don't fall into the trap of memorizing for exams without understanding the "why" behind each service. Real-world projects give context to that knowledge and help it stick. Combine cert prep with actual building.&lt;/p&gt;

&lt;p&gt;Try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Going through AWS Skill Builder's free training&lt;/li&gt;
&lt;li&gt;Pairing Udemy courses or A Cloud Guru content with personal projects&lt;/li&gt;
&lt;li&gt;Taking notes &lt;em&gt;in your own words&lt;/em&gt; after every study session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended &lt;em&gt;free&lt;/em&gt; study resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://explore.skillbuilder.aws/learn" rel="noopener noreferrer"&gt;AWS Skill Builder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/training/learning-paths/" rel="noopener noreferrer"&gt;AWS Learning Paths by Role&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/playlist?list=PLWKjhJtqVAbkFiqHnNaxpOPhh9tSWMXIF" rel="noopener noreferrer"&gt;FreeCodeCamp AWS Playlist&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. You Don’t Have to Learn Everything
&lt;/h2&gt;

&lt;p&gt;There are over 200 AWS services. Nobody expects you to know them all.&lt;/p&gt;

&lt;p&gt;Instead, focus on what aligns with your goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web developer? Learn S3, CloudFront, API Gateway, and Lambda.&lt;/li&gt;
&lt;li&gt;Backend engineer? Dive into DynamoDB, RDS, and ECS.&lt;/li&gt;
&lt;li&gt;DevOps or Infra-focused? Master CloudFormation, IAM, and CodePipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use AWS’s &lt;a href="https://aws.amazon.com/architecture/" rel="noopener noreferrer"&gt;Architecture Center&lt;/a&gt; to browse common use cases and decide where to focus. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let your learning be project-driven, not list-driven.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Find Community
&lt;/h2&gt;

&lt;p&gt;Learning in isolation is tough. Thankfully, the AWS community is one of the most active and supportive in tech. Engage with others to stay motivated and solve problems faster.&lt;/p&gt;

&lt;p&gt;Places to connect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/developer/community/usergroups/" rel="noopener noreferrer"&gt;AWS User Groups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/aws/" rel="noopener noreferrer"&gt;r/aws on Reddit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;LinkedIn hashtags like #AWSCommunity and #BuildOnAWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ask questions, share your projects, and don’t hesitate to say, "I don’t get this yet."&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;AWS can feel like a lot—because it is. But if you take it one step at a time, keep building, and stay connected to a supportive community, you’ll get there.&lt;/p&gt;

&lt;p&gt;Whether you're just starting or picking things back up, remember that cloud skills are built &lt;em&gt;one small project at a time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What AWS tip helped &lt;em&gt;you&lt;/em&gt; the most? Share it—I'd love to learn from your journey too.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>careerdevelopment</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why Everyone* Should Learn About Cloud Computing and AI</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Fri, 28 Mar 2025 14:48:50 +0000</pubDate>
      <link>https://forem.com/aws-builders/why-everyone-should-learn-about-cloud-computing-and-ai-37ch</link>
      <guid>https://forem.com/aws-builders/why-everyone-should-learn-about-cloud-computing-and-ai-37ch</guid>
      <description>&lt;p&gt;Yesterday, I hosted an Orlando AWS User Group meetup at UCF. We had a great turnout, but as always, I bought way too much pizza (I blame my upbringing, which is that everyone must eat until they are completely stuffed). So, as we were wrapping up, I started handing out slices to anyone nearby.&lt;/p&gt;

&lt;p&gt;One of those lucky recipients was a student majoring in film. As we chatted, I could tell they were curious about the tech buzz in the room. But the unspoken question lingered:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why should someone in film care about AWS or cloud computing at all?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here’s my answer, not just for that student but for everyone reading this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need to learn about cloud computing.&lt;br&gt;
And you &lt;em&gt;really&lt;/em&gt; need to learn about AI.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future Isn't Coming—It's Already Here
&lt;/h2&gt;

&lt;p&gt;We tend to associate cloud computing with software engineers, system architects, and DevOps folks. But the truth is, the cloud is powering everything—from the apps on your phone to the CGI rendering engines in your favorite blockbuster films.&lt;/p&gt;

&lt;p&gt;AI? It's not just for techies in lab coats anymore. It’s generating storyboards, editing footage, optimizing marketing campaigns, and even editing scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Matters—No Matter Your Major
&lt;/h2&gt;

&lt;p&gt;Whether you’re in &lt;strong&gt;film&lt;/strong&gt;, &lt;strong&gt;marketing&lt;/strong&gt;, &lt;strong&gt;healthcare&lt;/strong&gt;, &lt;strong&gt;agriculture&lt;/strong&gt;, &lt;strong&gt;education&lt;/strong&gt;, or any other industry—cloud and AI are the invisible engines behind today’s innovation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In film&lt;/strong&gt;, cloud services like AWS render farms help produce high-quality visuals faster and cheaper. AI tools are revolutionizing editing workflows, enhancing sound design, and creating personalized viewer experiences.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In education&lt;/strong&gt;, AI is shaping personalized learning paths. Cloud computing supports scalable platforms for remote learning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In healthcare&lt;/strong&gt;, AI is accelerating diagnostics, predicting disease outbreaks, and improving patient outcomes. Cloud platforms store and process massive amounts of medical data securely—enabling faster research and collaboration across the globe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In marketing&lt;/strong&gt;, machine learning predicts trends, automates campaigns, and analyzes customer behavior in real time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In farming and agriculture&lt;/strong&gt;, AI is driving precision farming—analyzing soil data, monitoring crop health with drones, and optimizing irrigation. The cloud powers real-time dashboards and data pipelines that help farmers make smarter decisions and improve yields sustainably.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In business&lt;/strong&gt;, data-driven decisions are no longer optional. They're critical. And where does all that data live? You guessed it—the cloud.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You Don’t Have to Be a Coder to Be Cloud-Literate
&lt;/h2&gt;

&lt;p&gt;Knowing the basics of cloud computing doesn't mean you have to spin up an EC2 instance tomorrow (though that would be cool). It means understanding how modern tools work so you can make informed decisions, collaborate better with technical teams, and stay competitive in your field.&lt;/p&gt;

&lt;p&gt;AI literacy is becoming just as essential. With generative tools, prompt engineering, and low-code/no-code platforms, you don’t have to be an ML expert to harness the power of artificial intelligence—but you do need to understand what’s happening under the hood at a high level.&lt;/p&gt;

&lt;p&gt;And yes, you don’t have to be a coder, but please don’t just start "vibe" coding. I’ve seen it too many times: folks diving into tools and services without understanding the underlying principles. You will have a bad time if you don’t understand the fundamentals. Take the time to learn the “why” behind the tech. It will save you countless hours and headaches later.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Because let’s be real—just clicking around in the AWS console without a plan is like getting behind the wheel without learning how to drive—sooner or later, something’s going to crash.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  “But I’m a Hermit on a Mountain-Top…”
&lt;/h2&gt;

&lt;p&gt;Ok, fine—you &lt;em&gt;probably&lt;/em&gt; don’t need to learn about cloud computing if you’re truly off-grid, living that solar-powered, goat-herding, no-internet life (i.e. every Senior Engineer's dream).&lt;/p&gt;

&lt;p&gt;But… I have a question.&lt;br&gt;
&lt;strong&gt;How are you reading this post?&lt;/strong&gt; 🤔&lt;/p&gt;

&lt;p&gt;If you’re connected enough to be scrolling dev.to, LinkedIn, or whatever platform you found a link to this post on, then you’re already participating in a cloud-powered world.&lt;/p&gt;

&lt;p&gt;It’s okay to be skeptical of change. It’s healthy, even. But if you don’t want to make yourself obsolete, you’ve got to be willing to learn. You don’t have to chase every shiny new tool, but being a Luddite to &lt;em&gt;all&lt;/em&gt; new tech won’t serve you either.&lt;/p&gt;

&lt;p&gt;Adaptation isn't just for survival—it’s for thriving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought: Curiosity Is Your Superpower
&lt;/h2&gt;

&lt;p&gt;That film student didn’t come to school that day expecting a tech pitch, but they walked away thinking a little differently. And that’s all it takes—a spark of curiosity.&lt;/p&gt;

&lt;p&gt;So, if you’re reading this and you think cloud computing and AI aren’t for you, I challenge you to reconsider. Start small. Watch a YouTube video. Try a beginner-friendly course. Come to a user group meetup (we’ll have pizza 🍕).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The future belongs to the curious—and the cloud-enabled.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudcomputing</category>
      <category>careerdevelopment</category>
      <category>career</category>
    </item>
    <item>
      <title>Multi-Region Data Replication with Amazon DynamoDB Global Tables</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Tue, 11 Mar 2025 14:20:47 +0000</pubDate>
      <link>https://forem.com/aws-builders/multi-region-data-replication-with-amazon-dynamodb-global-tables-3ej0</link>
      <guid>https://forem.com/aws-builders/multi-region-data-replication-with-amazon-dynamodb-global-tables-3ej0</guid>
      <description>&lt;p&gt;Ensuring data consistency and availability across multiple AWS regions is critical. &lt;strong&gt;Amazon DynamoDB Global Tables&lt;/strong&gt; provide a fully managed solution for multi-region data replication, enabling low-latency access, high availability, and disaster recovery. This post explores how to implement DynamoDB Global Tables for a resilient and scalable architecture, along with a real-world example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use DynamoDB Global Tables?
&lt;/h2&gt;

&lt;p&gt;Using DynamoDB Global Tables for multi-region data replication offers several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low-Latency Reads and Writes:&lt;/strong&gt; Users access the nearest region for faster responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Availability:&lt;/strong&gt; Data remains accessible even if one region experiences downtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Conflict Resolution:&lt;/strong&gt; DynamoDB ensures consistency across regions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; Supports high-throughput applications with minimal operational overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Strategies for Multi-Region Data Replication
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Setting Up DynamoDB Global Tables&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;DynamoDB Global Tables automatically replicate data across selected AWS regions, providing a seamless experience for distributed applications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Steps to Create a DynamoDB Global Table:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create a table in one AWS region.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable global table replication&lt;/strong&gt; and select additional regions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB handles data replication automatically.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Example AWS CLI Command to Create a Global Table:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws dynamodb create-table &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--table-name&lt;/span&gt; UserProfiles &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attribute-definitions&lt;/span&gt; &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;UserID,AttributeType&lt;span class="o"&gt;=&lt;/span&gt;S &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-schema&lt;/span&gt; &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;UserID,KeyType&lt;span class="o"&gt;=&lt;/span&gt;HASH &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--billing-mode&lt;/span&gt; PAY_PER_REQUEST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1

aws dynamodb update-table &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--table-name&lt;/span&gt; UserProfiles &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--replica-updates&lt;/span&gt; &lt;span class="s1"&gt;'[{"Create": {"RegionName": "eu-west-1"}}]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;strong&gt;Handling Multi-Region Reads and Writes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Applications should always read from and write to the nearest DynamoDB replica for optimal performance. AWS SDKs support &lt;strong&gt;endpoint discovery&lt;/strong&gt; to dynamically route requests to the closest region.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example API Call in Python Using Boto3:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UserProfiles&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Write data
&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UserID&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;123&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Region&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;US&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# Read data
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UserID&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;123&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Item&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Ensuring Conflict Resolution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;DynamoDB Global Tables use &lt;strong&gt;last writer wins (LWW)&lt;/strong&gt; conflict resolution by default. Applications should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement &lt;strong&gt;timestamps&lt;/strong&gt; to track data changes.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;event-driven mechanisms&lt;/strong&gt; (e.g., AWS Lambda + DynamoDB Streams) to resolve conflicts if needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Example: A Global E-Commerce Platform
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;e-commerce company&lt;/strong&gt; with customers across North America and Europe requires a &lt;strong&gt;distributed user profile database&lt;/strong&gt; to enhance customer experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Breakdown:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB Global Tables&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Stores user profiles replicated across &lt;code&gt;us-east-1&lt;/code&gt; and &lt;code&gt;eu-west-1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ensures user data is available with minimal latency.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway &amp;amp; Route 53&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Routes API requests to the nearest region.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFront Caching&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Improves response times for frequently accessed profile data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CloudWatch &amp;amp; X-Ray&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Monitors performance and detects anomalies.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Benefits for Users:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Faster profile updates and retrievals based on their location.&lt;/li&gt;
&lt;li&gt;Seamless experience even if a region goes offline.&lt;/li&gt;
&lt;li&gt;Real-time data consistency across multiple geographies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using &lt;strong&gt;Amazon DynamoDB Global Tables&lt;/strong&gt; for multi-region data replication ensures high availability, scalability, and low-latency access for global applications. By implementing &lt;strong&gt;region-aware API calls&lt;/strong&gt;, &lt;strong&gt;conflict resolution strategies&lt;/strong&gt;, and &lt;strong&gt;automatic failover mechanisms&lt;/strong&gt;, businesses can provide a seamless experience for users worldwide.&lt;/p&gt;

&lt;p&gt;In the next post, we’ll explore &lt;strong&gt;multi-region event-driven architectures using Amazon SNS and SQS&lt;/strong&gt; to distribute real-time messages across regions.&lt;/p&gt;

&lt;p&gt;Have you used DynamoDB Global Tables in your projects? Share your thoughts and questions in the comments!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>dynamodb</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Global API Management with API Gateway and Route 53</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Tue, 18 Feb 2025 16:54:08 +0000</pubDate>
      <link>https://forem.com/aws-builders/global-api-management-with-api-gateway-and-route-53-2mfb</link>
      <guid>https://forem.com/aws-builders/global-api-management-with-api-gateway-and-route-53-2mfb</guid>
      <description>&lt;p&gt;When you have a &lt;em&gt;global&lt;/em&gt; application, providing a seamless, low-latency experience to users worldwide is crucial. By deploying regional AWS API Gateway endpoints and using Amazon Route 53 for latency-based routing, you can ensure fast response times, high availability, and robust failover mechanisms. This post explores how to implement global API management with AWS services and provides a real-world example. If this looks familiar, you're right, some of this we touched on in the previous post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Multi-Region API Gateways?
&lt;/h2&gt;

&lt;p&gt;Deploying API Gateway in multiple AWS regions and managing routing with Route 53 offers key benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low Latency:&lt;/strong&gt; Directs users to the nearest API endpoint for faster response times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Availability:&lt;/strong&gt; Ensures service continuity even if one region experiences downtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Performance Scaling:&lt;/strong&gt; Distributes API traffic efficiently across multiple regions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance &amp;amp; Data Sovereignty:&lt;/strong&gt; Supports data sovereignty requirements by processing requests in specific regions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Strategies for Global API Management
&lt;/h2&gt;

&lt;p&gt;To effectively manage global APIs, follow these best practices:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Deploy Regional API Gateway Endpoints&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of relying on a single API Gateway instance, deploy regional API Gateway endpoints in multiple AWS regions. This allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traffic to be processed closer to the user.&lt;/li&gt;
&lt;li&gt;More control over caching and data processing in each region.&lt;/li&gt;
&lt;li&gt;Better fault tolerance by avoiding a single point of failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example AWS CLI Commands for API Gateway Deployment:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy an API Gateway in US-East-1&lt;/span&gt;
aws apigateway create-rest-api &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"ExchangeAPI-US"&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1

&lt;span class="c"&gt;# Deploy an API Gateway in EU-West-1&lt;/span&gt;
aws apigateway create-rest-api &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"ExchangeAPI-EU"&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; eu-west-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;strong&gt;Use Amazon Route 53 Latency-Based Routing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Amazon Route 53 can direct users to the API Gateway endpoint with the lowest latency. This is achieved using &lt;strong&gt;latency-based routing policies&lt;/strong&gt;, which evaluate where a request originates and send it to the closest AWS region.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example Route 53 Configuration:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;US-based users&lt;/strong&gt; are routed to the API Gateway in &lt;code&gt;us-east-1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;European users&lt;/strong&gt; are routed to the API Gateway in &lt;code&gt;eu-west-1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If one region is unavailable, failover routing directs traffic to the next best available region.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example AWS CLI Command for Route 53 Record Setup:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws route53 change-resource-record-sets &lt;span class="nt"&gt;--hosted-zone-id&lt;/span&gt; Z123456ABCDEFG &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--change-batch&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Changes": [
      {
        "Action": "UPSERT",
        "ResourceRecordSet": {
          "Name": "api.example.com",
          "Type": "A",
          "SetIdentifier": "US Region",
          "Region": "us-east-1",
          "TTL": 60,
          "ResourceRecords": [{"Value": "192.0.2.1"}]
        }
      },
      {
        "Action": "UPSERT",
        "ResourceRecordSet": {
          "Name": "api.example.com",
          "Type": "A",
          "SetIdentifier": "EU Region",
          "Region": "eu-west-1",
          "TTL": 60,
          "ResourceRecords": [{"Value": "192.0.2.2"}]
        }
      }
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Implement Failover Routing for High Availability&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If one region becomes unavailable, Route 53 failover routing ensures API traffic is redirected to an operational region. This setup improves resilience by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Using health checks&lt;/strong&gt; to monitor API availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatically switching traffic&lt;/strong&gt; in case of failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example Route 53 Health Check Configuration:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws route53 create-health-check &lt;span class="nt"&gt;--caller-reference&lt;/span&gt; &lt;span class="s2"&gt;"api-healthcheck"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--health-check-config&lt;/span&gt; &lt;span class="s1"&gt;'{
    "IPAddress": "192.0.2.1",
    "Port": 443,
    "Type": "HTTPS",
    "ResourcePath": "/status",
    "RequestInterval": 30,
    "FailureThreshold": 3
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Example: Fintech Currency Exchange API
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;fintech company&lt;/strong&gt; offering real-time &lt;strong&gt;currency exchange services&lt;/strong&gt; needs a globally distributed API to provide users with the latest exchange rates instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Breakdown:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Deploys &lt;strong&gt;regional API Gateway endpoints&lt;/strong&gt; in &lt;code&gt;us-east-1&lt;/code&gt; and &lt;code&gt;eu-west-1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Handles user requests efficiently based on their location.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route 53 (Latency-Based Routing)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Directs requests to the closest API endpoint.&lt;/li&gt;
&lt;li&gt;Uses failover routing in case of outages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Lambda (Currency Rate Processing)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Fetches live exchange rates from a data provider.&lt;/li&gt;
&lt;li&gt;Processes calculations before returning results.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon DynamoDB Global Tables&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Stores exchange rate history across regions.&lt;/li&gt;
&lt;li&gt;Provides data consistency with real-time replication.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch &amp;amp; X-Ray&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Monitors API performance and logs request traces.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Benefits for Users:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fast response times&lt;/strong&gt; due to latency-based routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal service disruptions&lt;/strong&gt; with automatic failover.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accurate exchange rates&lt;/strong&gt; retrieved in real-time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Implementing global API management with API Gateway and Route 53 ensures low-latency, highly available, and scalable API services. By leveraging latency-based routing, failover mechanisms, and regional API deployments, businesses can improve user experience and reliability.&lt;/p&gt;

&lt;p&gt;In the next post, we’ll explore &lt;strong&gt;using DynamoDB Global Tables for multi-region data replication&lt;/strong&gt; to ensure real-time consistency across distributed applications.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>webdev</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>Deploying Serverless Functions Across Regions with AWS Lambda</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Wed, 12 Feb 2025 01:06:30 +0000</pubDate>
      <link>https://forem.com/aws-builders/deploying-serverless-functions-across-regions-with-aws-lambda-2khc</link>
      <guid>https://forem.com/aws-builders/deploying-serverless-functions-across-regions-with-aws-lambda-2khc</guid>
      <description>&lt;p&gt;Providing seamless user experiences across different regions is crucial in a globalized digital world. Deploying AWS Lambda functions in multiple regions ensures low latency, high availability, and better fault tolerance. This post will explore deploying serverless functions effectively across AWS regions, focusing on best practices, automation strategies, and a real-world example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Deploy AWS Lambda Functions Across Regions?
&lt;/h2&gt;

&lt;p&gt;Deploying serverless functions across multiple AWS regions offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Latency:&lt;/strong&gt; Users in different geographic locations experience faster response times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Availability:&lt;/strong&gt; Ensures business continuity in case of regional failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance &amp;amp; Data Sovereignty:&lt;/strong&gt; Some applications require region-specific processing due to legal and regulatory requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability &amp;amp; Redundancy:&lt;/strong&gt; Balances workloads and provides failover mechanisms in case of outages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Strategies for Multi-Region AWS Lambda Deployment
&lt;/h2&gt;

&lt;p&gt;To efficiently deploy AWS Lambda functions across multiple regions, follow these best practices:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Use Infrastructure as Code (IaC) with AWS CloudFormation or Terraform&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Managing multi-region deployments manually can be error-prone and inefficient. Using IaC tools like CloudFormation or Terraform allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define Lambda functions, API Gateway endpoints, IAM roles, and other resources in code.&lt;/li&gt;
&lt;li&gt;Maintain consistent deployments across regions.&lt;/li&gt;
&lt;li&gt;Automate rollbacks and version control.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example Terraform Code for Multi-Region Deployment
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_us"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"reservation-processor"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"index.handler"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nodejs18.x"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_exec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda.zip"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_eu"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eu&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"reservation-processor"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"index.handler"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nodejs18.x"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_exec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda.zip"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;strong&gt;Implement CI/CD Pipelines with AWS CodePipeline or GitHub Actions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Automating deployments ensures consistency and reduces manual errors. A CI/CD pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploys Lambda functions to multiple regions automatically.&lt;/li&gt;
&lt;li&gt;Allows rollbacks in case of failures.&lt;/li&gt;
&lt;li&gt;Ensures version control and controlled releases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example GitHub Actions Workflow for Multi-Region Deployment
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Multi-Region Lambda&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to US-East-1&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws lambda update-function-code --function-name reservation-processor --zip-file fileb://lambda.zip --region us-east-1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to EU-West-1&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws lambda update-function-code --function-name reservation-processor --zip-file fileb://lambda.zip --region eu-west-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Use AWS Lambda Versions and Aliases for Controlled Releases&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;AWS Lambda allows function versioning and aliasing for better deployment control. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain multiple versions of a function.&lt;/li&gt;
&lt;li&gt;Use aliases like &lt;code&gt;production&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, or &lt;code&gt;beta&lt;/code&gt; to route traffic gradually.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;blue-green deployments&lt;/strong&gt; to minimize downtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example AWS CLI Commands for Versioning and Aliases
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Publish a new version&lt;/span&gt;
aws lambda publish-version &lt;span class="nt"&gt;--function-name&lt;/span&gt; reservation-processor &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1

&lt;span class="c"&gt;# Create an alias pointing to the new version&lt;/span&gt;
aws lambda create-alias &lt;span class="nt"&gt;--function-name&lt;/span&gt; reservation-processor &lt;span class="nt"&gt;--name&lt;/span&gt; production &lt;span class="nt"&gt;--function-version&lt;/span&gt; 2 &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Case Study: Travel Booking Application
&lt;/h2&gt;

&lt;p&gt;In my last post, we discussed a global travel booking platform that processes flight and hotel reservations. To provide fast and reliable service, the company would need to deploy its &lt;strong&gt;reservation-processing Lambda function&lt;/strong&gt; in both North America (&lt;code&gt;us-east-1&lt;/code&gt;) and Europe (&lt;code&gt;eu-west-1&lt;/code&gt;).&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%2F9524k44exgt1693arl4t.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%2F9524k44exgt1693arl4t.png" alt="Travel Booking Application - Architecture Diagram" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Breakdown:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; routes requests to the closest region using &lt;strong&gt;latency-based routing&lt;/strong&gt; via Amazon Route 53.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda functions&lt;/strong&gt; in both regions handle reservation processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB Global Tables&lt;/strong&gt; ensure real-time data replication across regions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch and X-Ray&lt;/strong&gt; provide monitoring and tracing for performance insights.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits for Users:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A traveler booking a flight from New York gets routed to &lt;code&gt;us-east-1&lt;/code&gt;, experiencing low latency.&lt;/li&gt;
&lt;li&gt;A traveler booking from London gets routed to &lt;code&gt;eu-west-1&lt;/code&gt;, ensuring fast processing.&lt;/li&gt;
&lt;li&gt;In case &lt;code&gt;us-east-1&lt;/code&gt; goes down, requests automatically fail over to &lt;code&gt;eu-west-1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Deploying AWS Lambda functions across multiple regions enhances performance, availability, and compliance for global applications. By leveraging &lt;strong&gt;Infrastructure as Code&lt;/strong&gt;, &lt;strong&gt;CI/CD automation&lt;/strong&gt;, and &lt;strong&gt;version control&lt;/strong&gt;, you can ensure a scalable and resilient architecture.&lt;/p&gt;

&lt;p&gt;In the next post, we’ll explore &lt;strong&gt;global API management using API Gateway and Route 53&lt;/strong&gt; to efficiently direct user traffic across regions.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>tutorial</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Going Global with Serverless: Multi-Region Architectures on AWS</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Tue, 04 Feb 2025 20:48:31 +0000</pubDate>
      <link>https://forem.com/aws-builders/going-global-with-serverless-multi-region-architectures-on-aws-2k8n</link>
      <guid>https://forem.com/aws-builders/going-global-with-serverless-multi-region-architectures-on-aws-2k8n</guid>
      <description>&lt;p&gt;In today’s fast-paced digital landscape, ensuring low latency and high availability for users worldwide is crucial. Whether you're building a SaaS product, an e-commerce platform, or a real-time data processing system, a multi-region serverless architecture on AWS can help you achieve global reach with minimal operational overhead. In this post, we'll explore strategies for designing multi-region serverless applications on AWS, including best practices, challenges, and key services to leverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Go Multi-Region?
&lt;/h2&gt;

&lt;p&gt;A multi-region architecture enables your application to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduce Latency:&lt;/strong&gt; Serve users closer to their geographic location for faster response times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improve Availability:&lt;/strong&gt; Ensure business continuity by distributing workloads across multiple regions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhance Disaster Recovery:&lt;/strong&gt; Avoid single points of failure by leveraging AWS’s global infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meet Compliance Requirements:&lt;/strong&gt; Store and process data in specific regions to comply with regulations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Core AWS Services for Multi-Region Serverless
&lt;/h2&gt;

&lt;p&gt;AWS provides several services that facilitate building a scalable and resilient multi-region serverless architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon API Gateway &amp;amp; AWS Lambda:&lt;/strong&gt; Deploy API endpoints and backend functions across multiple regions to serve requests locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Route 53:&lt;/strong&gt; Implement latency-based routing to direct users to the nearest AWS region.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon DynamoDB Global Tables:&lt;/strong&gt; Replicate data automatically across multiple regions to provide low-latency reads and high availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon S3 with Cross-Region Replication (CRR):&lt;/strong&gt; Sync data across regions to ensure redundancy and minimize delays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Step Functions:&lt;/strong&gt; Orchestrate workflows across multiple regions with stateful execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon EventBridge &amp;amp; SNS/SQS:&lt;/strong&gt; Enable cross-region event-driven communication between serverless components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Designing a Multi-Region Serverless Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Deploying Serverless Functions Across Regions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;AWS Lambda allows you to deploy functions in multiple regions. To manage deployments effectively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Infrastructure as Code (IaC)&lt;/strong&gt; with AWS CloudFormation or Terraform.&lt;/li&gt;
&lt;li&gt;Implement CI/CD pipelines with AWS CodePipeline or GitHub Actions.&lt;/li&gt;
&lt;li&gt;Use AWS Lambda &lt;strong&gt;versions and aliases&lt;/strong&gt; for controlled releases across regions.&lt;/li&gt;
&lt;li&gt;Example: A travel booking application can deploy its reservation processing Lambda functions in both North America and Europe to ensure users experience low latency when making bookings.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Global API Management with API Gateway and Route 53&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deploy &lt;strong&gt;regional API Gateway endpoints&lt;/strong&gt; in multiple AWS regions.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Route 53 latency-based routing&lt;/strong&gt; to direct users to the closest API endpoint.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;failover routing&lt;/strong&gt; to ensure availability in case of regional failures.&lt;/li&gt;
&lt;li&gt;Example: A fintech company providing currency exchange services can deploy API Gateway endpoints in multiple regions to process requests quickly, ensuring users always get the latest exchange rates with minimal delays.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Data Synchronization with DynamoDB Global Tables&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Store data in a &lt;strong&gt;multi-region DynamoDB Global Table&lt;/strong&gt; to provide fast reads/writes globally.&lt;/li&gt;
&lt;li&gt;Ensure &lt;strong&gt;conflict resolution&lt;/strong&gt; strategies in case of simultaneous writes in different regions.&lt;/li&gt;
&lt;li&gt;Monitor replication lag using AWS CloudWatch.&lt;/li&gt;
&lt;li&gt;Example: A multiplayer gaming platform can use DynamoDB Global Tables to synchronize player stats and leaderboard updates across different continents, ensuring a seamless experience for global users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Cross-Region Event Processing with EventBridge and SQS&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Amazon EventBridge&lt;/strong&gt; to forward events between regions.&lt;/li&gt;
&lt;li&gt;Leverage &lt;strong&gt;Amazon SNS and SQS&lt;/strong&gt; for decoupled messaging between services.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;deduplication&lt;/strong&gt; and &lt;strong&gt;idempotency&lt;/strong&gt; in Lambda functions processing cross-region events.&lt;/li&gt;
&lt;li&gt;Example: An IoT-based smart home system can use EventBridge to process sensor data from devices in multiple regions, triggering automated responses like turning on lights or adjusting thermostats in real-time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Storage &amp;amp; Caching Optimization&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Utilize &lt;strong&gt;Amazon S3 with CRR&lt;/strong&gt; to synchronize static assets across regions.&lt;/li&gt;
&lt;li&gt;Deploy &lt;strong&gt;Amazon CloudFront&lt;/strong&gt; as a global CDN to cache and serve content efficiently.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;Amazon ElastiCache (Redis/Memcached)&lt;/strong&gt; for reducing database load.&lt;/li&gt;
&lt;li&gt;Example: A video streaming platform can use S3 with Cross-Region Replication to distribute video content to different regions and CloudFront to ensure optimal streaming performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges &amp;amp; Mitigation Strategies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Data Consistency&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Challenge: Replicating data across regions can introduce delays and conflicts.&lt;/li&gt;
&lt;li&gt;Solution: Design for eventual consistency and use DynamoDB conflict resolution mechanisms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Increased Complexity&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Challenge: Managing infrastructure across multiple regions requires additional configuration.&lt;/li&gt;
&lt;li&gt;Solution: Automate deployments using Infrastructure as Code (IaC) and CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Cost Management&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Challenge: Running services in multiple regions can increase operational costs.&lt;/li&gt;
&lt;li&gt;Solution: Optimize resources with AWS Cost Explorer and leverage on-demand pricing models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;A well-designed multi-region serverless architecture can significantly improve performance, availability, and resilience for global applications. By leveraging AWS services like Lambda, API Gateway, DynamoDB Global Tables, and EventBridge, you can build scalable solutions with minimal maintenance overhead. While challenges exist, implementing best practices for data replication, routing, and event-driven communication ensures a robust and cost-efficient architecture.&lt;/p&gt;

&lt;p&gt;In the upcoming posts, we'll dive deeper into each of the multi-region scenarios discussed in this article. I’ll provide hands-on examples, architecture diagrams, and implementation guides to help you build global-ready serverless applications.&lt;/p&gt;

&lt;p&gt;In the meantime, are you building a multi-region serverless application on AWS? Share your experiences and challenges in the comments below!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>15 Git Tips You Should Know</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Thu, 30 Jan 2025 18:35:03 +0000</pubDate>
      <link>https://forem.com/drjoanneskiles/15-git-tips-you-should-know-5c7j</link>
      <guid>https://forem.com/drjoanneskiles/15-git-tips-you-should-know-5c7j</guid>
      <description>&lt;p&gt;Git is an essential tool in every developer's workflow, but are you using it to its full potential? In this post, I want to discuss some of my favorite Git tips to improve productivity and make you look like a Git wizard.  &lt;/p&gt;

&lt;h4&gt;
  
  
  1. Undo the Last Commit Without Losing Changes
&lt;/h4&gt;

&lt;p&gt;Made a mistake in your last commit? You can undo it while keeping your changes staged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; HEAD~1  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This resets the commit but preserves your work so you can fix and recommit.  &lt;/p&gt;

&lt;h4&gt;
  
  
  2. Stash Selectively
&lt;/h4&gt;

&lt;p&gt;Sometimes you only want to stash specific files. Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash push &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Partial changes"&lt;/span&gt; &amp;lt;file1&amp;gt; &amp;lt;file2&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This stashes only the specified files, keeping the rest of your work intact.  &lt;/p&gt;

&lt;h4&gt;
  
  
  3. Find Out Who Changed What and When
&lt;/h4&gt;

&lt;p&gt;Curious about who introduced a bug? Use &lt;code&gt;git blame&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git blame &amp;lt;file&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It shows the commit hash, author, and date for each line of the file. &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%2F79q8qr9b1rga0bww1ege.jpg" 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%2F79q8qr9b1rga0bww1ege.jpg" alt="Spiderman Pointing Meme" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Be careful though you might find out you're the problem&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Clean Up Your Commit History with Interactive Rebase
&lt;/h4&gt;

&lt;p&gt;Want a cleaner commit history? Use interactive rebase to combine, edit, or reorder commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase &lt;span class="nt"&gt;-i&lt;/span&gt; HEAD~&amp;lt;number-of-commits&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to squash multiple commits into one or rewrite commit messages.  &lt;/p&gt;

&lt;h4&gt;
  
  
  5. Quickly Check Out Previous Branches
&lt;/h4&gt;

&lt;p&gt;Switching back and forth between branches? Use &lt;code&gt;git checkout -&lt;/code&gt; to quickly switch to your previous branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout -  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It saves time when you’re toggling between two branches.  &lt;/p&gt;

&lt;h4&gt;
  
  
  6. Diff Changes Before Staging
&lt;/h4&gt;

&lt;p&gt;Before staging changes, review what you’ve modified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want to see changes in a specific file?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff &amp;lt;file&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures you’re only committing what you intend to.  &lt;/p&gt;

&lt;h4&gt;
  
  
  7. Fix the Last Commit
&lt;/h4&gt;

&lt;p&gt;Forgot to add something to your last commit? Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--amend&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you modify the most recent commit, whether it’s adding files or updating the message.  &lt;/p&gt;

&lt;h4&gt;
  
  
  8. Find and Fix Typos in Commit Messages
&lt;/h4&gt;

&lt;p&gt;Typos in commit messages happen to the best of us. If it’s in the latest commit, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"New message"&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the typo is in an older commit, use &lt;code&gt;git rebase -i&lt;/code&gt; and update the message there.  &lt;/p&gt;

&lt;h4&gt;
  
  
  9. Use &lt;code&gt;git log&lt;/code&gt; Like a Pro
&lt;/h4&gt;

&lt;p&gt;Tired of scrolling through a wall of commits? Customize your &lt;code&gt;git log&lt;/code&gt; output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt; &lt;span class="nt"&gt;--decorate&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a visual representation of your branch history with concise commit messages.  &lt;/p&gt;

&lt;h4&gt;
  
  
  10. Restore Deleted Branches
&lt;/h4&gt;

&lt;p&gt;Accidentally deleted a branch? If it wasn’t pushed, you can recover it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog  
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; &amp;lt;branch-name&amp;gt; &amp;lt;commit-hash&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the commit hash from the &lt;code&gt;reflog&lt;/code&gt; output and recreate the branch.  &lt;/p&gt;

&lt;h4&gt;
  
  
  11. Resolve Merge Conflicts Faster
&lt;/h4&gt;

&lt;p&gt;Merge conflicts are inevitable, but you can make them easier to handle. Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git merge &lt;span class="nt"&gt;--abort&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This stops the merge and resets your branch to its pre-merge state if things get messy.  &lt;/p&gt;

&lt;p&gt;Alternatively, try a graphical merge tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git mergetool  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  12. Pull Without Merge Commits
&lt;/h4&gt;

&lt;p&gt;Avoid unnecessary merge commits when pulling changes by using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull &lt;span class="nt"&gt;--rebase&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps your history clean and linear.  &lt;/p&gt;

&lt;h4&gt;
  
  
  13. Track Down a Bug Using &lt;code&gt;git bisect&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Isolating bugs in large codebases can be daunting. Use &lt;code&gt;git bisect&lt;/code&gt; to find the commit where things went wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git bisect start  
git bisect bad  
git bisect good &amp;lt;commit&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git will automatically narrow down the commit range for you to test.  &lt;/p&gt;

&lt;h4&gt;
  
  
  14. Trigger CI/CD Pipelines Without Changing Code
&lt;/h4&gt;

&lt;p&gt;Ever needed to re-run your CI/CD pipeline without making any code changes? Instead of pushing random changes, you can use an &lt;strong&gt;empty commit&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--allow-empty&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Trigger pipeline rerun"&lt;/span&gt;  
git push  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a commit without modifying any files, letting you trigger the pipeline cleanly.  &lt;/p&gt;

&lt;h4&gt;
  
  
  15. Create Lightweight Aliases for Common Commands
&lt;/h4&gt;

&lt;p&gt;Speed up your workflow by creating Git aliases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.co checkout  
git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.br branch  
git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.st status  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can type &lt;code&gt;git co&lt;/code&gt;, &lt;code&gt;git br&lt;/code&gt;, or &lt;code&gt;git st&lt;/code&gt; instead of the full commands.  &lt;/p&gt;




&lt;p&gt;Git is a powerhouse of features—these tips are just the beginning. Incorporate them into your workflow and see the difference they make!  &lt;/p&gt;

&lt;p&gt;Do you have a favorite Git tip or trick? Drop it in the comments and share your wisdom!  &lt;/p&gt;

</description>
      <category>git</category>
      <category>softwaredevelopment</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Event-Driven Design: Choosing Between SNS, SQS, and EventBridge</title>
      <dc:creator>Joanne Skiles</dc:creator>
      <pubDate>Thu, 23 Jan 2025 02:02:38 +0000</pubDate>
      <link>https://forem.com/aws-builders/event-driven-design-choosing-between-sns-sqs-and-eventbridge-i82</link>
      <guid>https://forem.com/aws-builders/event-driven-design-choosing-between-sns-sqs-and-eventbridge-i82</guid>
      <description>&lt;p&gt;Event-driven design is a cornerstone of modern application architecture, especially in serverless systems where decoupled components must communicate efficiently. &lt;/p&gt;

&lt;p&gt;In the world of AWS there are three messaging and event-routing services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Simple Notification Service (SNS)&lt;/li&gt;
&lt;li&gt;Amazon Simple Queue Service (SQS)&lt;/li&gt;
&lt;li&gt;Amazon EventBridge. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each service excels in different scenarios, therefore understanding their nuances can help you design robust and scalable solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon SNS: Publish/Subscribe Messaging
&lt;/h2&gt;

&lt;p&gt;Amazon SNS is a fully managed publish/subscribe (pub/sub) service. It allows a publisher to send messages to multiple subscribers simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Broadcast Messaging:&lt;/strong&gt; SNS pushes messages to multiple subscribers in real-time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple Protocol Support:&lt;/strong&gt; Subscribers can receive messages over HTTP/S, email, SMS, or AWS services like Lambda and SQS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fan-Out Pattern:&lt;/strong&gt; Combine SNS with SQS to ensure message durability while maintaining pub/sub communication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use SNS:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Updates:&lt;/strong&gt; Notify multiple systems or users about an event immediately (e.g., sending order confirmation emails and updating a dashboard).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fan-Out Architecture:&lt;/strong&gt; Distribute messages to multiple processing pipelines (e.g., log processing, alerting systems).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Region Notifications:&lt;/strong&gt; Enable globally distributed applications to communicate efficiently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A news publishing platform uses SNS to notify users of breaking news. Each subscriber chooses a preferred delivery channel—email, SMS, or mobile push notifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Architecture:&lt;/strong&gt;&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%2Fhytlq281wf2iz24swb4c.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%2Fhytlq281wf2iz24swb4c.png" alt="Example SNS that does email, SMS, or mobile push notifications" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon SQS: Decoupled Queuing
&lt;/h2&gt;

&lt;p&gt;Amazon SQS is a fully managed message queuing service. It decouples components of distributed systems, allowing them to communicate asynchronously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Message Durability:&lt;/strong&gt; Messages are stored until they are processed or expire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processing Guarantee:&lt;/strong&gt; Messages are delivered at least once (Standard Queue) or exactly once (FIFO Queue).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; Supports massive throughput and unlimited queue size.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use SQS:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Task Queues:&lt;/strong&gt; Process tasks asynchronously, such as image processing or order fulfillment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message Retention:&lt;/strong&gt; Ensure messages are not lost if a consumer is temporarily unavailable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting:&lt;/strong&gt; Buffer requests to avoid overwhelming downstream systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An e-commerce platform uses SQS to manage order processing. Orders are added to a queue and processed asynchronously by a fleet of worker instances.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Architecture:&lt;/strong&gt;&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%2Fdojmizep1my5bvn7xqi7.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%2Fdojmizep1my5bvn7xqi7.png" alt="Simple SQS Example for Order Processing" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon EventBridge: Event Bus for Rule-Based Routing
&lt;/h2&gt;

&lt;p&gt;Amazon EventBridge is an event bus service that allows you to ingest and route events from various sources based on customizable rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event Routing:&lt;/strong&gt; Route events to multiple targets based on rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrated Sources:&lt;/strong&gt; Ingest events from AWS services, SaaS applications, or custom applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema Discovery:&lt;/strong&gt; Automatically discover and use schemas for events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use EventBridge:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event-Driven Workflows:&lt;/strong&gt; Trigger workflows or orchestrate tasks based on specific event patterns (e.g., serverless pipelines).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Routing:&lt;/strong&gt; Implement fine-grained routing logic for event processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrating SaaS Applications:&lt;/strong&gt; Easily connect third-party SaaS events into your workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A logistics company uses EventBridge to trigger a Lambda function when a shipment's status changes in its tracking system, enabling real-time notifications to customers and automated updates to internal dashboards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Architecture:&lt;/strong&gt;&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%2Fijan7js128bc77fk1x6k.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%2Fijan7js128bc77fk1x6k.png" alt="Example Architecture for EventBridge Triggers to two Lambda Functions" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Service
&lt;/h2&gt;

&lt;p&gt;The choice between SNS, SQS, and EventBridge depends on your use case:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;SNS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;SQS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;EventBridge&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Message Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pub/Sub&lt;/td&gt;
&lt;td&gt;Queue&lt;/td&gt;
&lt;td&gt;Event Bus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delivery Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Push&lt;/td&gt;
&lt;td&gt;Pull&lt;/td&gt;
&lt;td&gt;Push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Routing Capability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Advanced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Durability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited (requires SQS for durable fan-out)&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time notifications, fan-out&lt;/td&gt;
&lt;td&gt;Task queues, async processing&lt;/td&gt;
&lt;td&gt;Event-driven workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Hybrid Patterns
&lt;/h2&gt;

&lt;p&gt;Combining these services can unlock even greater flexibility. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SNS + SQS:&lt;/strong&gt; Use SNS for fan-out to multiple SQS queues, ensuring durability and decoupled processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge + SQS:&lt;/strong&gt; Route events to specific SQS queues based on event patterns for targeted processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SNS, SQS, and EventBridge each play a vital role in event-driven architectures. SNS excels in real-time pub/sub messaging, SQS in decoupled queuing, and EventBridge in advanced event routing. By evaluating your requirements, you can choose the right service or combination of services to build your robust and scalable solution.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>webdev</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
