<?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>DEV Community: Ntombizakhona Mabaso</title>
    <description>The latest articles on DEV Community by Ntombizakhona Mabaso (@ntombizakhona).</description>
    <link>https://dev.to/ntombizakhona</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%2F899317%2Ffabcc82d-22a3-410a-b5b9-869687d73e09.jpg</url>
      <title>DEV Community: Ntombizakhona Mabaso</title>
      <link>https://dev.to/ntombizakhona</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ntombizakhona"/>
    <language>en</language>
    <item>
      <title>Implement Encryption By Using AWS Services | 🏗️ Create A KMS Customer Managed Key</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Wed, 03 Jun 2026 00:45:50 +0000</pubDate>
      <link>https://dev.to/aws-builders/implement-encryption-by-using-aws-services-create-a-kms-customer-managed-key-2eb7</link>
      <guid>https://dev.to/aws-builders/implement-encryption-by-using-aws-services-create-a-kms-customer-managed-key-2eb7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 2:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Security&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 2:&lt;/strong&gt; &lt;em&gt;Implement Encryption By Using AWS Services.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Encryption shows up everywhere, especially on this exam. S3, DynamoDB, SQS, Lambda environment variables, RDS, and more. You need to know the difference between client-side and server-side encryption, how KMS works, and when to use each approach.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Encryption at Rest vs Encryption In Transit
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Encryption At Rest&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Data stored on disk: S3 Objects, DynamoDB tables, EBS volumes, RDS databases.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Encryption In Transit&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Data moving between services or between client and server: HTTPS, TLS, VPN.&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;Where&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;At Rest&lt;/th&gt;
&lt;th&gt;&lt;em&gt;In Transit&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSE-S3, SSE-KMS, SSE-C&lt;/td&gt;
&lt;td&gt;&lt;em&gt;HTTPS (enforced via bucket policy)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encrypted by default (AWS owned or KMS)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;HTTPS (always)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RDS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KMS encryption&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SSL/TLS connections&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSE-KMS&lt;/td&gt;
&lt;td&gt;&lt;em&gt;HTTPS&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda env vars&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KMS (default + optional CMK)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;HTTPS&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  KMS Key Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Managed By&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Use Case&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS owned keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Default encryption (DynamoDB, S3 SSE-S3&lt;/em&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS managed keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS (in your account)&lt;/td&gt;
&lt;td&gt;Free (per-use charges)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;&lt;code&gt;aws/s3&lt;/code&gt;, &lt;code&gt;aws/dynamodb&lt;/code&gt; (you can't manage them)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customer managed keys (CMK)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;td&gt;Monthly + per-use&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Full control: rotation, policies, cross-account&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Envelope Encryption
&lt;/h3&gt;

&lt;p&gt;KMS can only directly encrypt up to &lt;strong&gt;4 KB&lt;/strong&gt;. For larger data, it uses envelope encryption:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; KMS generates a &lt;strong&gt;data key&lt;/strong&gt; (plaintext + encrypted copy)&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; You encrypt your data with the &lt;strong&gt;plaintext data key&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; You store the &lt;strong&gt;encrypted data key&lt;/strong&gt; alongside the encrypted data&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; You discard the plaintext data key from memory&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; To decrypt: KMS decrypts the data key → you decrypt the data&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The AWS Encryption SDK handles this automatically.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Server-Side Encryption Options for S3
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Option&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Key Management&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Use Case&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE-S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS manages everything&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Simplest, no KMS costs&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE-KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You control the KMS key&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Audit trail via CloudTrail, key policies&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE-C&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You provide the key with every request&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Full key control, AWS doesn't store the key&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Client-Side vs Server-Side Encryption
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Server-Side&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Client-Side&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who encrypts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS (after receiving data)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;You (before sending to AWS)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data in transit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encrypted by HTTPS&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Encrypted by you + HTTPS&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS or KMS&lt;/td&gt;
&lt;td&gt;&lt;em&gt;You (via KMS or your own keys)&lt;/em&gt;&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;Simple&lt;/td&gt;
&lt;td&gt;&lt;em&gt;More complex&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Most workloads&lt;/td&gt;
&lt;td&gt;&lt;em&gt;When you can't trust the storage layer&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Key Rotation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS managed keys:&lt;/strong&gt; Rotated every year automatically (can't change this)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer managed keys:&lt;/strong&gt; Enable automatic rotation (every year)&lt;/li&gt;
&lt;li&gt;Old key material is kept, existing encrypted data still works&lt;/li&gt;
&lt;li&gt;The key ID doesn't change with automatic rotation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual rotation:&lt;/strong&gt; Create a new key, update alias to point to it&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Certificate Management
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Free public SSL/TLS certificates for AWS services&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Private CA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Issue private certificates for internal services&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡ACM certificates can't be exported. They're bound to AWS services (CloudFront, ALB, API Gateway). They auto-renew.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  🏗️ Create A KMS Customer Managed Key
&lt;/h2&gt;

&lt;p&gt;Now let's put these concepts into practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a KMS customer managed key&lt;/li&gt;
&lt;li&gt;Encrypt and decrypt data using KMS&lt;/li&gt;
&lt;li&gt;Upload objects to S3 with different encryption options (SSE-S3, SSE-KMS)&lt;/li&gt;
&lt;li&gt;Enable automatic key rotation&lt;/li&gt;
&lt;li&gt;Encrypt Lambda environment variables with a custom KMS key&lt;/li&gt;
&lt;/ul&gt;


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

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;An AWS account&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An S3 bucket&lt;/strong&gt; (&lt;em&gt;we'll create one&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a KMS Customer Managed Key
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;KMS&lt;/strong&gt; console &lt;br&gt;
Click &lt;strong&gt;Create key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Configure key&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key type:&lt;/strong&gt; &lt;code&gt;Symmetric&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key usage:&lt;/strong&gt; &lt;code&gt;Encrypt and decrypt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Add labels&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alias:&lt;/strong&gt; &lt;code&gt;app-encryption-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Customer managed key for application data&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Define key administrative permissions - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key administrators:&lt;/strong&gt; &lt;code&gt;Select your IAM user&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Define key usage permissions - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key administrators:&lt;/strong&gt; &lt;code&gt;Select your IAM user&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Edit key policy - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; &lt;strong&gt;Review&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Finish&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Success&lt;br&gt;
Your AWS KMS key was created with alias app-encryption-key and key ID 17fx7cex-17ae-419x-x816-de16fx50x928.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 You now have a customer managed KMS key. Note the &lt;strong&gt;Key ID&lt;/strong&gt; and &lt;strong&gt;ARN&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Enable Automatic Key Rotation&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; Click on your key (&lt;code&gt;app-encryption-key&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Go to the &lt;strong&gt;Key material rotations&lt;/strong&gt; tab&lt;br&gt;
Click &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; &lt;strong&gt;Edit automatic key rotation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key rotation:&lt;/strong&gt; &lt;code&gt;Enable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotation period in days:&lt;/strong&gt; &lt;code&gt;365&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully enabled automatic key rotation&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 With automatic rotation, KMS keeps old key material so existing ciphertext can still be decrypted. The key ID and ARN don't change. With manual rotation (creating a new key), you get a new key ID. Use aliases to abstract this.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Encrypt and Decrypt Data with KMS
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Using the Console&lt;/strong&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 In the KMS console, you can't directly encrypt/decrypt from the UI&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Create a Lambda function to demonstrate&lt;br&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;Create function:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;KMSEncryptionDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;Python 3.12&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy:&lt;/strong&gt; &lt;code&gt;Code:&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&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;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="n"&gt;kms&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;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;KEY_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;alias/app-encryption-key&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Demonstrates KMS encrypt/decrypt operations.

    Key concepts:
    - KMS can directly encrypt up to 4 KB
    - For larger data, use GenerateDataKey (envelope encryption)
    - The AWS Encryption SDK handles envelope encryption automatically
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&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;encrypt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&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, this is sensitive data!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;encrypt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Encrypt data (up to 4 KB)
&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;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;KeyId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Plaintext&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ciphertext_b64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&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;CiphertextBlob&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&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="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="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;Data encrypted successfully&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;ciphertext&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ciphertext_b64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;keyId&lt;/span&gt;&lt;span class="sh"&gt;'&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;KeyId&lt;/span&gt;&lt;span class="sh"&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;elif&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;decrypt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Decrypt data
&lt;/span&gt;        &lt;span class="n"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ciphertext&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&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;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CiphertextBlob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ciphertext&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="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;Data decrypted successfully&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;plaintext&lt;/span&gt;&lt;span class="sh"&gt;'&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;Plaintext&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&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;elif&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;generate_data_key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Generate a data key for envelope encryption
&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;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_data_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;KeyId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;KeySpec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AES_256&lt;/span&gt;&lt;span class="sh"&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="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="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;Data key generated (envelope encryption)&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;plaintextKey&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;*** exists in memory only — use it to encrypt, then discard ***&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;encryptedKey&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&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;CiphertextBlob&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;note&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;Store the encrypted key alongside your encrypted data&lt;/span&gt;&lt;span class="sh"&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;Step 02:&lt;/strong&gt; Add KMS permissions to the Lambda role:&lt;br&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Permissions&lt;/strong&gt; → click role name&lt;br&gt;
&lt;strong&gt;Add permissions&lt;/strong&gt; → &lt;strong&gt;Attach policies&lt;/strong&gt; → &lt;code&gt;AWSKeyManagementServicePowerUser&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Test events&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Encrypt:&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="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"encrypt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My secret credit card number"&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;Generate data key:&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="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"generate_data_key"&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;h2&gt;
  
  
  Part III
&lt;/h2&gt;

&lt;h3&gt;
  
  
  S3 Encryption Options
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create an S3 Bucket&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;S3&lt;/strong&gt; console → &lt;strong&gt;Create bucket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;General configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bucket type&lt;/strong&gt;: &lt;code&gt;General purpose&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bucket namespace&lt;/strong&gt;: &lt;code&gt;Account Regional namespace (recommended)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bucket name prefix&lt;/strong&gt;: &lt;code&gt;dva-encryption-demo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption type:&lt;/strong&gt; &lt;code&gt;Server-side encryption with Amazon S3 managed keys (SSE-S3)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Upload with SSE-S3 (Default)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add files&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;file&lt;/code&gt; (any text file)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡&lt;code&gt;SSE-S3&lt;/code&gt; is applied automatically&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Upload with SSE-KMS&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add files&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Expand &lt;strong&gt;▶ Properties&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server side encryption:&lt;/strong&gt; &lt;code&gt;Specify an encryption key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption settings:&lt;/strong&gt; &lt;code&gt;Override bucket settings for default encryption&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption type:&lt;/strong&gt; &lt;code&gt;Server-side encryption with AWS Key Management Service keys (SSE-KMS)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS KMS Key:&lt;/strong&gt; &lt;code&gt;Choose from your AWS KMS keys&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Available AWS KMS Key:&lt;/strong&gt; &lt;code&gt;app-encryption-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Verify Encryption&lt;/strong&gt;&lt;br&gt;
Click on each uploaded file&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Go to the &lt;strong&gt;Properties&lt;/strong&gt; tab → &lt;strong&gt;Server-side encryption settings&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;You'll see which encryption method was used&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡&lt;code&gt;SSE-KMS&lt;/code&gt; gives you an audit trail in CloudTrail (every encrypt/decrypt call is logged). &lt;code&gt;SSE-S3 does not&lt;/code&gt;. If a question mentions "audit" or "compliance," &lt;code&gt;SSE-KMS&lt;/code&gt; is usually the answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Encrypt Lambda Environment Variables
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Using a Custom KMS Key&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open your &lt;code&gt;KMSEncryptionDemo&lt;/code&gt; function&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Environment variables&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Edit environment variables&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Add environment variable&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;DB_PASSWORD&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;super-secret-password&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Expand &lt;strong&gt;▶ Encryption configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encryption in transit:&lt;/strong&gt; &lt;code&gt;✔ Check&lt;/code&gt; &lt;strong&gt;Enable helpers for encryption in transit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS KMS key to encrypt at rest:&lt;/strong&gt; &lt;code&gt;Use a customer managed key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;app-encryption-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Encrypt&lt;/strong&gt; button next to &lt;code&gt;DB_PASSWORD&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The value is now encrypted. &lt;br&gt;
In your code, you'd decrypt it:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;kms&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;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Decrypt at cold start, cache the result
&lt;/span&gt;&lt;span class="n"&gt;ENCRYPTED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;DECRYPTED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;CiphertextBlob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENCRYPTED&lt;/span&gt;&lt;span class="p"&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;Plaintext&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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="c1"&gt;# Use DECRYPTED: already decrypted at cold start
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password length: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DECRYPTED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Don't log the actual password!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a Customer Managed KMS key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Key types: &lt;em&gt;AWS owned vs AWS managed vs customer managed&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*&lt;em&gt;Assigned key administrators and key users *&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;KMS key policies: &lt;em&gt;separate admin and usage permissions&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enabled automatic key rotation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Old key material preserved, key ID unchanged, seamless decryption&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encrypted data directly with &lt;code&gt;kms.encrypt()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KMS direct encryption: &lt;em&gt;limited to 4 KB&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Called &lt;code&gt;generate_data_key&lt;/code&gt; for AES-256&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Envelope encryption: &lt;em&gt;encrypt large data locally, protect the key with KMS&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discarded the plaintext data key after use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security best practice: &lt;em&gt;plaintext key lives only in memory&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uploaded S3 objects with SSE-S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Default encryption: &lt;em&gt;AWS manages everything, no audit trail&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uploaded S3 objects with SSE-KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Audit trail in CloudTrail, key policy control, cross-account possible&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encrypted Lambda environment variables with a CMK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Protecting secrets at rest in Lambda configuration&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decrypted env vars at cold start and cached the result&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Performance pattern: &lt;em&gt;avoid decrypting on every invocation&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; → Empty and delete the bucket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;KMSEncryptionDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KMS&lt;/strong&gt; → Schedule key deletion (7-day minimum waiting period)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete Lambda execution roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete log groups&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;KMS encrypts up to 4 KB directly:&lt;/strong&gt; use envelope encryption (GenerateDataKey) for larger data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSE-S3:&lt;/strong&gt; simplest. &lt;strong&gt;SSE-KMS:&lt;/strong&gt; audit trail + key control. &lt;strong&gt;SSE-C:&lt;/strong&gt; you provide the key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-side encryption:&lt;/strong&gt; you encrypt before sending to AWS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic key rotation&lt;/strong&gt; keeps old key material and existing data ia still decryptable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use aliases&lt;/strong&gt; for seamless manual key rotation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-account KMS&lt;/strong&gt; requires both a key policy AND an IAM policy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACM&lt;/strong&gt; = free public certs (can't export). &lt;strong&gt;Private CA&lt;/strong&gt; = private certs (costs money).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Envelope encryption:&lt;/strong&gt; the AWS Encryption SDK handles this for you&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html" rel="noopener noreferrer"&gt;AWS KMS concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html" rel="noopener noreferrer"&gt;Protecting data using server-side encryption in S3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html" rel="noopener noreferrer"&gt;Key policies in AWS KMS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/introduction.html" rel="noopener noreferrer"&gt;What is the AWS Encryption SDK?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html" rel="noopener noreferrer"&gt;Rotate AWS KMS keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-encryption" rel="noopener noreferrer"&gt;Working with Lambda environment variables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏗️
&lt;/h2&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>developer</category>
    </item>
    <item>
      <title>Implement Authentication And/Or Authorization For Applications And AWS Services | 🏗️ Build A Secure Notes API</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Tue, 02 Jun 2026 22:54:48 +0000</pubDate>
      <link>https://dev.to/aws-builders/implement-authentication-andor-authorization-for-applications-and-aws-services-build-a-secure-13op</link>
      <guid>https://dev.to/aws-builders/implement-authentication-andor-authorization-for-applications-and-aws-services-build-a-secure-13op</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 2:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Security&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 1:&lt;/strong&gt; &lt;em&gt;Implement Authentication And/Or Authorisation For Applications And AWS&lt;br&gt;
Services&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://ntombizakhona.medium.com/authentication-fb0d207899a1" rel="noopener noreferrer"&gt;&lt;em&gt;Authentication&lt;/em&gt;&lt;/a&gt; (who are you?) and &lt;a href="https://ntombizakhona.medium.com/authorisation-ce5e449e621a" rel="noopener noreferrer"&gt;&lt;em&gt;Authorisation&lt;/em&gt;&lt;/a&gt; (what can you do?) are central to the DVA-C02, and just Cloud Development in general. &lt;br&gt;
You need to know Cognito, IAM Roles and Policies, STS, Lambda Authorizers, and how to secure microservice-to-microservice communication or communication within a Microservices Architectural Environment.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘 Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Authentication vs Authorisation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;What It Answers&lt;/th&gt;
&lt;th&gt;AWS Services&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Who are you?&lt;/td&gt;
&lt;td&gt;Cognito User Pools, IAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authorisation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What can you do?&lt;/td&gt;
&lt;td&gt;IAM Policies, Cognito Identity Pools, Lambda Authorizers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Amazon Cognito
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;User Pools vs Identity Pools&lt;/strong&gt;
&lt;/h4&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;User Pool&lt;/th&gt;
&lt;th&gt;Identity Pool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Authentication (sign up, sign in)&lt;/td&gt;
&lt;td&gt;Authorisation (temporary AWS credentials)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Returns&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JWT tokens (ID, access, refresh)&lt;/td&gt;
&lt;td&gt;AWS credentials (access key, secret, session token)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use When&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You need a user directory&lt;/td&gt;
&lt;td&gt;Users need to call AWS services directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Federation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google, Facebook, SAML, OIDC&lt;/td&gt;
&lt;td&gt;Google, Facebook, SAML, User Pools&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;User Pools = &lt;em&gt;authentication&lt;/em&gt; (get tokens). &lt;br&gt;
Identity Pools = &lt;em&gt;authorisation&lt;/em&gt; (get AWS credentials). &lt;br&gt;
They're often used together but serve different purposes.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;The Three Cognito Tokens&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Token&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Contains&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Use For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ID Token&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;User identity claims (name, email, groups)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Your application to identify the user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access Token&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Scopes and permissions&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;API authorisation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Refresh Token&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Long-lived credential&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Getting new ID/access tokens without re-authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  API Gateway Authorisation Options
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Option&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;None&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;No auth&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Public APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SigV4 signing&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Internal/service-to-service calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cognito Authorizer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Validates JWTs from User Pool&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Simple JWT validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda Authorizer (Token)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Custom logic on the token&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Complex auth, external IDPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda Authorizer (Request)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Custom logic on full request&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Auth based on headers, query strings, source IP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  IAM Roles vs Users
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Credentials&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM User&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Long-lived (access key + secret)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Humans, legacy CLI access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM Role&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Temporary (via STS AssumeRole)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Applications, EC2, Lambda, cross-account&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Roles are always preferred for applications. Never hardcode access keys. &lt;br&gt;
Lambda uses an &lt;em&gt;execution role&lt;/em&gt;, EC2 uses an &lt;em&gt;instance profile&lt;/em&gt;, ECS uses a &lt;em&gt;task role&lt;/em&gt;.&lt;br&gt;
All of these give your code temporary credentials automatically.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  STS (Security Token Service) Operations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Operation&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AssumeRole&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Same or cross-account role assumption&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AssumeRoleWithWebIdentity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Exchange OAuth/OIDC token for AWS credential&lt;/strong&gt;s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AssumeRoleWithSAML&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Exchange SAML assertion for AWS credentials&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetSessionToken&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Temporary credentials (for MFA-protected API calls)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Credential Resolution Order (SDK Default Chain)
&lt;/h3&gt;

&lt;p&gt;The AWS SDK looks for credentials in this order:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Environment variables (&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;)&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Shared credentials file (&lt;code&gt;~/.aws/credentials&lt;/code&gt;)&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; AWS config file (&lt;code&gt;~/.aws/config&lt;/code&gt;)&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; Container credentials (&lt;code&gt;ECS task role&lt;/code&gt;)&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; Instance profile (&lt;code&gt;EC2 role&lt;/code&gt;)&lt;br&gt;
&lt;strong&gt;6.&lt;/strong&gt; SSO credentials (&lt;code&gt;IAM Identity Center&lt;/code&gt;)&lt;/p&gt;


&lt;h2&gt;
  
  
  🏗️ Build A Secure Notes API
&lt;/h2&gt;

&lt;p&gt;Now lets put these concepts into practice, by building a &lt;strong&gt;Secure Notes  API&lt;/strong&gt; with Cognito authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Cognito User Pool with Managed Login for sign-up/sign-in&lt;/li&gt;
&lt;li&gt;An App Client configured with the authorization code grant flow&lt;/li&gt;
&lt;li&gt;A Lambda function that stores and retrieves notes&lt;/li&gt;
&lt;li&gt;An API Gateway REST API protected by a Cognito authorizer&lt;/li&gt;
&lt;li&gt;Test users signing in and calling the API with JWT tokens&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;An AWS account&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create the Cognito User Pool
&lt;/h3&gt;

&lt;p&gt;The Cognito console now uses a streamlined, application-focused setup wizard with &lt;strong&gt;Managed Login&lt;/strong&gt; (the successor to the classic Hosted UI). The wizard creates your user pool and app client in one flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Cognito&lt;/strong&gt; console &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create user pool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Set up resources for your application&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Application type:&lt;/strong&gt; &lt;code&gt;Traditional web application&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Name your application:&lt;/strong&gt; &lt;code&gt;My Notes App&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Options for sign-in identifiers:&lt;/strong&gt; &lt;code&gt;Email&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Self-registration:&lt;/strong&gt; &lt;code&gt;✔&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Required attributes for sign-up:&lt;/strong&gt; &lt;code&gt;email&lt;/code&gt; &lt;code&gt;name&lt;/code&gt;&lt;br&gt;
Click &lt;strong&gt;Create user directory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Your application "My Notes App" and user pool "User pool - gcvhqy" have been created successfully! Follow the instruction to continue the setup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡The new console creates the user pool, app client, and managed login domain all in one step. "Traditional web application" configures the authorization code grant flow with a client secret by default. This is the recommended pattern for server-side apps.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Get Your Pool Details&lt;/strong&gt;&lt;br&gt;
After creation, click on the user pool and note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User pool ID&lt;/strong&gt; (looks like &lt;code&gt;us-east-1_ABC123XYZ&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App client ID&lt;/strong&gt; (from the &lt;strong&gt;▼ Applications&lt;/strong&gt; tab → &lt;strong&gt;App clients&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognito domain&lt;/strong&gt; (from the &lt;strong&gt;▼ Branding&lt;/strong&gt; tab → &lt;strong&gt;Domain&lt;/strong&gt; )&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a Test User
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; In the User Pool, click the &lt;strong&gt;▼ Users management&lt;/strong&gt; tab&lt;br&gt;
Click &lt;strong&gt;Users&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Users&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Create user&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;User Information&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Invitation message:&lt;/strong&gt; &lt;code&gt;Send an email invitation&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email address:&lt;/strong&gt; &lt;code&gt;Use a real email you can access&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporary password:&lt;/strong&gt; &lt;code&gt;Generate a password&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; User &lt;em&gt;&lt;a href="mailto:username@example.com"&gt;username@example.com&lt;/a&gt;&lt;/em&gt; has been created successfully.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You'll receive an email with a temporary password.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;⚠️ Don't log in yet. We'll do that through the &lt;strong&gt;Managed Login&lt;/strong&gt; in a moment.&lt;/p&gt;


&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Test with Managed Login (Authorization Code Flow)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; View the hosted UI URL&lt;br&gt;
From the &lt;strong&gt;▼ Applications&lt;/strong&gt; tab → &lt;strong&gt;App clients&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;View login page&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://YOUR_COGNITO_DOMAIN.auth.us-east-1.amazoncognito.com/login?
  client_id=YOUR_APP_CLIENT_ID&amp;amp;
  response_type=code&amp;amp;
  scope=email+openid+profile&amp;amp;
  redirect_uri=YOUR_CALLBACK_URL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Sign in&lt;/strong&gt;&lt;br&gt;
Sign in with the user credentials from the email&lt;br&gt;
Cognito will force you to set a permanent password&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Change password&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅:&lt;/strong&gt; &lt;strong&gt;Success page&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Successfully signed in&lt;br&gt;
This is the default redirect page for Amazon Cognito user pools.&lt;br&gt;
You're seeing this page because your Amazon Cognito app client doesn't have a return URL set.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; After login, you'll be redirected to &lt;code&gt;https://d84l1y8p4kdic.cloudfront.net/?code=2f927666-cd13-4e94-9af9-58887a9d9cd3...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;💡Copy the &lt;code&gt;code&lt;/code&gt; value from the URL fragment.&lt;br&gt;
You'll need it to call the API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;This code is NOT a token.&lt;/strong&gt; &lt;em&gt;It's a one-time authorization code that expires in a few minutes. You need to exchange it for actual JWT tokens in the next step.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Exchange the Code for Tokens&lt;/strong&gt;&lt;br&gt;
Use the Cognito token endpoint to exchange the authorization code for JWT tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://YOUR_COGNITO_DOMAIN.auth.us-east-1.amazoncognito.com/oauth2/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"grant_type=authorization_code&amp;amp;code=YOUR_AUTH_CODE&amp;amp;client_id=YOUR_APP_CLIENT_ID&amp;amp;client_secret=YOUR_CLIENT_SECRET&amp;amp;redirect_uri=YOUR_CALLBACK_URL"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ If your app client has a client secret (default for "Traditional web application"), you also need to include a &lt;code&gt;client_secret&lt;/code&gt; parameter, or pass the credentials as a Basic auth header: &lt;code&gt;-H "Authorization: Basic BASE64(client_id:client_secret)"&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Expected response:&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;"id_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJraWQiOiJ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJraWQiOiJ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJjdHkiOiJ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_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;"Bearer"&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;💡 Copy the &lt;code&gt;id_token&lt;/code&gt; value. You'll need it to call the API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;response_type=token&lt;/code&gt; uses the implicit grant flow (returns tokens directly). &lt;code&gt;response_type=code&lt;/code&gt; uses the authorization code flow (returns a code you exchange for tokens via the token endpoint). &lt;br&gt;
Auth code is more secure for production apps.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create the DynamoDB Table
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt;  Open the &lt;strong&gt;DynamoDB&lt;/strong&gt; console → &lt;strong&gt;Create table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Create table&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Table name:&lt;/strong&gt; &lt;code&gt;Notes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partition key:&lt;/strong&gt; &lt;code&gt;userId&lt;/code&gt; (String)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort key:&lt;/strong&gt; &lt;code&gt;noteId&lt;/code&gt; (String)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Table settings:&lt;/strong&gt; &lt;code&gt;Default settings&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; The Notes table was created successfully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create the Notes Lambda Function
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;NotesAPI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;Python 3.12&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created the function "NotesAPI"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Configuration → &lt;strong&gt;General configuration&lt;/strong&gt; → click &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; &lt;code&gt;256 MB&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout:&lt;/strong&gt; &lt;code&gt;0 min 10 sec&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "NotesAPI".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Add DynamoDB Permissions&lt;br&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Permissions&lt;/strong&gt; → click the &lt;strong&gt;Role name&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Add permissions ▼&lt;/strong&gt; → &lt;strong&gt;Attach policies&lt;/strong&gt; → search &lt;code&gt;AmazonDynamoDBFullAccess&lt;/code&gt; → &lt;strong&gt;attach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Policies have been successfully attached to role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Function Code&lt;/strong&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;boto3.dynamodb.conditions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Key&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;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;Notes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Notes API with user isolation.
    Each user can only access their own notes — enforced by extracting
    the userId from the JWT claims and using it as the partition key.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;httpMethod&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;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&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;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Extract the authenticated user from Cognito claims
&lt;/span&gt;    &lt;span class="c1"&gt;# API Gateway puts the JWT claims here after the authorizer validates the token
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;claims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;requestContext&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;authorizer&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;claims&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sub&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Cognito's unique user ID
&lt;/span&gt;        &lt;span class="n"&gt;user_email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&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;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyError&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;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&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;error&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;Unauthorized&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request from user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/notes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&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;list_notes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/notes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&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="nf"&gt;get&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="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="sh"&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;create_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/notes/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;note_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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;get_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;note_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/notes/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DELETE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;note_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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;delete_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;note_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;else&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;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&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;error&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;Not found&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;response&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&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;Internal server error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_notes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;List all notes for the authenticated user.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&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;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;KeyConditionExpression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Key&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="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&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;response&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="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;notes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Items&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;count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Count&lt;/span&gt;&lt;span class="sh"&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;def&lt;/span&gt; &lt;span class="nf"&gt;create_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a new note owned by the authenticated user.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;note_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;note&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="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# ← partition key ensures data isolation
&lt;/span&gt;        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;noteId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;note_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&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;Untitled&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;content&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;content&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;createdAt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&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;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="n"&gt;note&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;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;note_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get a specific note. User can only access their own notes.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&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="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;noteId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;note_id&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;item&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;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&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;error&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;Note not found&lt;/span&gt;&lt;span class="sh"&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;response&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="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;note_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Delete a note. User can only delete their own notes.&lt;/span&gt;&lt;span class="sh"&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;delete_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="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;noteId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;note_id&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;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&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="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&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;Step 07:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notice the user isolation pattern. The &lt;code&gt;userId&lt;/code&gt; comes from the JWT claims (set by Cognito, validated by API Gateway), not from the request body. Users can't forge this value because API Gateway won't invoke Lambda without a valid token. This is application-level authorization.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part VI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create the API Gateway API
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create the API&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open &lt;strong&gt;API Gateway&lt;/strong&gt; → &lt;strong&gt;Create API&lt;/strong&gt; → &lt;strong&gt;REST API&lt;/strong&gt; → &lt;strong&gt;Build&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Create REST API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;New API&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API name:&lt;/strong&gt; &lt;code&gt;NotesAPI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API endpoint type:&lt;/strong&gt; Regional&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security policy - &lt;em&gt;new&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;TLS_1_0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created REST API 'NotesAPI (xoq5e4tu3j)'.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create the Cognito Authorizer&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; In the left sidebar, click &lt;strong&gt;Authorizers&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Create authorizer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Create authorizer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authorizer name:&lt;/strong&gt; &lt;code&gt;CognitoAuth&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorizer type:&lt;/strong&gt; &lt;code&gt;Cognito&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognito user pool:&lt;/strong&gt; Select &lt;code&gt;NotesUserPool&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token source:&lt;/strong&gt; &lt;code&gt;Authorization&lt;/code&gt; (&lt;em&gt;this is the header name&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create authorizer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created authorizer 'CognitoAuth'.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create the Resources and Methods&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Click &lt;strong&gt;Resources&lt;/strong&gt; → &lt;strong&gt;Create resource&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Create resource&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource path:&lt;/strong&gt; &lt;code&gt;/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource name:&lt;/strong&gt; &lt;code&gt;notes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;✔ CORS (Cross Origin Resource Sharing)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create resource&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created resource '/notes'&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Now create a child resource for &lt;code&gt;/notes/{noteId}&lt;/code&gt;:&lt;br&gt;
Select the &lt;code&gt;/notes&lt;/code&gt; resource → &lt;strong&gt;Create resource&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource path:&lt;/strong&gt; &lt;code&gt;/notes/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource name:&lt;/strong&gt; &lt;code&gt;{noteID}&lt;/code&gt; (proxy resource)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create resource&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created resource '/notes/{noteID}'&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Add the Methods&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; For each method below, click the resource, then &lt;strong&gt;Create method&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; &lt;strong&gt;POST /notes&lt;/strong&gt; (create a note)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;▼ Method type:&lt;/strong&gt; &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration type:&lt;/strong&gt; &lt;code&gt;Lambda Function&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda proxy integration:&lt;/strong&gt; &lt;code&gt;✔&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda function:&lt;/strong&gt; &lt;code&gt;NotesAPI&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created method 'POST' in 'notes'. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; &lt;strong&gt;GET /notes&lt;/strong&gt; (list notes)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;▼ Method type:&lt;/strong&gt; &lt;code&gt;GET&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration type:&lt;/strong&gt; &lt;code&gt;Lambda Function&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda proxy integration:&lt;/strong&gt; &lt;code&gt;✔&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda function:&lt;/strong&gt; &lt;code&gt;NotesAPI&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created method 'GET' in 'notes'. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 11:&lt;/strong&gt; &lt;strong&gt;GET /notes/{noteId}&lt;/strong&gt; (get one note)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;▼ Method type:&lt;/strong&gt; &lt;code&gt;GET&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration type:&lt;/strong&gt; &lt;code&gt;Lambda Function&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda proxy integration:&lt;/strong&gt; &lt;code&gt;✔&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda function:&lt;/strong&gt; &lt;code&gt;NotesAPI&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created method 'GET' in 'noteID'.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 12:&lt;/strong&gt; &lt;strong&gt;DELETE /notes/{noteId}&lt;/strong&gt; (delete a note)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;▼ Method type:&lt;/strong&gt; &lt;code&gt;DELETE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration type:&lt;/strong&gt; &lt;code&gt;Lambda Function&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda proxy integration:&lt;/strong&gt; &lt;code&gt;✔&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda function:&lt;/strong&gt; &lt;code&gt;NotesAPI&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created method 'DELETE' in 'noteID'. &lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Attach the Authorizer to Each Method&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 13:&lt;/strong&gt; For each of the four methods:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13.1:&lt;/strong&gt; Click the method (e.g., GET under &lt;code&gt;/notes&lt;/code&gt;)&lt;br&gt;
&lt;strong&gt;13.2:&lt;/strong&gt; Click &lt;strong&gt;Method request&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;13.3:&lt;/strong&gt; Set &lt;strong&gt;Authorization&lt;/strong&gt; to &lt;code&gt;CognitoAuth&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;13.4:&lt;/strong&gt; Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banners:&lt;/strong&gt; Successfully edited method request for ‘x’&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Deploy the API&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 14:&lt;/strong&gt; Click &lt;strong&gt;Deploy API&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Stage:&lt;/strong&gt; Create a new stage called &lt;code&gt;dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created deployment for NotesAPI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 15:&lt;/strong&gt; Copy the &lt;strong&gt;Invoke URL&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Part VII
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Test the Authenticated API
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Test One:&lt;/strong&gt; Unauthenticated Request (Should Fail)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://YOUR_API_URL/dev/notes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"title": "My first note", "content": "Hello world"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Expected response:&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="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unauthorized"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ API Gateway rejected the request because there's no JWT token. Lambda was never invoked.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test Two:&lt;/strong&gt; Authenticated Request (Should Succeed)
&lt;/h4&gt;

&lt;p&gt;Use the &lt;code&gt;id_token&lt;/code&gt; you got from the token exchange in Part III:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://YOUR_API_URL/dev/notes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: eyJraWQiOiJ..."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"title": "My first note", "content": "Hello world"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&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;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc-123-def-456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"noteId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7f8e9d0c-..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My first note"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-24T10:00:00"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Notice the &lt;code&gt;userId&lt;/code&gt; was pulled from the JWT claims. The user didn't have to send it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test Three:&lt;/strong&gt; List Your Notes
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://YOUR_API_URL/dev/notes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: YOUR_ID_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll only see notes belonging to the authenticated user.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test Four:&lt;/strong&gt; Invalid Token (Should Fail)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://YOUR_API_URL/dev/notes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: invalid.token.here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&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="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unauthorized"&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;h2&gt;
  
  
  Part VIII
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Build a Lambda Authorizer (Advanced)
&lt;/h3&gt;

&lt;p&gt;Cognito authorizers are simple. &lt;br&gt;
They just validate JWT signatures. &lt;br&gt;
Lambda authorizers let you implement custom logic. &lt;br&gt;
Let's see how.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create the Authorizer Function&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Create function&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;NotesLambdaAuthorizer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;Python 3.12&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created the function "NotesLambdaAuthorizer".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Paste this code&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Custom Lambda authorizer.
    Returns an IAM policy that allows or denies the API call.

    Lambda authorizer results can be cached (default 5 min, max 1 hour)
    to reduce the number of Lambda invocations.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;authorizationToken&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="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bearer &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="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;method_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;methodArn&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorizing request with token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# In production, validate the JWT signature using the Cognito JWKS
&lt;/span&gt;    &lt;span class="c1"&gt;# For this demo, we just check if the token starts with a specific prefix
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;invalid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Deny access
&lt;/span&gt;        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unauthorized&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Simulate extracting user info from the token
&lt;/span&gt;    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;demo-user-001&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;user_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="c1"&gt;# Build the policy based on the user's role
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_role&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="c1"&gt;# Admin can access everything in the API
&lt;/span&gt;        &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;method_arn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rsplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/*/*&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="c1"&gt;# Regular user can only access the specific method
&lt;/span&gt;        &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;method_arn&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;principalId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;policyDocument&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Version&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;2012-10-17&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;Statement&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Action&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;execute-api:Invoke&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;Effect&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Resource&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;
            &lt;span class="p"&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;context&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;userRole&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_role&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;Step 04:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "NotesLambdaAuthorizer"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Attach It to the API (Walkthrough Only)&lt;br&gt;
In API Gateway:&lt;br&gt;
&lt;strong&gt;1.&lt;/strong&gt; &lt;strong&gt;Authorizers&lt;/strong&gt; → &lt;strong&gt;Create authorizer&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;LambdaAuth&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorizer type:&lt;/strong&gt; Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda function:&lt;/strong&gt; &lt;code&gt;NotesLambdaAuthorizer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda event payload:&lt;/strong&gt; Token&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token source:&lt;/strong&gt; &lt;code&gt;Authorization&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization caching:&lt;/strong&gt; 300 seconds
&lt;strong&gt;3.&lt;/strong&gt; &lt;strong&gt;Create authorizer&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You could switch methods to use this instead of the Cognito authorizer. We won't switch for this tutorial. The Cognito authorizer is already doing its job.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡Lambda authorizer results are &lt;em&gt;cached&lt;/em&gt; (up to 1 hour). This reduces Lambda invocations but means permission changes aren't immediate.&lt;/strong&gt; Two types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Token-based:&lt;/em&gt; receives the token, caches by token value&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Request-based:&lt;/em&gt; receives the full request, caches by specified identity sources.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a Cognito User Pool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Authentication via managed user directory&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configured the Cognito Hosted UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;OAuth 2.0 authorization code / implicit grant flows&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Signed in and received ID, access, and refresh tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;The three Cognito token types and their purposes&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attached a Cognito Authorizer to API Gateway methods&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;JWT validation at the API layer&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extracted &lt;code&gt;sub&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; from JWT claims in Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Passing identity from API Gateway to Lambda&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used &lt;code&gt;userId&lt;/code&gt; as the DynamoDB partition key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Application-level authorization (data isolation)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built a Lambda authorizer with custom logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Fine-grained authorization&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Returned an IAM policy from the Lambda authorizer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;How Lambda authorizers control access&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Set the Lambda authorizer cache TTL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Performance vs permission freshness trade-off&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tested with and without a valid token&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;API Gateway rejects unauthenticated requests before Lambda&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; → Delete &lt;code&gt;NotesAPI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;NotesAPI&lt;/code&gt;, &lt;code&gt;NotesLambdaAuthorizer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt; → Delete the &lt;code&gt;Notes&lt;/code&gt; table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognito&lt;/strong&gt; → Delete the User Pool (also delete the domain first)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete the Lambda execution roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete the log groups&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cognito User Pools&lt;/strong&gt; = authentication (tokens). &lt;strong&gt;Identity Pools&lt;/strong&gt; = authorization (AWS credentials).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three tokens:&lt;/strong&gt; ID (user identity), Access (API auth), Refresh (getting new tokens).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognito Authorizer&lt;/strong&gt; = simple JWT validation. &lt;strong&gt;Lambda Authorizer&lt;/strong&gt; = custom logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda authorizer caching:&lt;/strong&gt; (up to 1 hour) reduces invocations but delays permission changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never hardcode credentials:&lt;/strong&gt; use IAM roles (execution role for Lambda, task role for ECS, instance profile for EC2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STS AssumeRole&lt;/strong&gt; for cross-account access and temporary credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SigV4 signing&lt;/strong&gt; is used for IAM authorization on API Gateway (service-to-service).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential resolution order:&lt;/strong&gt; env vars → credentials file → config → container → instance profile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT claims&lt;/strong&gt; are passed to Lambda via &lt;code&gt;event.requestContext.authorizer.claims&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application-level authorization:&lt;/strong&gt; use the authenticated user ID as the partition key to enforce data isolation.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/authentication-fb0d207899a1" rel="noopener noreferrer"&gt;Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/authorisation-ce5e449e621a" rel="noopener noreferrer"&gt;Authorisation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.hashnode.dev/authentication-vs-authorisation" rel="noopener noreferrer"&gt;Authentication vs Authorisation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html" rel="noopener noreferrer"&gt;Amazon Cognito User Pools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html" rel="noopener noreferrer"&gt;Control access to a REST API using Amazon Cognito user pools as authorizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html" rel="noopener noreferrer"&gt;Use API Gateway Lambda authorizers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html" rel="noopener noreferrer"&gt;Temporary security credentials in IAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/security-iam.html" rel="noopener noreferrer"&gt;Identity and access management for API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html" rel="noopener noreferrer"&gt;Amazon Cognito Identity Pools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>Formerly Known As</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Wed, 20 May 2026 11:25:01 +0000</pubDate>
      <link>https://dev.to/ntombizakhona/formerly-known-as-2j2f</link>
      <guid>https://dev.to/ntombizakhona/formerly-known-as-2j2f</guid>
      <description>&lt;h2&gt;
  
  
  Keeping Up With The Renamed World
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tech changes fast.&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Sometimes too fast.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One day you are confidently talking about a service, framework, tool, or company… and the next day it has a completely different name, a new logo, a rebrand announcement, and an updated pricing page. 😭&lt;/p&gt;

&lt;p&gt;That is exactly why I am starting this series:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;"Formerly Known As"&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A blog series where I keep up with the former names of current resources, services, platforms, tools, and technologies across Cloud, AI, Web Development, and Tech in general.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because honestly?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A huge part of learning tech is also learning its history.&lt;/p&gt;

&lt;p&gt;If you have ever said things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Wait… wasn’t this called something else before?”&lt;/li&gt;
&lt;li&gt;“Isn’t this just a rebrand?”&lt;/li&gt;
&lt;li&gt;“I still call it Twitter.”&lt;/li&gt;
&lt;li&gt;“When did this AWS service change names?”&lt;/li&gt;
&lt;li&gt;“Hold on… what happened to that?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…then this series is for you, and mostly me.😂&lt;/p&gt;

&lt;p&gt;I am going to explore:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Old names vs new names&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Why companies rebrand&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Mergers and acquisitions&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; Product evolution&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; Retired services&lt;br&gt;
&lt;strong&gt;6.&lt;/strong&gt; Tech nostalgia&lt;br&gt;
&lt;strong&gt;7.&lt;/strong&gt; The confusion these changes create for beginners&lt;/p&gt;

&lt;p&gt;And maybe most importantly:&lt;br&gt;
How keeping up with naming changes helps us better understand the tech ecosystem.&lt;/p&gt;

&lt;p&gt;Some posts may be quick and fun.&lt;br&gt;
Others may dive deeper into the history behind a platform or service.&lt;/p&gt;

&lt;p&gt;Either way, this series is for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers 👩🏽‍💻&lt;/li&gt;
&lt;li&gt;Cloud learners ☁️&lt;/li&gt;
&lt;li&gt;Curious tech people 🔍&lt;/li&gt;
&lt;li&gt;Documentation readers 📚&lt;/li&gt;
&lt;li&gt;People who still accidentally use old names 😅&lt;/li&gt;
&lt;li&gt;Me. &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;"Formerly Known As."&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The internet never forgets old names…&lt;br&gt;
and apparently neither do we.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;No particular frequency. &lt;br&gt;
No particular theme. &lt;br&gt;
Just the renamed things that cross my mind...and my notifications.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>womenintech</category>
      <category>developer</category>
      <category>cloud</category>
      <category>ai</category>
    </item>
    <item>
      <title>Use Data Stores In Application Development | 🏗️ Build A Product Catalog API</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Tue, 12 May 2026 20:45:57 +0000</pubDate>
      <link>https://dev.to/aws-builders/use-data-stores-in-application-development-build-a-product-catalog-api-404f</link>
      <guid>https://dev.to/aws-builders/use-data-stores-in-application-development-build-a-product-catalog-api-404f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 1:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Development with AWS Services&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 3:&lt;/strong&gt; &lt;em&gt;Use Data Stores In Application Development&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DynamoDB dominates this task. The need to understand table design, key selection, indexing, consistency models, and how to write efficient queries is essential. As well as caching with ElastiCache and DAX. Plus specialized stores like OpenSearch.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘 Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  DynamoDB Key Concepts
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Primary Keys&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Every table needs one. Two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple primary key:&lt;/strong&gt; Partition key only (PK). Each item has a unique PK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite primary key:&lt;/strong&gt; Partition key (PK) + Sort key (SK). Multiple items can share a PK if they have different SKs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Partition Key Selection&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The partition key determines which physical partition stores your data. A good partition key has &lt;strong&gt;high cardinality&lt;/strong&gt; which refers to many distinct values so that the data spreads evenly.&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;Good Partition Keys&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Bad Partition Keys&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;userId, orderId, sessionId&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;status&lt;/em&gt; ("active"/"inactive")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;deviceId, transactionId&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;country&lt;/em&gt; (few values, uneven distribution)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;email, accountId&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;date&lt;/em&gt; (hot partition for today)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Consistency Models
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Behaviour&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Available On&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Eventually Consistent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;May return stale data&lt;/em&gt; (usually consistent within 1 second)&lt;/td&gt;
&lt;td&gt;1x read capacity&lt;/td&gt;
&lt;td&gt;Base table + GSIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Strongly Consistent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Always returns the most up-to-date data&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;2x read capacity&lt;/td&gt;
&lt;td&gt;Base table only (NOT GSIs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Query vs Scan
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Operation&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;How It Works&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Finds items by partition key + optional sort key condition&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Reads only matching items&lt;/td&gt;
&lt;td&gt;Always prefer this&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Reads every item in the table&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Reads entire table&lt;/td&gt;
&lt;td&gt;Analytics, one-time migrations only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GetItem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Fetches one item by its full primary key&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Reads exactly one item&lt;/td&gt;
&lt;td&gt;When you know the exact key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;FilterExpression&lt;/code&gt; does NOT reduce the amount of data read. It only filters what's returned to you. You still pay for the full scan/query. To reduce reads, use better key design or GSIs&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Global Secondary Index (GSI) vs Local Secondary Index (LSI)
&lt;/h3&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;GSI&lt;/th&gt;
&lt;th&gt;LSI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Partition Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Different from base table&lt;/td&gt;
&lt;td&gt;Same as base table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sort Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Different from base table&lt;/td&gt;
&lt;td&gt;Different from base table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;When To Create&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Anytime&lt;/td&gt;
&lt;td&gt;At table creation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Has its own (separate from base table)&lt;/td&gt;
&lt;td&gt;Shares with base table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Eventually consistent only&lt;/td&gt;
&lt;td&gt;Supports strongly consistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20 per table&lt;/td&gt;
&lt;td&gt;5 per table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;GSI Projection Types:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ALL:&lt;/strong&gt; &lt;em&gt;all attributes&lt;/em&gt; (most flexible, most storage cost)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KEYS_ONLY:&lt;/strong&gt; &lt;em&gt;only key attributes&lt;/em&gt; (cheapest)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INCLUDE:&lt;/strong&gt; &lt;em&gt;keys + specified attributes&lt;/em&gt; (balanced)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Caching Options
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Use Case&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;th&gt;Works With&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DAX&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;DynamoDB read cache&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Microseconds&lt;/td&gt;
&lt;td&gt;DynamoDB only, eventually consistent only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ElastiCache Redis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;General-purpose cache&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Sub-millisecond&lt;/td&gt;
&lt;td&gt;Any data source, complex data types, persistence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ElastiCache Memcached&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Simple caching&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Sub-millisecond&lt;/td&gt;
&lt;td&gt;Any data source, multi-threaded, no persistence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Specialized Data Stores
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Store&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Use Case&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Key-value lookups, known access patterns, serverless&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RDS/Aurora&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Relational data, complex joins, ACID transactions&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenSearch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Full-text search, log analytics, complex queries&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Object storage, data lake, large files&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ElastiCache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Session storage, leaderboards, real-time analytics&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Data Lifecycle
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;DynamoDB TTL&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Automatically deletes expired items at no cost. Eventually consistent (up to 48 hours delay). &lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;S3 Lifecycle Policies&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Transition objects between storage classes (Standard → IA → Glacier) or expire them after a set time.&lt;/p&gt;


&lt;h2&gt;
  
  
  🏗️ Build A Product Catalog API
&lt;/h2&gt;

&lt;p&gt;Now let's put these concepts into practice by builidng a &lt;strong&gt;Product Catalog API&lt;/strong&gt; backed by DynamoDB:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A DynamoDB table with a composite primary key and a Global Secondary Index (GSI)&lt;/li&gt;
&lt;li&gt;A Lambda function that performs queries, scans, and writes&lt;/li&gt;
&lt;li&gt;TTL configured for automatic data expiration&lt;/li&gt;
&lt;li&gt;DAX caching in front of DynamoDB&lt;/li&gt;
&lt;li&gt;A clear understanding of when to use query vs scan, GSI vs LSI, and strong vs eventual consistency&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;An AWS account&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;(free tier covers DynamoDB and Lambda)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Design the DynamoDB Table
&lt;/h3&gt;

&lt;p&gt;Before creating anything, let's think about access patterns. This is the most important step in DynamoDB design.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Our Access Patterns&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Access Pattern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;How We'll Query&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Get a product by ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query PK = &lt;code&gt;PRODUCT#123&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List all products in a category&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query PK = &lt;code&gt;CATEGORY#electronics&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Get a product's reviews&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query PK = &lt;code&gt;PRODUCT#123&lt;/code&gt;, SK begins_with &lt;code&gt;REVIEW#&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Find products by price range in a category&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query GSI with category + price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List recently added products&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query GSI with status + createdAt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create the Table&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;DynamoDB&lt;/strong&gt; console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create table&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Table name:&lt;/strong&gt; &lt;code&gt;ProductCatalog&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partition key:&lt;/strong&gt; &lt;code&gt;PK&lt;/code&gt; (String)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort key — &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;SK&lt;/code&gt; (String)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Under &lt;strong&gt;Table settings&lt;/strong&gt;, choose &lt;strong&gt;Customize settings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Read/write capacity settings:&lt;/strong&gt; &lt;code&gt;On-demand&lt;/code&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Don't create the just table yet, let's add a GSI first&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Add a Global Secondary Index&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Scroll down to &lt;strong&gt;Secondary indexes&lt;/strong&gt; → click &lt;strong&gt;Create global index&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Index name:&lt;/strong&gt; &lt;code&gt;GSI1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partition key:&lt;/strong&gt; &lt;code&gt;GSI1PK&lt;/code&gt; (String)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort key:&lt;/strong&gt; &lt;code&gt;GSI1SK&lt;/code&gt; (String)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attribute projections:&lt;/strong&gt; &lt;code&gt;All&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create index&lt;/strong&gt;, then click &lt;strong&gt;Create table&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; The ProductCatalog table was created successfully.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Why This Design?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;We're using the &lt;strong&gt;single-table design&lt;/strong&gt; pattern with &lt;strong&gt;overloaded keys&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base table:
  PK = "PRODUCT#laptop-001"    SK = "METADATA"           → product details
  PK = "PRODUCT#laptop-001"    SK = "REVIEW#2026-04-24"  → a review
  PK = "CATEGORY#electronics"  SK = "PRODUCT#laptop-001" → category listing

GSI1:
  GSI1PK = "CATEGORY#electronics"  GSI1SK = "PRICE#00079.99" → find by price
  GSI1PK = "STATUS#active"         GSI1SK = "2026-04-24"     → find by date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Partition Key Selection: A good partition key has &lt;em&gt;high cardinality&lt;/em&gt; (many distinct values). Bad examples: &lt;code&gt;status&lt;/code&gt; (only a few values → hot partition), &lt;code&gt;country&lt;/code&gt; (uneven distribution). Good examples: &lt;code&gt;userId&lt;/code&gt;, &lt;code&gt;productId&lt;/code&gt;, &lt;code&gt;orderId&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Add Sample Data
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Using the Console&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt;  In the &lt;strong&gt;DynamoDB console&lt;/strong&gt;, click on &lt;code&gt;ProductCatalog&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Explore table items&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Click &lt;strong&gt;Create item&lt;/strong&gt;&lt;br&gt;
Switch to &lt;strong&gt;JSON view&lt;/strong&gt; (toggle at the top)&lt;br&gt;
Paste this item:&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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRODUCT#laptop-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;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"METADATA"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"GSI1PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CATEGORY#electronics"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"GSI1SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRICE#00999.99"&lt;/span&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="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pro Laptop 15"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"15-inch laptop with 16GB RAM"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"999.99"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electronics"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-20T10:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stock"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"50"&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;Step 04:&lt;/strong&gt; Click &lt;strong&gt;Create item&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add a few more products:&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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRODUCT#mouse-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;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"METADATA"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"GSI1PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CATEGORY#electronics"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"GSI1SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRICE#00029.99"&lt;/span&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="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wireless Mouse"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ergonomic wireless mouse"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"29.99"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electronics"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-22T10:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stock"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"200"&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;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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRODUCT#laptop-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;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REVIEW#2026-04-24#user-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;"rating"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"comment"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Great laptop, fast and reliable"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-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;"createdAt"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-24T14:30:00Z"&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;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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CATEGORY#electronics"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRODUCT#laptop-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;"name"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pro Laptop 15"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"999.99"&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;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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CATEGORY#electronics"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRODUCT#mouse-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;"name"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wireless Mouse"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"29.99"&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;h2&gt;
  
  
  Part III
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Query vs Scan
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Query (Efficient)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; In the &lt;strong&gt;▼ Scan or query items&lt;/strong&gt; tab, switch from &lt;strong&gt;Scan&lt;/strong&gt; to &lt;strong&gt;Query&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Set:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Partition key Value:&lt;/strong&gt; &lt;code&gt;PRODUCT#laptop-001&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort key Value:&lt;/strong&gt; Begins with ▼ &lt;code&gt;REVIEW&lt;/code&gt;
Click &lt;strong&gt;Run&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Completed · Items returned: 1 · Items scanned: 1 · Efficiency: 100% · RCUs consumed: 0.5&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You'll see only the review items for that product. DynamoDB read exactly the items you asked for.&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;Efficient.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Scan (Expensive)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Switch back to &lt;strong&gt;Scan&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Run&lt;/strong&gt; with no filters&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Completed · Items returned: 5 · Items scanned: 5 · Efficiency: 100% · RCUs consumed: 2&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You'll see ALL items in the table. DynamoDB read every single item. On a table with millions of items.&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;This is slow and expensive.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Click &lt;strong&gt;Add filter&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attribute name: &lt;code&gt;category&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Type: String&lt;/li&gt;
&lt;li&gt;Condition: Equal to&lt;/li&gt;
&lt;li&gt;Value: &lt;code&gt;electronics&lt;/code&gt;
Click &lt;strong&gt;Run&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Completed · Items returned: 2 · Items scanned: 5 · Efficiency: 40% · RCUs consumed: 2&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You'll see only electronics items, but &lt;em&gt;DynamoDB still read the entire table&lt;/em&gt; and filtered afterward. The filter doesn't reduce the read cost.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 &lt;code&gt;FilterExpression&lt;/code&gt; does NOT reduce the amount of data read. It only filters what's returned to you. You still pay for the full scan. To reduce reads, use better key design or GSIs.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Query the GSI&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Switch to &lt;strong&gt;Query&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Change the &lt;strong&gt;Index&lt;/strong&gt; dropdown to &lt;strong&gt;GSI1&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Partition key (GSI1PK) Value:&lt;/strong&gt; &lt;code&gt;CATEGORY#electronics&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort key (GSI1SK) Value:&lt;/strong&gt; Begins with ▼ &lt;code&gt;PRICE#&lt;/code&gt;
Click &lt;strong&gt;Run&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt;: Completed · Items returned: 2 · Items scanned: 2 · Efficiency: 100% · RCUs consumed: 0.5&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This efficiently finds all electronics products, sorted by price. That's the power of a well-designed GSI.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Build the API with Lambda
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Create the Lambda Function
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;ProductCatalogAPI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;Python 3.12&lt;/code&gt;
Click &lt;strong&gt;Create function&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt;: Successfully created the function "ProductCatalogAPI".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;General configuration&lt;/strong&gt; → click Edit&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; &lt;code&gt;256 MB&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout:&lt;/strong&gt; &lt;code&gt;10 seconds&lt;/code&gt;
Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt;Add DynamoDB Permissions&lt;br&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Permissions&lt;/strong&gt; → click the role name&lt;br&gt;
&lt;strong&gt;Add permissions&lt;/strong&gt; → &lt;strong&gt;Attach policies&lt;/strong&gt;&lt;br&gt;
Search for and attach &lt;code&gt;AmazonDynamoDBFullAccess&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;In production, scope this down to only the &lt;code&gt;ProductCatalog&lt;/code&gt; table. For this tutorial, full access keeps things simple. &lt;em&gt;Brevity.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Write the Function Code&lt;/strong&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;decimal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;boto3.dynamodb.conditions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Attr&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize OUTSIDE the handler — reused across warm invocations
&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;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;ProductCatalog&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DecimalEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONEncoder&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 returns Decimal types — this converts them to float for JSON.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Decimal&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;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&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;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Routes requests based on the HTTP method and path.
    Demonstrates query, scan, get_item, and put_item operations.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;httpMethod&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;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&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;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;path_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pathParameters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;query_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;queryStringParameters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/products&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&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;list_products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/products/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;productId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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;get_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/products&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;http_method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&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="nf"&gt;get&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;{}&lt;/span&gt;&lt;span class="sh"&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;create_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/products/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/reviews&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;productId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;2&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;get_reviews&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;else&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;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&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;error&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;Not found&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;response&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&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;Internal server error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_params&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    List products by category using QUERY (efficient).
    Falls back to SCAN if no category is specified (expensive — avoid in production).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# QUERY — efficient, reads only matching items
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Querying products in category: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&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;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;KeyConditionExpression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CATEGORY#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# SCAN — reads entire table, expensive!
&lt;/span&gt;        &lt;span class="c1"&gt;# In production, require a category or use pagination
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WARNING: Scanning entire table — this is expensive!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&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;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;FilterExpression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;METADATA&lt;/span&gt;&lt;span class="sh"&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;response&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="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Items&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;count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Count&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;scannedCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ScannedCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Shows the difference between query and scan
&lt;/span&gt;    &lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Get a single product by ID using GET_ITEM.
    This is the most efficient read — directly accesses one item by its full key.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&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;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PRODUCT#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&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;SK&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;METADATA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;# ConsistentRead=True  # Uncomment for strongly consistent read (2x cost)
&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;item&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;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&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;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Product &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found&lt;/span&gt;&lt;span class="sh"&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;response&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="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_reviews&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Get all reviews for a product using QUERY with sort key condition.
    begins_with on the sort key efficiently finds all reviews.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&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;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;KeyConditionExpression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PRODUCT#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;
            &lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;begins_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;REVIEW#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ScanIndexForward&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;  &lt;span class="c1"&gt;# Sort descending (newest first)
&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;response&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="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;productId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reviews&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Items&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;count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Count&lt;/span&gt;&lt;span class="sh"&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;def&lt;/span&gt; &lt;span class="nf"&gt;create_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Create a new product using PUT_ITEM.
    Also creates the category listing item for efficient category queries.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;productId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;  &lt;span class="c1"&gt;# DynamoDB requires Decimal, not float!
&lt;/span&gt;
    &lt;span class="c1"&gt;# Write the product metadata
&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;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PRODUCT#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&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;SK&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;METADATA&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;GSI1PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CATEGORY#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="si"&gt;}&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;GSI1SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PRICE#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;010.2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Zero-padded for correct sort order
&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="n"&gt;body&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;description&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;description&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&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;active&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;stock&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stock&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# Write the category listing (denormalization for efficient queries)
&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;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CATEGORY#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="si"&gt;}&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;SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PRODUCT#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&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="n"&gt;body&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;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&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;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&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;message&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;Product created&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;productId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&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="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DecimalEncoder&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;Step 05:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "ProductCatalogAPI".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Notice &lt;code&gt;Decimal(str(body['price']))&lt;/code&gt;. DynamoDB does NOT support Python &lt;code&gt;float&lt;/code&gt;. You must use &lt;code&gt;Decimal&lt;/code&gt;. This is a common gotcha.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Test The Function&lt;/strong&gt;&lt;br&gt;
Click the &lt;strong&gt;Test&lt;/strong&gt; tab&lt;br&gt;
Create test events for each operation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;List products by category:&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;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"queryStringParameters"&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="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electronics"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pathParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;Get a single product:&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;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products/laptop-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;"pathParameters"&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="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laptop-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;"queryStringParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;Get reviews:&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;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products/laptop-001/reviews"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pathParameters"&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="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laptop-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;"queryStringParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;Create a product:&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;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;productId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;keyboard-003&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Mechanical Keyboard&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;category&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;electronics&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;price&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:89.99,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;stock&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:100}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pathParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"queryStringParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Run each test and check the results. Look at the &lt;code&gt;count&lt;/code&gt; vs &lt;code&gt;scannedCount&lt;/code&gt; in the list response. When using query, they'll be equal. When scanning, &lt;code&gt;scannedCount&lt;/code&gt; will be higher.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Consistency Models
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;See the Difference&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; In the Lambda function, find the &lt;code&gt;get_product&lt;/code&gt; function&lt;br&gt;
Uncomment the &lt;code&gt;ConsistentRead=True&lt;/code&gt; line&lt;br&gt;
&lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "ProductCatalogAPI".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Now &lt;code&gt;get_product&lt;/code&gt; uses &lt;strong&gt;strongly consistent reads&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always returns the latest data&lt;/li&gt;
&lt;li&gt;Costs &lt;strong&gt;2x&lt;/strong&gt; the read capacity&lt;/li&gt;
&lt;li&gt;Only works on the &lt;strong&gt;base table&lt;/strong&gt; (not GSIs)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 &lt;strong&gt;GSIs only support eventually consistent reads.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;When to Use Each&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Consistency&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Why&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Product catalog browsing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Eventually consistent&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Stale data for a second is fine&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shopping cart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strongly consistent&lt;/td&gt;
&lt;td&gt;&lt;em&gt;User expects to see what they just added&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inventory check before purchase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strongly consistent&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Must be accurate to avoid overselling&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analytics dashboard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Eventually consistent&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Slight delay is acceptable&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Part VI
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;TTL:&lt;/strong&gt; Automatic Data Expiration
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Enable TTL&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; In the &lt;strong&gt;DynamoDB&lt;/strong&gt; console, click on &lt;code&gt;ProductCatalog&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;▼ Actions&lt;/strong&gt; &lt;br&gt;
Click &lt;strong&gt;Turn on TTL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Turn on Time to Live (TTL)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;TTL attribute name:&lt;/strong&gt; &lt;code&gt;ttl&lt;/code&gt;&lt;br&gt;
Click &lt;strong&gt;Turn on TTL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully activated Time to Live for the ProductCatalog table with the ttl attribute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Add Items with TTL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create an item with a TTL value (Unix epoch timestamp):&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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SESSION#user-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;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DATA"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-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;"cartItems"&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="nl"&gt;"L"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laptop-001"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mouse-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;"ttl"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1745625600"&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;blockquote&gt;
&lt;p&gt;The &lt;code&gt;ttl&lt;/code&gt; value is a Unix timestamp. Items are deleted after this time. Use an &lt;a href="https://www.epochconverter.com/" rel="noopener noreferrer"&gt;epoch converter&lt;/a&gt; to set a time a few minutes in the future for testing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 TTL deletion is &lt;strong&gt;eventually consistent:&lt;/strong&gt; items may persist for up to 48 hours after expiration. Don't rely on TTL for exact timing. Always filter out expired items in your queries as a safety measure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part VII
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Caching with DAX
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DAX (DynamoDB Accelerator) is an in-memory cache that sits in front of DynamoDB. It's a drop-in replacement. Same API, just change the client endpoint&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;When to Use DAX&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Scenario&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Use DAX?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read-heavy workload, same items queried repeatedly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsecond response times needed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write-heavy workload&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No (DAX is a read cache)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Need strongly consistent reads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No (DAX returns eventually consistent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Diverse access patterns, rarely same item twice&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No (low cache hit rate)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;How DAX Works&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Without DAX:
  App → DynamoDB (single-digit millisecond reads)

With DAX:
  App → DAX (microsecond reads if cached) → DynamoDB (on cache miss)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Console Walkthrough&lt;/strong&gt; &lt;em&gt;(Don't Create.Just Understand)&lt;/em&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;💸 &lt;strong&gt;DAX requires a VPC and costs money even when idle. We'll walk through the setup without creating it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Click &lt;strong&gt;▼ DAX&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create cluster&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cluster name:&lt;/strong&gt; &lt;code&gt;product-cache&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node type family:&lt;/strong&gt; &lt;code&gt;t-type family&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node type:&lt;/strong&gt; &lt;code&gt;dax.t3.small&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster size:&lt;/strong&gt; &lt;code&gt;3&lt;/code&gt; nodes (for high availability)
Click &lt;strong&gt;Next&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Configure networks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Network Type:&lt;/strong&gt; &lt;code&gt;IPv4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subnet group:&lt;/strong&gt; &lt;code&gt;Create new&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subnet group name:&lt;/strong&gt; &lt;code&gt;MySubnetGroup&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC ID:&lt;/strong&gt; &lt;code&gt;defaultVPC&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subnets:&lt;/strong&gt; &lt;code&gt;Select all&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security group:&lt;/strong&gt; &lt;code&gt;default ▼&lt;/code&gt;
Click &lt;strong&gt;View in EC2 console&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Allow port 8111 from your Lambda functions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Inbound security group rules successfully modified on security group&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Configure Security&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM Service role for DynamoDB access:&lt;/strong&gt; &lt;code&gt;Create new&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM role name:&lt;/strong&gt; &lt;code&gt;DaxToDynamoDB&lt;/code&gt;
Click &lt;strong&gt;Next&lt;/strong&gt; → Click &lt;strong&gt;Next&lt;/strong&gt; → Click &lt;strong&gt;Create cluster&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created the cluster product-cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; In your Lambda code, you'd change one line:&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;# Without DAX
&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="c1"&gt;# With DAX — same API, just different endpoint
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;amazondax&lt;/span&gt;
&lt;span class="n"&gt;dax_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amazondax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AmazonDaxClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;endpoints&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;product-cache.abc123.dax-clusters.us-east-1.amazonaws.com:8111&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;dax_client&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;ProductCatalog&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# All your existing code works unchanged!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DAX is a drop-in replacement for DynamoDB reads. Same API, same code. Just change the client. 💡 But remember: DAX only supports eventually consistent reads and is for read-heavy workloads.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Designed a table around access patterns first&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Access-pattern-driven DynamoDB design&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a composite primary key (PK + SK)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Single-table design, sort key relationships&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Added a Global Secondary Index with different keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;GSI for alternate access patterns&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used overloaded keys (&lt;code&gt;PRODUCT#&lt;/code&gt;, &lt;code&gt;CATEGORY#&lt;/code&gt;, &lt;code&gt;REVIEW#&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Single-table design pattern&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queried by partition key with &lt;code&gt;begins_with&lt;/code&gt; on sort key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Efficient query operations&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ran a scan and compared &lt;code&gt;Count&lt;/code&gt; vs &lt;code&gt;ScannedCount&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Scan is expensive: it reads the entire table&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Added a &lt;code&gt;FilterExpression&lt;/code&gt; to a scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Filters run AFTER reading: don't save capacity&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used &lt;code&gt;Decimal(str(price))&lt;/code&gt; instead of &lt;code&gt;float&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;DynamoDB type system: no float support&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Toggled &lt;code&gt;ConsistentRead=True&lt;/code&gt; on &lt;code&gt;get_item&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Strongly vs eventually consistent reads&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Noted GSIs only support eventual consistency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;GSI limitations&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enabled TTL on a &lt;code&gt;ttl&lt;/code&gt; attribute&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Automatic data lifecycle management&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Walked through DAX setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Read caching for DynamoDB, microsecond latency&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;strong&gt;DynamoDB&lt;/strong&gt; → Delete the &lt;code&gt;ProductCatalog&lt;/code&gt; table&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; &lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;ProductCatalogAPI&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; &lt;strong&gt;IAM&lt;/strong&gt; → Delete the Lambda execution role&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; &lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete the log groups&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Partition key cardinality:&lt;/strong&gt; high cardinality = even distribution = good performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query &amp;gt; Scan:&lt;/strong&gt; always prefer query. Scan reads the entire table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FilterExpression doesn't save reads:&lt;/strong&gt; it filters after reading. Use key design or GSIs instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GSIs&lt;/strong&gt; can be added anytime. &lt;strong&gt;LSIs&lt;/strong&gt; must be created with the table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GSIs are eventually consistent only:&lt;/strong&gt; no strongly consistent reads on GSIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Decimal, not float&lt;/strong&gt; for DynamoDB numbers in Python&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; is free but eventually consistent (up to 48 hours delay)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DAX&lt;/strong&gt; = DynamoDB read cache (microsecond reads). Same API as DynamoDB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DAX doesn't support strongly consistent reads&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-table design&lt;/strong&gt; with overloaded keys is the recommended DynamoDB pattern&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html" rel="noopener noreferrer"&gt;Best practices for designing and architecting with DynamoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html" rel="noopener noreferrer"&gt;Querying tables in DynamoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html" rel="noopener noreferrer"&gt;Using Global Secondary Indexes in DynamoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DAX.html" rel="noopener noreferrer"&gt;In-memory acceleration with DynamoDB Accelerator (DAX)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html" rel="noopener noreferrer"&gt;Using Time to Live (TTL) in DynamoDB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>Develop Code for Lambda | 🏗️ Build A Real-Time Data Processing Pipeline</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Thu, 07 May 2026 20:34:21 +0000</pubDate>
      <link>https://dev.to/aws-builders/develop-code-for-lambda-build-a-real-time-data-processing-pipeline-iii</link>
      <guid>https://dev.to/aws-builders/develop-code-for-lambda-build-a-real-time-data-processing-pipeline-iii</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 1:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Development with AWS Services&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 2:&lt;/strong&gt; &lt;em&gt;Develop Code for Lambda&lt;/em&gt;       &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lambda is the most heavily tested service on the DVA-C02. It is also perhaps, one of the most used and talked about services in general too, well after EC2 and S3. So, you need to know how to configure it, write code for it, handle errors, tune performance, and integrate it with practically every other AWS service.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Lambda Execution Model
&lt;/h3&gt;

&lt;p&gt;When you invoke a Lambda function, AWS:&lt;br&gt;
&lt;strong&gt;1.&lt;/strong&gt; Finds or creates an &lt;em&gt;execution environment&lt;/em&gt; (container)&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Loads your code and initializes it (&lt;em&gt;cold start&lt;/em&gt;)&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Runs your &lt;em&gt;handler function&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; Keeps the environment &lt;em&gt;warm&lt;/em&gt; for reuse (subsequent invocations skip step 2)&lt;/p&gt;

&lt;p&gt;Code outside the handler runs once during cold start and is reused. This is why you initialize SDK clients and database connections outside the handler.&lt;/p&gt;
&lt;h3&gt;
  
  
  Key Configuration Parameters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;128 MB – 10,240 MB&lt;/td&gt;
&lt;td&gt;128 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CPU scales proportionally with memory&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1s – 900s (15 min)&lt;/td&gt;
&lt;td&gt;3s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Max 15 minutes: use Step Functions or ECS for longer or Durable functions&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reserved or Provisioned&lt;/td&gt;
&lt;td&gt;1000/region&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Reserved = guarantee + cap; Provisioned = pre-warmed&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ephemeral storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;512 MB – 10,240 MB&lt;/td&gt;
&lt;td&gt;512 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;/tmp directory for temporary files&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Up to 5&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;250 MB total unzipped (function + layers)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Invocation Types and Error Handling
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Invocation Type&lt;/th&gt;
&lt;th&gt;Source Examples&lt;/th&gt;
&lt;th&gt;Retry Behavior&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Error Destination&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Synchronous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API Gateway, ALB&lt;/td&gt;
&lt;td&gt;No automatic retries caller handles it&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Caller gets the error&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Asynchronous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3, SNS, EventBridge&lt;/td&gt;
&lt;td&gt;2 automatic retries (3 total)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DLQ or Lambda Destinations&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Event source mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SQS, Kinesis, DynamoDB Streams&lt;/td&gt;
&lt;td&gt;Retries until record expires or succeeds&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DLQ (SQS) or on-failure destination&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Lambda Destinations vs DLQ
&lt;/h3&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;DLQ&lt;/th&gt;
&lt;th&gt;Lambda Destinations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Captures success&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Captures failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context included&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Full (request, response, error)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Supported targets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SQS, SNS&lt;/td&gt;
&lt;td&gt;SQS, SNS, Lambda, EventBridge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Works with&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Async invocations&lt;/td&gt;
&lt;td&gt;Async invocations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lambda Destinations are the modern approach and preferred over DLQs. DLQs are still valid for SQS event source mappings.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  VPC Access
&lt;/h3&gt;

&lt;p&gt;By default, Lambda runs in an AWS-managed VPC and can access the internet and public AWS services. When you attach Lambda to your own VPC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Without VPC:  Lambda → Internet → AWS Services ✅  |  Lambda → Private RDS ❌
With VPC:     Lambda → Private RDS ✅  |  Lambda → Internet ❌ (needs NAT Gateway)
              Lambda → VPC Endpoint → AWS Services ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; VPC Lambda needs &lt;em&gt;private subnets&lt;/em&gt; with a &lt;em&gt;NAT Gateway&lt;/em&gt; for internet access&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Use &lt;em&gt;VPC endpoints&lt;/em&gt; (PrivateLink) to access DynamoDB, S3, SQS without NAT&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Use &lt;em&gt;RDS Proxy&lt;/em&gt; for connection pooling because Lambda can overwhelm databases with concurrent connections&lt;/p&gt;
&lt;h3&gt;
  
  
  Cold Starts
&lt;/h3&gt;

&lt;p&gt;A cold start happens when Lambda creates a new execution environment. Strategies to reduce them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;em&gt;Provisioned Concurrency:&lt;/em&gt; pre-warms environments (costs money when idle)&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; &lt;em&gt;Keep deployment packages small:&lt;/em&gt; smaller packages initialize faster&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; &lt;em&gt;Initialize outside the handler:&lt;/em&gt; SDK clients, DB connections, config&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; &lt;em&gt;Use ARM (Graviton2):&lt;/em&gt; often faster and 20% cheaper&lt;/p&gt;
&lt;h3&gt;
  
  
  Kinesis And Lambda Key Settings
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Relevance&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Batch size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Records per invocation (max 10,000)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Larger batches = fewer invocations&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Batch window&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wait time to fill batch (max 300s)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Reduces invocations for low-traffic streams&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Parallelization factor&lt;/strong&gt; (1–10)&lt;/td&gt;
&lt;td&gt;Concurrent batches per shard&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Increases throughput without adding shards&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bisect batch on error&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Splits batch in half on failure&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Isolates the bad record&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Starting position&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LATEST or TRIM_HORIZON&lt;/td&gt;
&lt;td&gt;&lt;em&gt;LATEST = new only. TRIM_HORIZON = from beginning&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  🏗️ Build A Real Time Data Processing Pipeline
&lt;/h2&gt;

&lt;p&gt;Now let's put these concepts into practice by building a &lt;strong&gt;Real-Time Data Processing Pipeline&lt;/strong&gt; using Lambda:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Lambda function with VPC access connecting to an RDS database&lt;/li&gt;
&lt;li&gt;Lambda layers for shared dependencies&lt;/li&gt;
&lt;li&gt;A DLQ (Dead Letter Queue) and Lambda Destinations for error handling&lt;/li&gt;
&lt;li&gt;A Kinesis stream processor that transforms data in near real time&lt;/li&gt;
&lt;li&gt;Performance tuning with memory configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This covers all the skills for this task: Lambda configuration, VPC access, error handling, event lifecycle, integrations, and performance tuning.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;&lt;strong&gt;An AWS account&lt;/strong&gt;&lt;/a&gt; &lt;em&gt;(free tier covers most of this)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create and Configure a Lambda Function
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create the Function&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02: **Click **Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Create function&lt;/strong&gt;&lt;br&gt;
Choose &lt;strong&gt;Author from scratch&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;DataProcessor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.12&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created the function "DataProcessor".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Before writing code, let's walk through the configuration tabs.&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Click the &lt;strong&gt;Configuration&lt;/strong&gt; tab.&lt;br&gt;
&lt;strong&gt;General configuration&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Edit&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; 256 MB &lt;em&gt;(default is 128 MB)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ephemeral storage:&lt;/strong&gt; 512 MB &lt;em&gt;(default)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout:&lt;/strong&gt; 0 min 30 sec &lt;em&gt;(default is 3 seconds)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lambda allocates CPU proportionally to memory. At 1,769 MB you get one full vCPU. Increasing memory from 128 MB to 256 MB doubles your CPU therefore your function might run in half the time, costing the same or less.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Environment variables&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Environment variables&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt; → &lt;strong&gt;Add environment variable&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key: &lt;code&gt;LOG_LEVEL&lt;/code&gt;, Value: &lt;code&gt;INFO&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Key: &lt;code&gt;STAGE&lt;/code&gt;, Value: &lt;code&gt;dev&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "DataProcessor".&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Access environment variables in your code
&lt;/span&gt;&lt;span class="n"&gt;log_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LOG_LEVEL&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;INFO&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;stage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;STAGE&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;dev&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lambda encrypts all environment variables at rest by default using an AWS managed KMS key. For extra security, you can use a customer managed KMS key and decrypt in your code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a Lambda Layer
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Layers let you share code and libraries across multiple functions. Let's create one with a utility module.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Build and Upload a Layer&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Layers must be uploaded as a zip file. &lt;br&gt;
We'll use &lt;strong&gt;AWS CloudShell:&lt;/strong&gt; a browser-based terminal built into the AWS Console so that you don't need anything installed locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open &lt;strong&gt;CloudShell&lt;/strong&gt; by clicking the terminal icon (&lt;code&gt;&amp;gt;_&lt;/code&gt;) in the top navigation bar of the AWS Console next to the search bar&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Wait for it to initialize as it might take a few seconds the first time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt;&lt;br&gt;
Run these commands:&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="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; python
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; python/utils.py &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
"""
Shared utilities for Lambda functions.
This module is packaged as a Lambda Layer so multiple functions can use it.
"""
import json
import time
import random

def retry_with_backoff(func, max_retries=3, base_delay=0.5):
    """Retry with exponential backoff and jitter."""
    for attempt in range(max_retries + 1):
        try:
            return func()
        except Exception as e:
            if attempt == max_retries:
                raise
            delay = base_delay * (2 ** attempt)
            jitter = random.uniform(0, delay)
            time.sleep(delay + jitter)

def format_response(status_code, body):
    """Standard API Gateway response format."""
    return {
        'statusCode': status_code,
        'headers': {'Content-Type': 'application/json'},
        'body': json.dumps(body, default=str)
    }
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;zip &lt;span class="nt"&gt;-r&lt;/span&gt; utils-layer.zip python/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Download the zip&lt;br&gt;
Click &lt;strong&gt;Actions ▼&lt;/strong&gt; (top right of CloudShell) → &lt;strong&gt;Download file&lt;/strong&gt; → type &lt;code&gt;utils-layer.zip&lt;/code&gt; → click &lt;strong&gt;Download&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why the &lt;code&gt;python/&lt;/code&gt; folder?&lt;/strong&gt; Lambda layers must follow a specific directory structure. For Python, your code must be inside a &lt;code&gt;python/&lt;/code&gt; folder in the zip. Lambda adds this path to &lt;code&gt;sys.path&lt;/code&gt; automatically so your functions can &lt;code&gt;import utils&lt;/code&gt; directly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Upload the Layer via Console&lt;br&gt;
In the Lambda console, click &lt;strong&gt;Layers&lt;/strong&gt; in the left sidebar&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Click &lt;strong&gt;Create layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;shared-utils&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload:&lt;/strong&gt; Choose the &lt;code&gt;utils-layer.zip&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatible runtimes - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; Python 3.12
Click &lt;strong&gt;Create&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created layer shared-utils version 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; &lt;strong&gt;Attach the Layer to Your Function&lt;/strong&gt;&lt;br&gt;
Go back to the &lt;code&gt;DataProcessor&lt;/code&gt; function&lt;br&gt;
Scroll down to the &lt;strong&gt;Layers&lt;/strong&gt; section&lt;br&gt;
Click &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; &lt;strong&gt;Edit layers&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Add a layer&lt;/strong&gt;&lt;br&gt;
Choose &lt;strong&gt;Custom layers&lt;/strong&gt;&lt;br&gt;
Select &lt;code&gt;shared-utils&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Version:&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt;&lt;br&gt;
Click &lt;strong&gt;Add&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "DataProcessor".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Now you can use it in your function code:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;format_response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retry_with_backoff&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="nf"&gt;format_response&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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 DataProcessor&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;blockquote&gt;
&lt;p&gt;A function can have up to &lt;strong&gt;5 layers&lt;/strong&gt;. The total unzipped size of the function + all layers can't exceed &lt;strong&gt;250 MB&lt;/strong&gt;. Layers are versioned and immutable. Each upload creates a new version.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Error Handling:&lt;/strong&gt; DLQ and Lambda Destinations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create a Dead Letter Queue&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;SQS&lt;/strong&gt; console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create queue&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; Standard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;data-processor-dlq&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create queue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Queue data-processor-dlq created successfully&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Copy the &lt;strong&gt;Queue ARN&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Create a Success Queue (for Destinations)&lt;br&gt;
Create another queue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;data-processor-success&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copy the &lt;strong&gt;Queue ARN&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Lambda&lt;/strong&gt; → DataProcessor → Configuration → Permissions → Click the &lt;strong&gt;Role name&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Add permissions&lt;/strong&gt; → &lt;code&gt;Attach policies&lt;/code&gt;&lt;br&gt;
Search for &lt;code&gt;AmazonSQSFullAccess&lt;/code&gt; and attach it&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Configure the DLQ on the Lambda Function&lt;br&gt;
Go back to the &lt;code&gt;DataProcessor&lt;/code&gt; function&lt;br&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; tab → &lt;strong&gt;Asynchronous invocation&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maximum age of event:&lt;/strong&gt; &lt;code&gt;1 h 0 min 0 sec&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry attempts:&lt;/strong&gt; &lt;code&gt;2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead-letter queue service ▼:&lt;/strong&gt; Select &lt;code&gt;Amazon SQS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue ▼:&lt;/strong&gt; Select &lt;code&gt;data-processor-dlq&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "DataProcessor".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Destinations are the modern approach. They capture both success AND failure.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Configure Lambda Destinations&lt;br&gt;
Still in &lt;strong&gt;Asynchronous invocation&lt;/strong&gt;, find the &lt;strong&gt;Destinations&lt;/strong&gt; section&lt;br&gt;
Click &lt;strong&gt;Add destination&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; Add destination&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;code&gt;Asynchronous invocation&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Condition:&lt;/strong&gt; &lt;code&gt;On success&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination type:&lt;/strong&gt; &lt;code&gt;SQS queue&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination:&lt;/strong&gt; &lt;code&gt;data-processor-success&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Your changes have been saved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Click &lt;strong&gt;Add destination&lt;/strong&gt; again&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;code&gt;Asynchronous invocation&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Condition:&lt;/strong&gt; &lt;code&gt;On failure&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination type:&lt;/strong&gt; &lt;code&gt;SQS queue&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination:&lt;/strong&gt; &lt;code&gt;data-processor-dlq&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Your changes have been saved.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lambda Destinations are preferred over DLQs because:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Destinations capture &lt;strong&gt;both&lt;/strong&gt; success and failure (DLQ only captures failure)&lt;/li&gt;
&lt;li&gt;Destinations include more context (request payload, response, error details)&lt;/li&gt;
&lt;li&gt;Destinations work with async invocations only (same as DLQ)&lt;/li&gt;
&lt;li&gt;DLQs are still valid for SQS event source mappings&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Test Error Handling&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Let's create a function that sometimes fails to see the error handling in action:&lt;/p&gt;

&lt;p&gt;Deploy this code, then test it&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    This function randomly fails to demonstrate error handling.
    When invoked asynchronously:
    1. First attempt fails → Lambda retries
    2. Second attempt fails → Lambda retries again
    3. Third attempt fails → Event goes to DLQ / failure destination
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&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;process&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Always fail — will end up in DLQ after 3 attempts
&lt;/span&gt;        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Simulated failure for testing DLQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;random&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# 50% chance of failure
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Random failure occurred&lt;/span&gt;&lt;span class="sh"&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="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="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;Processed successfully&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;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&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;Step 11:&lt;/strong&gt; Click &lt;strong&gt;Test&lt;/strong&gt; tab&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 12:&lt;/strong&gt; &lt;strong&gt;Test event&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test event action:&lt;/strong&gt; &lt;code&gt;Create new event&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invocation type:&lt;/strong&gt; &lt;code&gt;Synchronous&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event name:&lt;/strong&gt; &lt;code&gt;TestFailure&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Even JSON:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&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;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fail"&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;Click &lt;strong&gt;Test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Executing function:&lt;/strong&gt; failed&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The function will fail. Since this is a synchronous test invocation, you'll see the error immediately. To test the DLQ flow, you need an &lt;strong&gt;asynchronous&lt;/strong&gt; invocation (from S3, SNS, or EventBridge).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lambda with VPC Access
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When Lambda needs to access private resources like RDS or ElastiCache, you attach it to a VPC. Let's walk through the configuration.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Understanding VPC Access&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Without VPC:
  Lambda → Internet → AWS Services (DynamoDB, S3, SQS)  ✅
  Lambda → Private RDS                                    ❌

With VPC:
  Lambda → VPC → Private RDS                              ✅
  Lambda → VPC → Internet                                 ❌ (no NAT)
  Lambda → VPC → NAT Gateway → Internet                   ✅
  Lambda → VPC → VPC Endpoint → AWS Services              ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Configure VPC Access (Console Walkthrough)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;code&gt;DataProcessor&lt;/code&gt; function&lt;br&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; tab → &lt;strong&gt;VPC&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Edit VPC&lt;/strong&gt;&lt;br&gt;
Select the default VPC&lt;br&gt;
Select &lt;strong&gt;at least 2 private subnets&lt;/strong&gt; (for high availability)&lt;br&gt;
Select or create a &lt;strong&gt;security group&lt;/strong&gt; that allows outbound traffic&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️Important:&lt;/strong&gt; The Lambda execution role needs the &lt;code&gt;AWSLambdaVPCAccessExecutionRole&lt;/code&gt; managed policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Connection Reuse Pattern&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When connecting to databases from Lambda, initialize the connection &lt;strong&gt;outside&lt;/strong&gt; the handler:&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&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;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="c1"&gt;# This runs ONCE during cold start, then reuses across warm invocations
# This is critical for database connections — you don't want to open
# a new connection on every single invocation
&lt;/span&gt;&lt;span class="n"&gt;db_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_HOST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# In a real app, you'd initialize your database connection here
# connection = pymysql.connect(host=db_host, ...)
&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    The handler runs on every invocation.
    The connection above is reused across warm invocations.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Use the connection...
&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="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;Connected to database&lt;/span&gt;&lt;span class="sh"&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;blockquote&gt;
&lt;p&gt;Always initialize SDK clients and database connections &lt;strong&gt;outside&lt;/strong&gt; the handler. This code runs once during the cold start and is reused for subsequent warm invocations. &lt;br&gt;
For RDS specifically, use &lt;strong&gt;RDS Proxy&lt;/strong&gt; to manage connection pooling.  Lambda can open hundreds of connections simultaneously, which can overwhelm a database.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Process Streaming Data with Kinesis
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Let's build a real-time clickstream processor.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create a Kinesis Data Stream&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Kinesis&lt;/strong&gt; console&lt;br&gt;
Click &lt;strong&gt;Create data stream&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data stream name:&lt;/strong&gt; &lt;code&gt;clickstream&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capacity mode:&lt;/strong&gt; &lt;code&gt;On-demand&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create data stream&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Data stream clickstream successfully created&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Create the Stream Processor Function&lt;br&gt;
Go to &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;Create function&lt;/strong&gt;&lt;br&gt;
Configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;ClickstreamProcessor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.12&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; 512 MB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout:&lt;/strong&gt; 60 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Paste this code&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Processes clickstream records from Kinesis in near real time.

    Key concepts for the exam:
    - Kinesis data is base64 encoded
    - Records come in batches
    - The partition key determines which shard receives the record
    - Use a high-cardinality partition key (like userId) for even distribution
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Received &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; records from Kinesis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Kinesis data is base64 encoded — decode it
&lt;/span&gt;            &lt;span class="n"&gt;raw_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kinesis&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;data&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Extract clickstream fields
&lt;/span&gt;            &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;anonymous&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;page&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;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&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;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

            &lt;span class="c1"&gt;# Process the click event
&lt;/span&gt;            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Click: user=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, page=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, action=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, time=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# In a real app, you'd:
&lt;/span&gt;            &lt;span class="c1"&gt;# - Aggregate metrics
&lt;/span&gt;            &lt;span class="c1"&gt;# - Write to DynamoDB or S3
&lt;/span&gt;            &lt;span class="c1"&gt;# - Trigger alerts for specific patterns
&lt;/span&gt;
            &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to process record: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Batch complete: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;processed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; processed, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; failed&lt;/span&gt;&lt;span class="sh"&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="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="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;processed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;failed&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;Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "ClickstreamProcessor".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Add Permissions&lt;/strong&gt;&lt;br&gt;
The Lambda execution role needs permission to read from Kinesis:&lt;br&gt;
Go to &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Permissions&lt;/strong&gt; → click the role name&lt;br&gt;
&lt;strong&gt;Add permissions&lt;/strong&gt; → &lt;strong&gt;Attach policies&lt;/strong&gt;&lt;br&gt;
Search for and attach &lt;code&gt;AmazonKinesisReadOnlyAccess&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Connect Kinesis to Lambda&lt;br&gt;
In the &lt;code&gt;ClickstreamProcessor&lt;/code&gt; function, click &lt;strong&gt;Add trigger&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Add trigger&lt;br&gt;
&lt;strong&gt;Select a source ▼:&lt;/strong&gt; &lt;code&gt;Kinesis&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kinesis stream:&lt;/strong&gt; &lt;code&gt;clickstream&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch size:&lt;/strong&gt; &lt;code&gt;100&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starting position:&lt;/strong&gt; Latest&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch window:&lt;/strong&gt; &lt;code&gt;5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-failure destination:&lt;/strong&gt; ``&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry attempts:&lt;/strong&gt; &lt;code&gt;3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split batch on error:&lt;/strong&gt; &lt;code&gt;✔ enabled&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent batches per shard:&lt;/strong&gt; &lt;code&gt;10&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Add&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; The trigger clickstream was successfully added to function ClickstreamProcessor.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Kinesis + Lambda settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batch size:&lt;/strong&gt; how many records per invocation (max 10,000)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch window:&lt;/strong&gt; how long to wait to fill the batch (max 300s)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelization factor:&lt;/strong&gt; (1–10) process multiple batches per shard concurrently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bisect batch on error:&lt;/strong&gt; splits the batch in half on failure to isolate the bad record&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starting position:&lt;/strong&gt; LATEST (new records only) or TRIM_HORIZON (from the beginning)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; &lt;strong&gt;Test with Sample Data&lt;/strong&gt;&lt;br&gt;
Send test records to the stream:&lt;br&gt;
Open the &lt;strong&gt;Kinesis&lt;/strong&gt; console → click &lt;code&gt;clickstream&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; &lt;strong&gt;clickstream&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Data viewer&lt;/strong&gt; tab&lt;br&gt;
Or use the &lt;strong&gt;AWS CLI&lt;/strong&gt; to send test data:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;`shell&lt;br&gt;
aws kinesis put-record \&lt;br&gt;
  --stream-name clickstream \&lt;br&gt;
  --partition-key "USER-001" \&lt;br&gt;
  --data '{"userId":"USER-001","page":"/products/laptop","action":"view","timestamp":"2026-04-24T10:00:00Z"}'&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Check the &lt;code&gt;ClickstreamProcessor&lt;/code&gt; CloudWatch logs to see the processed records&lt;/p&gt;




&lt;h2&gt;
  
  
  Part VI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Performance Tuning
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Memory vs Duration Experiment&lt;/strong&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's see how memory affects performance:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;code&gt;ClickstreamProcessor&lt;/code&gt; function&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;General configuration&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;br&gt;
Set &lt;strong&gt;Memory&lt;/strong&gt; to &lt;strong&gt;128 MB&lt;/strong&gt; → &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Run a test → note the &lt;strong&gt;Duration&lt;/strong&gt; and &lt;strong&gt;Max Memory Used&lt;/strong&gt; in the execution results&lt;/p&gt;

&lt;p&gt;&lt;code&gt;`json&lt;br&gt;
{&lt;br&gt;
  "Records": [&lt;br&gt;
    {&lt;br&gt;
      "kinesis": {&lt;br&gt;
        "data": "eyJ1c2VySWQiOiJVU0VSLTAwMSIsInBhZ2UiOiIvcHJvZHVjdHMvbGFwdG9wIiwiYWN0aW9uIjoidmlldyIsInRpbWVzdGFtcCI6IjIwMjYtMDQtMjRUMTA6MDA6MDBaIn0="&lt;br&gt;
      },&lt;br&gt;
      "eventSource": "aws:kinesis",&lt;br&gt;
      "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789012:stream/clickstream"&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
      "kinesis": {&lt;br&gt;
        "data": "eyJ1c2VySWQiOiJVU0VSLTAwMiIsInBhZ2UiOiIvY2hlY2tvdXQiLCJhY3Rpb24iOiJjbGljayIsInRpbWVzdGFtcCI6IjIwMjYtMDQtMjRUMTA6MDE6MDBaIn0="&lt;br&gt;
      },&lt;br&gt;
      "eventSource": "aws:kinesis",&lt;br&gt;
      "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789012:stream/clickstream"&lt;br&gt;
    }&lt;br&gt;
  ]&lt;br&gt;
}&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Change memory to &lt;strong&gt;256 MB&lt;/strong&gt; → test again&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Change to &lt;strong&gt;512 MB&lt;/strong&gt; → test again&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Change to &lt;strong&gt;1024 MB&lt;/strong&gt; → test again&lt;/p&gt;

&lt;p&gt;You'll typically see:&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;Memory&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;Billed Duration&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Cost per Invocation&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;128 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~200ms&lt;/td&gt;
&lt;td&gt;200ms&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Low per-ms, but slow&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;256 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~110ms&lt;/td&gt;
&lt;td&gt;110ms&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Sweet spot for many functions&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;512 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~60ms&lt;/td&gt;
&lt;td&gt;60ms&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Faster, slightly more per-ms&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1024 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~55ms&lt;/td&gt;
&lt;td&gt;55ms&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Diminishing returns&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The sweet spot is where increasing memory no longer significantly reduces duration.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Concurrency Settings&lt;br&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Concurrency and recursion detection&lt;/strong&gt; &lt;br&gt;
&lt;strong&gt;Concurrency&lt;/strong&gt;→ &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; &lt;strong&gt;Edit concurrency&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;USe unreserved account concurrency:&lt;/strong&gt; Uses the shared regional pool (default 1000)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reserve concurrency:&lt;/strong&gt; Guarantees capacity AND caps the function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Select &lt;strong&gt;Reserve concurrency:&lt;/strong&gt; &lt;code&gt;50&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Your changes have been saved.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Your function is guaranteed 50 concurrent executions&lt;/li&gt;
&lt;li&gt;It can never exceed 50 (acts as a throttle)&lt;/li&gt;
&lt;li&gt;The remaining 950 are available for other functions&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Reserved concurrency is free and serves two purposes&lt;/strong&gt;: guaranteeing capacity AND protecting downstream services from being overwhelmed. Provisioned concurrency costs money even when idle but eliminates cold starts.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Did&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a Lambda function and configured memory/timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Lambda configuration parameters&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built and attached a Lambda Layer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Sharing code across functions, layer structure and limits&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Set up a DLQ and Lambda Destinations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Async error handling, event lifecycle&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used &lt;code&gt;--invocation-type Event&lt;/code&gt; to trigger async flow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Synchronous vs asynchronous invocation types&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attached Lambda to a VPC with subnets and security group&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;VPC access for private resources (RDS, ElastiCache)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Initialized SDK clients outside the handler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Connection reuse, cold start optimization&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a Kinesis stream and connected it to Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Real-time data processing, event source mappings&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configured batch size, batch window, parallelization factor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Kinesis + Lambda tuning for throughput&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enabled bisect batch on error&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Isolating bad records in stream processing&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Changed memory settings and compared Duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Performance tuning, memory = CPU relationship&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Set reserved concurrency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Throttling, capacity guarantees, protecting downstream services&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;DataProcessor&lt;/code&gt;, &lt;code&gt;ClickstreamProcessor&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; &lt;strong&gt;Lambda Layers&lt;/strong&gt; → Delete &lt;code&gt;shared-utils&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; &lt;strong&gt;Kinesis&lt;/strong&gt; → Delete &lt;code&gt;clickstream&lt;/code&gt; stream&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; &lt;strong&gt;SQS&lt;/strong&gt; → Delete &lt;code&gt;data-processor-dlq&lt;/code&gt;, &lt;code&gt;data-processor-success&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; &lt;strong&gt;IAM&lt;/strong&gt; → Delete the Lambda execution roles&lt;br&gt;
&lt;strong&gt;6.&lt;/strong&gt; &lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete the log groups&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Memory = CPU:&lt;/strong&gt; more memory means more CPU. &lt;em&gt;Find the sweet spot where cost and performance balance.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initialize outside the handler:&lt;/strong&gt; SDK clients, DB connections, config loading. &lt;em&gt;Reused across warm invocations.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Destinations &amp;gt; DLQ:&lt;/strong&gt; Destinations capture success AND failure with more context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ReportBatchItemFailures&lt;/strong&gt; for SQS, &lt;strong&gt;BisectBatchOnFunctionError&lt;/strong&gt; for Kinesis to isolate bad records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Lambda loses internet:&lt;/strong&gt; needs NAT Gateway or VPC endpoints for AWS services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS Proxy&lt;/strong&gt; for Lambda-to-RDS: manages connection pooling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layers:&lt;/strong&gt; up to 5 per function, 250 MB total unzipped, versioned and immutable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;15-minute timeout&lt;/strong&gt; is the max: for longer tasks, use Step Functions or ECS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provisioned concurrency:&lt;/strong&gt; eliminates cold starts but costs money when idle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kinesis parallelization factor"&lt;/strong&gt; (1–10) lets you process multiple batches per shard.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" rel="noopener noreferrer"&gt;What Is AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html" rel="noopener noreferrer"&gt;Using Lambda to process records from Amazon Kinesis Data Streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-function-common.html" rel="noopener noreferrer"&gt;Configuring AWS Lambda functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html" rel="noopener noreferrer"&gt;Best practices for working with AWS Lambda functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-concurrency.html" rel="noopener noreferrer"&gt;Configuring reserved concurrency for a function&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>Develop Code for Applications Hosted on AWS | 🏗️ Build An Order Processing System</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Mon, 04 May 2026 20:51:50 +0000</pubDate>
      <link>https://dev.to/aws-builders/develop-code-for-applications-hosted-on-aws-build-an-order-processing-system-38jc</link>
      <guid>https://dev.to/aws-builders/develop-code-for-applications-hosted-on-aws-build-an-order-processing-system-38jc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 1:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Development with AWS Services&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 1:&lt;/strong&gt; &lt;em&gt;Develop Code for Applications Hosted on AWS&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the &lt;strong&gt;broadest&lt;/strong&gt; task on the exam. It tests whether you can build real applications on AWS. Not just use the console, but write code that interacts with AWS services, write code that handles failures gracefully, and write code that follows modern architectural patterns.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Architectural Patterns
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Event-Driven&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Event-Driven&lt;/strong&gt; Components communicate through events. A producer emits an event, and one or more consumers react to it. There is no direct coupling between the producer and the consumer. &lt;br&gt;
&lt;strong&gt;AWS Services:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EventBridge&lt;/li&gt;
&lt;li&gt;SNS &lt;/li&gt;
&lt;li&gt;SQS &lt;/li&gt;
&lt;li&gt;Kinesis&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Microservices&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In &lt;strong&gt;Microservices&lt;/strong&gt; Architecture, the application is split into small, independently deployable services. Each service owns its data and communicates via APIs or events.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Monolithic&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;Monolithic&lt;/strong&gt; Application is a single deployable unit. Even though it is simpler to start with, it is much harder to scale independently. Knowing when and even how to migrate from Monolithic Applications to Microservices Architecture is a must.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Choreography vs Orchestration&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choreography:&lt;/strong&gt; Each service knows what to do when it receives an event. No central coordinator. Uses EventBridge or SNS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orchestration:&lt;/strong&gt; A central coordinator such as Step Functions manages the workflow and tells each service what to do.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Fanout&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In a &lt;strong&gt;Fanout&lt;/strong&gt; Pattern, one event triggers multiple consumers in parallel whereas the classic pattern consists of an SNS topic with multiple SQS queue subscriptions, or EventBridge with multiple rule targets.&lt;/p&gt;
&lt;h3&gt;
  
  
  Stateful vs Stateless
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Stateful&lt;/th&gt;
&lt;th&gt;Stateless&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Session Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stored locally (memory/disk)&lt;/td&gt;
&lt;td&gt;Externalized (DynamoDB, ElastiCache)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard to scale horizontally&lt;/td&gt;
&lt;td&gt;Easy to scale horizontally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure Impact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Session data lost on crash&lt;/td&gt;
&lt;td&gt;No data loss, any instance can serve any request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not possible (ephemeral by design)&lt;/td&gt;
&lt;td&gt;Default behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lambda functions are stateless by design. If you need state, externalize it to DynamoDB, ElastiCache, or S3.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Tightly Coupled vs Loosely Coupled
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Tightly Coupled&lt;/th&gt;
&lt;th&gt;Loosely Coupled&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Communication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct API calls&lt;/td&gt;
&lt;td&gt;Queues, events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure Impact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cascading failures&lt;/td&gt;
&lt;td&gt;Isolated failures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scale together&lt;/td&gt;
&lt;td&gt;Scale independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Synchronous Lambda-to-Lambda&lt;/td&gt;
&lt;td&gt;Lambda → SQS → Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If a scenario describes a system where one component's failure brings down others, the answer usually involves adding a queue &lt;em&gt;(SQS)&lt;/em&gt; or event bus &lt;em&gt;(EventBridge)&lt;/em&gt; between them.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Synchronous vs Asynchronous
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Synchronous&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Caller waits for a response. &lt;br&gt;
&lt;em&gt;API Gateway → Lambda&lt;/em&gt; is &lt;strong&gt;synchronous.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Asynchronous&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Caller sends a message and moves on. &lt;br&gt;
&lt;em&gt;S3 event → Lambda&lt;/em&gt; is &lt;strong&gt;asynchronous.&lt;/strong&gt; &lt;br&gt;
&lt;em&gt;SQS → Lambda&lt;/em&gt; is &lt;strong&gt;asynchronous.&lt;/strong&gt;&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;# Synchronous: Caller Waits
&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;lambda_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-function&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;InvocationType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RequestResponse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;# synchronous
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Asynchronous: Fire &amp;amp; Forget
&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;lambda_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-function&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;InvocationType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Event&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;# asynchronous
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Messaging Services Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Pattern&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Point-to-point queue&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Decoupling, one consumer, buffering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SNS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Pub/sub fanout&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;One message to many subscribers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EventBridge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Event bus with routing&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Complex routing, content-based filtering, cross-account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kinesis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Real-time streaming&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;High-throughput ordered data (clickstreams, IoT, logs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SQS long polling&lt;/strong&gt; (&lt;code&gt;WaitTimeSeconds &amp;gt; 0&lt;/code&gt;) reduces empty responses and costs. Always use it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Resilient Code Patterns
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Exponential Backoff with Jitter&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;When a request to another service fails, retrying immediately often makes things worse.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Exponential Backoff&lt;/strong&gt; Algorithm increases the wait time after each failed attempt (for example: &lt;code&gt;1s → 2s → 4s → 8s&lt;/code&gt;). This gives the failing system time to recover instead of being overwhelmed by repeated traffic.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Jitter&lt;/strong&gt; adds randomness to the wait time so multiple clients don’t retry at the exact same moment. Without it, many clients can retry together and create a &lt;strong&gt;thundering herd problem&lt;/strong&gt;, overwhelming the service again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Exponential Backoff Matters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces pressure on struggling services
&lt;/li&gt;
&lt;li&gt;Improves recovery success rates
&lt;/li&gt;
&lt;li&gt;Prevents retry storms
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Circuit Breaker&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;Circuit Breaker&lt;/strong&gt; protects your application from repeatedly calling a service that is already failing.&lt;/p&gt;

&lt;p&gt;After a defined number of consecutive failures (for example, &lt;code&gt;5&lt;/code&gt; failed requests), the circuit &lt;strong&gt;opens&lt;/strong&gt; and temporarily blocks new requests to that service for a cooldown period.&lt;/p&gt;

&lt;p&gt;After the cooldown, the system allows a few test requests (&lt;strong&gt;half-open state&lt;/strong&gt;) to check if the service has recovered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If successful:&lt;/strong&gt; the circuit closes and normal traffic resumes
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If failures continue:&lt;/strong&gt;, the circuit stays open
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as &lt;strong&gt;failing fast&lt;/strong&gt; instead of &lt;em&gt;failing repeatedly.&lt;/em&gt; A rare act of discipline in software engineering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Circuit Breakers Matter:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents wasted resources
&lt;/li&gt;
&lt;li&gt;Avoids cascading failures
&lt;/li&gt;
&lt;li&gt;Helps systems recover faster
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Idempotency&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;An operation is &lt;strong&gt;Idempotent&lt;/strong&gt; if performing it multiple times produces the same result as performing it once.&lt;/p&gt;

&lt;p&gt;This is important in distributed systems because retries can happen due to timeouts, network failures, or duplicate messages.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updating a user’s email to the same value multiple times &lt;em&gt;is safe.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Creating the same order multiple times &lt;strong&gt;is dangerous&lt;/strong&gt; unless protected
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Idempotency is often implemented using:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Unique request IDs&lt;br&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Deduplication checks&lt;br&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Database constraints &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Idempotency Matters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes retries safe
&lt;/li&gt;
&lt;li&gt;Prevents duplicate side effects
&lt;/li&gt;
&lt;li&gt;Improves consistency in unreliable systems
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🏗️ Build An Order Processing System
&lt;/h2&gt;

&lt;p&gt;Now let's put these concepts into practice by building an &lt;strong&gt;Order Processing System&lt;/strong&gt; from scratch using the AWS Console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An API Gateway REST API that accepts orders&lt;/li&gt;
&lt;li&gt;A Lambda function that publishes order events&lt;/li&gt;
&lt;li&gt;An EventBridge custom event bus that routes events&lt;/li&gt;
&lt;li&gt;Two consumer Lambda functions (order processing + inventory)&lt;/li&gt;
&lt;li&gt;An SQS queue for decoupling&lt;/li&gt;
&lt;li&gt;Request validation on the API&lt;/li&gt;
&lt;li&gt;Error handling with retry logic&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This covers the key skills for this task: architectural patterns, loose coupling, event-driven design, APIs, messaging, and resilient code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;&lt;strong&gt;An AWS account&lt;/strong&gt;&lt;/a&gt; &lt;em&gt;(free tier covers everything here)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Web Browser&lt;/strong&gt; &lt;em&gt;(we're doing this entirely in the console)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ntombizakhona.medium.com/python-in-the-cloud-1462f61e3293" rel="noopener noreferrer"&gt;&lt;strong&gt;Basic familiarity with Python&lt;/strong&gt;&lt;/a&gt; &lt;em&gt;(we'll use Python 3.12 for Lambda)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Understanding the Architecture
&lt;/h3&gt;

&lt;p&gt;Before we build, let's understand what we're building and why.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client (POST /orders)
    │
    ▼
API Gateway (validates request)
    │
    ▼
PublishOrder Lambda (publishes event)
    │
    ▼
EventBridge (custom event bus: "orders")
    │
    ├──► Rule: "OrderPlaced" ──► ProcessOrder Lambda
    │
    └──► Rule: "OrderPlaced" ──► SQS Queue ──► UpdateInventory Lambda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Why This Architecture?&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; validates requests before they reach your code which saves compute costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge&lt;/strong&gt; decouples the publisher from consumers because the publisher doesn't know or care who's listening&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS&lt;/strong&gt; between EventBridge and the inventory function adds durability so if inventory processing fails, the message stays in the queue&lt;/li&gt;
&lt;li&gt;Each component can &lt;strong&gt;scale independently&lt;/strong&gt; and &lt;strong&gt;fail independently&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a &lt;strong&gt;loosely coupled, event-driven, asynchronous&lt;/strong&gt; architecture &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Exam Concepts Demonstrated&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concept&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Where You'll See It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Event-Driven Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;EventBridge routing events to consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fanout Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One event triggers two different consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loose Coupling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Publisher doesn't know about consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Async Processing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;EventBridge + SQS = fire and forget&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Choreography&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each service reacts to events independently (no central orchestrator)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Request Validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API Gateway validates before Lambda runs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Create the EventBridge Custom Event Bus&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We start with the event bus because it's the backbone of our system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Amazon EventBridge&lt;/strong&gt; console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; In the left sidebar under &lt;strong&gt;▼ Buses&lt;/strong&gt;, click &lt;strong&gt;Event buses&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Click &lt;strong&gt;Create event bus&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Create event bus&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;orders&lt;/code&gt;&lt;br&gt;
Click &lt;strong&gt;Create&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created event bus orders.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You now have a custom event bus called &lt;code&gt;orders&lt;/code&gt;. AWS services publish to the &lt;code&gt;default&lt;/code&gt; bus and your application events go to your custom bus.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;default&lt;/code&gt; event bus receives events from AWS services (EC2 state changes, etc.). Custom event buses are for your application events. You can have rules on both.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Create the Order Processing Lambda Functions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We need three Lambda functions. Let's create them one at a time.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Function 1&lt;/strong&gt; | Create the PublishOrder Function
&lt;/h4&gt;

&lt;p&gt;This function receives the API request and publishes an event to EventBridge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; console &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Create function&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;strong&gt;Author from scratch&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;PublishOrder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;Python 3.12&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created the function "PublishOrder".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Paste this code into the code editor:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="n"&gt;eventbridge&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;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;events&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Receives an order from API Gateway and publishes it to EventBridge.
    This function doesn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t process the order — it just routes it.
    That&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the event-driven pattern: publish and forget.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Parse the request body from API Gateway
&lt;/span&gt;        &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&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="nf"&gt;get&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;{}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Publish the event to our custom event bus
&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;eventbridge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&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;Source&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;orders.api&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;DetailType&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;OrderPlaced&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;Detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORD-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&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;customerId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&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;items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&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;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&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;EventBusName&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;orders&lt;/span&gt;&lt;span class="sh"&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;# Check if the event was published successfully
&lt;/span&gt;        &lt;span class="k"&gt;if&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;FailedEntryCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to publish event: &lt;/span&gt;&lt;span class="si"&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;Entries&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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="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;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&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;Failed to process order&lt;/span&gt;&lt;span class="sh"&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="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;202&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# 202 Accepted — order is being processed async
&lt;/span&gt;            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;Order accepted for processing&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;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORD-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;except&lt;/span&gt; &lt;span class="nb"&gt;KeyError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Missing required field: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unexpected error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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="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;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&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;Internal server error&lt;/span&gt;&lt;span class="sh"&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;Step 05:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "PublishOrder".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Important:&lt;/strong&gt; This function needs permission to publish to EventBridge. Let's add that:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Go to the &lt;strong&gt;Configuration&lt;/strong&gt; tab → &lt;strong&gt;Permissions&lt;/strong&gt;&lt;br&gt;
Click the &lt;strong&gt;Role name&lt;/strong&gt; link (opens IAM in a new tab)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; &lt;strong&gt;PublishOrder-role-nx91xx0d&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Add permissions ▼&lt;/strong&gt; → &lt;strong&gt;Attach policies&lt;/strong&gt;&lt;br&gt;
Search for &lt;code&gt;AmazonEventBridgeFullAccess&lt;/code&gt; and attach it&lt;br&gt;
Click &lt;strong&gt;Add permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Policy was successfully attached to role&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In production, you'd create a scoped policy that only allows &lt;code&gt;events:PutEvents&lt;/code&gt; on the &lt;code&gt;orders&lt;/code&gt; bus. For this tutorial, the full access policy keeps things simple.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Function 2&lt;/strong&gt; | Create the ProcessOrder Function
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 08: Navigate to **Lambda&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;strong&gt;Author from scratch&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;ProcessOrder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.12
Click &lt;strong&gt;Create function&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created the function "ProcessOrder"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Paste this code into the code editor:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Processes an order after receiving it from EventBridge.
    In a real app, this would save to a database, charge payment, etc.

    Notice: this function has NO idea who published the event.
    It just reacts to OrderPlaced events. That&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s loose coupling.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&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;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&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;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=== Processing Order ===&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Order ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Customer: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Items: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total items: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Simulate order processing
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Processing: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;productId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; x &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=== Order &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; processed successfully ===&lt;/span&gt;&lt;span class="sh"&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="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="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&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;processed&lt;/span&gt;&lt;span class="sh"&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;Step 11:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "ProcessOrder".&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Function 3&lt;/strong&gt; | Create the UpdateInventory Function
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 12:&lt;/strong&gt; Create another function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;UpdateInventory&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.12
&lt;strong&gt;✅Green banner:&lt;/strong&gt; 
Successfully created the function "UpdateInventory".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 13:&lt;/strong&gt; Paste this code:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Updates inventory based on order events.
    This function receives messages from SQS (not directly from EventBridge).
    The SQS queue adds durability — if this function fails, the message
    stays in the queue and gets retried.

    SQS event structure is different from EventBridge:
    - event[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;] contains an array of SQS messages
    - Each message body contains the EventBridge event
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;batch_failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# SQS wraps the EventBridge event in the message body
&lt;/span&gt;            &lt;span class="n"&gt;message_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&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="c1"&gt;# EventBridge puts the actual event data in 'detail'
&lt;/span&gt;            &lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&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;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=== Updating Inventory for Order &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ===&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;productId&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;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Reducing stock: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; by &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; units&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=== Inventory updated for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ===&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to process record: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Report this specific message as failed
&lt;/span&gt;            &lt;span class="c1"&gt;# Only this message will be retried, not the whole batch
&lt;/span&gt;            &lt;span class="n"&gt;batch_failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;itemIdentifier&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;messageId&lt;/span&gt;&lt;span class="sh"&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;# Return partial batch failures so only failed messages retry
&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;batchItemFailures&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;batch_failures&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;Step 14:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "UpdateInventory".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notice the &lt;code&gt;batchItemFailures&lt;/code&gt; return. This is the **ReportBatchItemFailures&lt;/strong&gt; pattern. Without it, if one message in a batch of 10 fails, all 10 go back to the queue. With it, only the failed message retries.**&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Create the SQS Queue&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;We'll put an SQS queue between EventBridge and the UpdateInventory function. This adds durability so if the function fails, the message stays in the queue.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;SQS&lt;/strong&gt; console &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create queue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Create queue&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; &lt;code&gt;Standard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;inventory-updates&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scroll to &lt;strong&gt;Access policy&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;strong&gt;Advanced&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Replace the policy with this (update your account ID):
&lt;/li&gt;
&lt;/ul&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowEventBridge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&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;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"events.amazonaws.com"&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;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sqs:SendMessage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:sqs:us-east-1:YOUR_ACCOUNT_ID:inventory-updates"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Replace &lt;code&gt;YOUR_ACCOUNT_ID&lt;/code&gt; with your actual AWS account ID and &lt;code&gt;us-east-1&lt;/code&gt; with your region.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Click &lt;strong&gt;Create queue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Queue inventory-updates created successfully&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Copy the &lt;strong&gt;Queue ARN&lt;/strong&gt;. You'll need it for the EventBridge rule&lt;/p&gt;

&lt;h4&gt;
  
  
  Add SQS Permissions to the UpdateInventory Lambda Role
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Before connecting the trigger, the Lambda function needs permission to read from SQS. Without this, you'll get: &lt;em&gt;"The function execution role does not have permissions to call ReceiveMessage on SQS."&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Go to the &lt;strong&gt;Lambda&lt;/strong&gt; console &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Open the &lt;strong&gt;UpdateInventory&lt;/strong&gt; function&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 08&lt;/strong&gt; Click the &lt;strong&gt;Configuration&lt;/strong&gt; tab &lt;br&gt;
Select &lt;strong&gt;Permissions&lt;/strong&gt;&lt;br&gt;
Click the &lt;strong&gt;Role name&lt;/strong&gt; link (opens IAM in a new tab)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; &lt;strong&gt;PublishOrder-role-nx91xx0d&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Add permissions ▼&lt;/strong&gt; → &lt;strong&gt;Attach policies&lt;/strong&gt;&lt;br&gt;
Search for &lt;code&gt;AWSLambdaSQSQueueExecutionRole&lt;/code&gt; and attach it&lt;br&gt;
Click &lt;strong&gt;Add permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Policy was successfully attached to role&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This policy grants &lt;code&gt;sqs:ReceiveMessage&lt;/code&gt;, &lt;code&gt;sqs:DeleteMessage&lt;/code&gt;, and &lt;code&gt;sqs:GetQueueAttributes&lt;/code&gt; which is exactly what Lambda needs to poll the queue.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why does Lambda need these permissions?&lt;/strong&gt; When you use SQS as a Lambda trigger, Lambda itself polls the queue on your behalf using your function's execution role. It calls &lt;code&gt;ReceiveMessage&lt;/code&gt; to get messages, invokes your function, and then calls &lt;code&gt;DeleteMessage&lt;/code&gt; after successful processing. Without these permissions, Lambda can't even start polling.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Connect SQS to the UpdateInventory Lambda
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Go back to the &lt;strong&gt;Lambda&lt;/strong&gt; console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 11:&lt;/strong&gt; Open the &lt;strong&gt;UpdateInventory&lt;/strong&gt; function&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 12:&lt;/strong&gt; Click &lt;strong&gt;Add trigger&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 13:&lt;/strong&gt; &lt;strong&gt;Select a source ▼&lt;/strong&gt;&lt;br&gt;
Select &lt;strong&gt;SQS&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;SQS Queue:&lt;/strong&gt; &lt;code&gt;inventory-updates&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Batch size - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;5&lt;/code&gt;&lt;br&gt;
Check &lt;strong&gt;✔ Report batch item failures&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Add&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;✅Green banner:&lt;/strong&gt; The trigger inventory-updates was successfully added to function UpdateInventory.&lt;/p&gt;


&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Create EventBridge Rules&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now we connect everything by creating rules that route events to our consumers.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Rule 1&lt;/strong&gt; | Route to ProcessOrder Lambda
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;EventBridge&lt;/strong&gt; console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; In the left sidebar under &lt;strong&gt;▼ Buses&lt;/strong&gt;, Click &lt;strong&gt;Rules&lt;/strong&gt; &lt;br&gt;
&lt;strong&gt;Builder mode:&lt;/strong&gt; &lt;code&gt;Advanced builder&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;route-to-process-order&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Routes OrderPlaced events to the ProcessOrder function&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event bus:&lt;/strong&gt; orders
Click &lt;strong&gt;Next&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Build event pattern&lt;br&gt;
&lt;strong&gt;Event source:&lt;/strong&gt; &lt;code&gt;AWS events or Eventbridge partner events&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Event pattern:&lt;/strong&gt; &lt;code&gt;Custom pattern (JSON editor)&lt;/code&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;"source"&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;"orders.api"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail-type"&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;"OrderPlaced"&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;blockquote&gt;
&lt;p&gt;This pattern matches any event where the source is &lt;code&gt;orders.api&lt;/code&gt; AND the detail-type is &lt;code&gt;OrderPlaced&lt;/code&gt;. EventBridge content-based filtering is powerful — you can match on nested fields, numeric ranges, prefixes, and more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Select target(s)&lt;br&gt;
&lt;strong&gt;Target types:&lt;/strong&gt; &lt;code&gt;AWS service&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Select a target:&lt;/strong&gt; &lt;code&gt;Lambda function&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Target location:&lt;/strong&gt; &lt;code&gt;Target in this account&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Function:&lt;/strong&gt; &lt;code&gt;ProcessOrder&lt;/code&gt;&lt;br&gt;
Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Configure tags - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Review and create&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Create rule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Rule route-to-process-order was created successfully&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Rule 2&lt;/strong&gt; | Route to SQS Queue (for Inventory)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Click &lt;strong&gt;Create rule&lt;/strong&gt; again&lt;br&gt;
&lt;strong&gt;Builder mode:&lt;/strong&gt; &lt;code&gt;Advanced builder&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;route-to-inventory-queue&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Routes OrderPlaced events to the inventory SQS queue&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event bus:&lt;/strong&gt; &lt;code&gt;orders&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; Build event pattern&lt;br&gt;
&lt;strong&gt;Event source:&lt;/strong&gt; &lt;code&gt;AWS events or Eventbridge partner events&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Event pattern:&lt;/strong&gt; &lt;code&gt;Custom pattern (JSON editor)&lt;/code&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;"source"&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;"orders.api"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail-type"&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;"OrderPlaced"&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;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Select target(s)&lt;br&gt;
&lt;strong&gt;Target types:&lt;/strong&gt; &lt;code&gt;AWS service&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Select a target:&lt;/strong&gt; &lt;code&gt;SQS queue&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Target location:&lt;/strong&gt; &lt;code&gt;Target in this account&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Queue:&lt;/strong&gt; &lt;code&gt;inventory-updates&lt;/code&gt;&lt;br&gt;
Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; &lt;strong&gt;Configure tags - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 11:&lt;/strong&gt; &lt;strong&gt;Review and create&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Create rule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Rule route-to-inventory-queue was created successfully&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You now have the &lt;em&gt;fanout pattern&lt;/em&gt;: one event, two consumers, each doing their own thing independently.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part VI
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create the API Gateway REST API
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create the API&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;API Gateway&lt;/strong&gt; console &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create API&lt;/strong&gt;&lt;br&gt;
Under &lt;strong&gt;REST API&lt;/strong&gt;, click &lt;strong&gt;Build&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Create REST API&lt;/strong&gt;&lt;br&gt;
Select &lt;strong&gt;New API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API name:&lt;/strong&gt; &lt;code&gt;OrdersAPI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Order processing API&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API endpoint type:&lt;/strong&gt; &lt;code&gt;Regional ▼&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security policy - &lt;em&gt;new&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;TLS_1_0&lt;/code&gt;
Click &lt;strong&gt;Create API&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created REST API 'OrdersAPI (5bmhxxqtp7)'.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Create the /orders Resource&lt;br&gt;
Click &lt;strong&gt;Create resource&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Resource path ▼:&lt;/strong&gt; &lt;code&gt;/&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Resource name:&lt;/strong&gt; &lt;code&gt;orders&lt;/code&gt;&lt;br&gt;
✔ CORS (Cross Origin Resource Sharing)&lt;br&gt;
Click &lt;strong&gt;Create resource&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created resource '/orders'&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Create the POST Method&lt;br&gt;
Select the &lt;code&gt;/orders&lt;/code&gt; resource&lt;br&gt;
Click &lt;strong&gt;Create method&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Create method&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method type:&lt;/strong&gt; &lt;code&gt;POST ▼&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration type:&lt;/strong&gt; &lt;code&gt;Lambda Function&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda proxy integration:&lt;/strong&gt; ✅ enabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda function:&lt;/strong&gt; select &lt;code&gt;PublishOrder&lt;/code&gt;
Click &lt;strong&gt;Create method&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created method 'POST' in 'orders'. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Add Request Validation&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;API Gateway can validate requests before they reach Lambda, saving compute costs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; In the left sidebar under &lt;strong&gt;API:OrdersAPI ▼&lt;/strong&gt;, click &lt;strong&gt;Models&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Click &lt;strong&gt;Create model&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;CreateOrderModel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content type:&lt;/strong&gt; &lt;code&gt;application/json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model schema:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&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;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://json-schema.org/draft-04/schema#"&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;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&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;"customerId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"customerId"&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;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;"items"&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;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;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;"items"&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;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"required"&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;"productId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"productId"&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;"string"&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;"quantity"&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;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;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;Click &lt;strong&gt;Create&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created model 'CreateOrderModel'.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Now attach the validator to the POST method&lt;br&gt;
Go back to &lt;strong&gt;Resources&lt;/strong&gt; → select the &lt;strong&gt;POST&lt;/strong&gt; method under &lt;code&gt;/orders&lt;/code&gt;&lt;br&gt;
Click on the &lt;strong&gt;Method request&lt;/strong&gt; tab&lt;br&gt;
Click &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 11:&lt;/strong&gt; &lt;strong&gt;Edit method request&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Authorization:&lt;/strong&gt; &lt;code&gt;none&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Request validator:&lt;/strong&gt; &lt;code&gt;Validate body&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;▶ Request body:&lt;/strong&gt; click &lt;strong&gt;Add model&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content type: &lt;code&gt;application/json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Model: &lt;code&gt;CreateOrderModel&lt;/code&gt;
Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully edited method request for ‘POST’. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 12:&lt;/strong&gt; Deploy the API&lt;br&gt;
Click &lt;strong&gt;Deploy API&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Stage:&lt;/strong&gt; Create a new stage called &lt;code&gt;dev&lt;/code&gt;&lt;br&gt;
Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully created deployment for OrdersAPI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 13:&lt;/strong&gt; Copy the &lt;strong&gt;Invoke URL&lt;/strong&gt; it looks like: &lt;code&gt;https://abc123.execute-api.us-east-1.amazonaws.com/dev&lt;/code&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Part VII
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Test the Entire Flow
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Test 1&lt;/strong&gt; | Valid Order
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open a terminal (or use an API testing tool like Postman) and send a valid order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://YOUR_API_URL/dev/orders &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "customerId": "CUST-001",
    "items": [
      {"productId": "LAPTOP-001", "quantity": 1},
      {"productId": "MOUSE-002", "quantity": 2}
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order accepted for processing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-A1B2C3D4"&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;h4&gt;
  
  
  Verify The Events Flowed Through:
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Go to &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;ProcessOrder&lt;/strong&gt; → &lt;strong&gt;Monitor&lt;/strong&gt; tab → &lt;strong&gt;View CloudWatch logs&lt;/strong&gt; →  Click on the &lt;strong&gt;Log stream&lt;/strong&gt;&lt;br&gt;
You should see logs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   === Processing Order ===
   Order ID: ORD-A1B2C3D4
   Customer: CUST-001
   Items: [{"productId": "LAPTOP-001", "quantity": 1}, ...]
   === Order ORD-A1B2C3D4 processed successfully ===
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Go to &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;UpdateInventory&lt;/strong&gt; → &lt;strong&gt;Monitor&lt;/strong&gt; tab → &lt;strong&gt;View CloudWatch logs&lt;/strong&gt; →  Click on the &lt;strong&gt;Log stream&lt;/strong&gt;&lt;br&gt;
You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   === Updating Inventory for Order ORD-A1B2C3D4 ===
     Reducing stock: LAPTOP-001 by 1 units
     Reducing stock: MOUSE-002 by 2 units
   === Inventory updated for ORD-A1B2C3D4 ===
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One API call triggered two independent consumers. That's the fanout pattern in action.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test 2&lt;/strong&gt; | Invalid Request (Missing customerID)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://YOUR_API_URL/dev/orders &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "items": [{"productId": "LAPTOP-001", "quantity": 1}]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid request body"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;API Gateway rejected this request &lt;em&gt;before it reached Lambda&lt;/em&gt;. &lt;br&gt;
Your function was never invoked. That's the value of request validation. It saves compute costs and keeps your Lambda code cleaner.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test 3&lt;/strong&gt; | Invalid Request (Missing quantity is 0)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://YOUR_API_URL/dev/orders &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "customerId": "CUST-001",
    "items": [{"productId": "LAPTOP-001", "quantity": 0}]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid request body"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This also gets rejected because our model requires &lt;code&gt;"minimum": 1&lt;/code&gt; for quantity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part VII
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Add Resilient Code:&lt;/strong&gt; Retry Logic with Exponential Backoff
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;In real applications, your Lambda functions call external services that can fail temporarily. The exam tests whether you understand retry patterns.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Let's Update The &lt;code&gt;PublishOrder&lt;/code&gt; Function To Include Retry Logic For The EventBridge Call
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;PublishOrder&lt;/strong&gt; function in the Lambda console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Replace the code with this updated version&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="n"&gt;eventbridge&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;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;events&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;retry_with_backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Retry a function with exponential backoff and jitter.

    Why exponential backoff?
    - Constant retries can overwhelm a recovering service
    - Exponential delays give the service time to recover
    - Jitter prevents all clients from retrying at the same time (thundering herd)

    This pattern is tested on the DVA-C02 exam.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&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;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;max_retries&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;All &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; attempts failed. Last error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt;

            &lt;span class="c1"&gt;# Exponential backoff: 0.5s, 1s, 2s, 4s...
&lt;/span&gt;            &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_delay&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Add jitter: random value between 0 and the delay
&lt;/span&gt;            &lt;span class="n"&gt;jitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;wait_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;jitter&lt;/span&gt;

            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attempt &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Retrying in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;wait_time&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait_time&lt;/span&gt;&lt;span class="p"&gt;)&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&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="nf"&gt;get&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;{}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;order_detail&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;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORD-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&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;customerId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&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;items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&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;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&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 retry logic for the EventBridge call
&lt;/span&gt;        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;publish_event&lt;/span&gt;&lt;span class="p"&gt;():&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;eventbridge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;Entries&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;Source&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;orders.api&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;DetailType&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;OrderPlaced&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;Detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_detail&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;EventBusName&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;orders&lt;/span&gt;&lt;span class="sh"&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="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;FailedEntryCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EventBridge rejected the event: &lt;/span&gt;&lt;span class="si"&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;Entries&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

        &lt;span class="nf"&gt;retry_with_backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publish_event&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;202&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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;Order accepted for processing&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;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORD-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;except&lt;/span&gt; &lt;span class="nb"&gt;KeyError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Missing required field: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to publish order event: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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="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;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&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;Failed to process order. Please try again.&lt;/span&gt;&lt;span class="sh"&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;Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully updated the function "PublishOrder".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;The AWS SDK (boto3) already includes built-in retry logic for AWS API calls. But the exam tests whether you can implement retry logic for **third-party service calls&lt;/strong&gt; where you don't get automatic retries. Know the pattern: exponential backoff + jitter.**&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part IX
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Explore the Messaging Patterns
&lt;/h3&gt;

&lt;p&gt;Before we clean up, let's look at the SQS queue to understand the messaging flow.&lt;/p&gt;

&lt;h4&gt;
  
  
  View Messages in SQS
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;SQS&lt;/strong&gt; console&lt;br&gt;
Click on &lt;code&gt;inventory-updates&lt;/code&gt;&lt;br&gt;
Click &lt;strong&gt;Send and receive messages&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Poll for messages&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If the UpdateInventory function processed everything, the queue should be empty. That's correct! SQS deletes messages after successful processing.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Understanding Visibility Timeout
&lt;/h4&gt;

&lt;p&gt;When Lambda picks up a message from SQS:&lt;br&gt;
&lt;strong&gt;1.&lt;/strong&gt; The message becomes &lt;strong&gt;invisible&lt;/strong&gt; to other consumers (visibility timeout)&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Lambda processes it&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; If successful, Lambda &lt;strong&gt;deletes&lt;/strong&gt; the message&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; If Lambda fails, the message becomes &lt;strong&gt;visible&lt;/strong&gt; again after the timeout and gets retried&lt;/p&gt;

&lt;p&gt;You can see the visibility timeout in the queue settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the queue → &lt;strong&gt;Edit&lt;/strong&gt; → &lt;strong&gt;Visibility timeout&lt;/strong&gt; (default: 30 seconds)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Set the visibility timeout to &lt;em&gt;at least 6x your Lambda timeout&lt;/em&gt;. If your function takes 30 seconds and the visibility timeout is 30 seconds, a slow execution could cause duplicate processing.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Did&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a custom EventBridge bus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Event-driven architecture&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;One event → two consumers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Fanout pattern&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Publisher doesn't know about consumers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Loose coupling&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway validates before Lambda runs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Request validation&lt;/em&gt; (saves compute)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS between EventBridge and Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Durability, async processing&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;batchItemFailures&lt;/code&gt; in UpdateInventory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Partial batch failure handling&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;retry_with_backoff&lt;/code&gt; in PublishOrder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Resilient code, exponential backoff + jitter&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP 202 response&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Asynchronous acceptance pattern&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Each function has its own role&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Least privilege, stateless design&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;p&gt;To avoid charges, delete the resources in this order:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;strong&gt;API Gateway&lt;/strong&gt; → Delete the &lt;code&gt;OrdersAPI&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; &lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;PublishOrder&lt;/code&gt;, &lt;code&gt;ProcessOrder&lt;/code&gt;, &lt;code&gt;UpdateInventory&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; &lt;strong&gt;SQS&lt;/strong&gt; → Delete the &lt;code&gt;inventory-updates&lt;/code&gt; queue&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; &lt;strong&gt;EventBridge&lt;/strong&gt; → Delete both rules, then delete the &lt;code&gt;orders&lt;/code&gt; event bus&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; &lt;strong&gt;IAM&lt;/strong&gt; → Delete the Lambda execution roles (they start with &lt;code&gt;PublishOrder-role-&lt;/code&gt;, etc.)&lt;br&gt;
&lt;strong&gt;6.&lt;/strong&gt; &lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete the log groups under &lt;code&gt;/aws/lambda/&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Loose coupling&lt;/strong&gt; is almost always the right answer. If components talk directly, add a queue or event bus between them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge&lt;/strong&gt; is the preferred service for event-driven architectures. It supports content-based filtering, multiple targets, and cross-account routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway request validation&lt;/strong&gt; happens before Lambda which saves compute costs. Know how to create models and attach validators.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS long polling&lt;/strong&gt; (&lt;code&gt;WaitTimeSeconds &amp;gt; 0&lt;/code&gt;) reduces empty responses and costs. Always use it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ReportBatchItemFailures:&lt;/strong&gt; without it, one failed message retries the entire batch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exponential backoff with jitter&lt;/strong&gt; is the standard retry pattern for third-party calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 202 Accepted&lt;/strong&gt; signals that the request was accepted for async processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fanout&lt;/strong&gt; = SNS or EventBridge sending one event to multiple consumers.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html" rel="noopener noreferrer"&gt;Amazon EventBridge User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-request-validation.html" rel="noopener noreferrer"&gt;Request validation for REST APIs in API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html" rel="noopener noreferrer"&gt;Using Lambda with Amazon SQS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/invocation-retries.html" rel="noopener noreferrer"&gt;Understanding retry behavior in Lambda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>OpenClaw on AWS for Beginners: How To Build An AI Agent That Deploys Your WordPress Multisite in Minutes With Just Three Services</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Sun, 26 Apr 2026 23:07:34 +0000</pubDate>
      <link>https://dev.to/aws-builders/openclaw-on-aws-for-beginners-how-to-build-an-ai-agent-that-deploys-your-wordpress-multisite-in-abm</link>
      <guid>https://dev.to/aws-builders/openclaw-on-aws-for-beginners-how-to-build-an-ai-agent-that-deploys-your-wordpress-multisite-in-abm</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/openclaw-2026-04-16"&gt;OpenClaw Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Just Lightsail, IAM, and Route 53:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How My Agent Built My Site For Me In Minutes
&lt;/h3&gt;

&lt;p&gt;AWS has over &lt;strong&gt;200&lt;/strong&gt; services. &lt;br&gt;
I used &lt;strong&gt;&lt;em&gt;three.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No VPCs. &lt;br&gt;
No EC2 security groups. &lt;br&gt;
No CloudFormation templates. &lt;br&gt;
No Lambda functions. &lt;br&gt;
No S3 bucket policies. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Just Lightsail&lt;/strong&gt; to run things, &lt;strong&gt;IAM&lt;/strong&gt; to grant permissions, and &lt;strong&gt;Route 53&lt;/strong&gt; for DNS. &lt;/p&gt;

&lt;p&gt;That's the entire infrastructure for an AI Agent that builds WordPress Multisite installations and writes SEO blog content from scratch, in under 30 minutes, for people who've never touched a server.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the story of how I built it with OpenClaw, and why staying in the shallow end of AWS was the whole point.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem I Wanted To Solve
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The monotony of manual labor&lt;/em&gt;...&lt;/strong&gt; &lt;br&gt;
Setting up WordPress Multisite manually is a multi-hour ordeal even for developers. You SSH into a server, edit &lt;code&gt;wp-config.php&lt;/code&gt; by hand, hope you didn't miss a semicolon, configure Apache, set up a static IP, mess with DNS records, install SSL, restart services, Google the error, and try again.&lt;/p&gt;

&lt;p&gt;For a beginner? It's a wall. &lt;br&gt;
Most people give up somewhere between &lt;em&gt;"what's SSH?"&lt;/em&gt; and &lt;strong&gt;"Error establishing database connection."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I didn't want to write another tutorial that people would bookmark and never finish. I wanted to build an AI agent that does the whole thing for you step by step, in plain language, while explaining everything along the way. And I wanted it to run entirely &lt;strong&gt;in the cloud&lt;/strong&gt; &lt;em&gt;so beginners don't need to install anything on their computer.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Lightsail (and only Lightsail)
&lt;/h2&gt;

&lt;p&gt;Most AWS tutorials assume you're comfortable with the &lt;strong&gt;full ecosystem.&lt;/strong&gt; They'll casually tell you to "create a VPC with two subnets" or "configure a security group with the following inbound rules" as if that's a normal thing beginners do on a Sunday afternoon...even though it's quite simple, it's just too many services and a confusing dashboard.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lightsail&lt;/em&gt; exists specifically to avoid that. &lt;br&gt;
It's AWS for people who don't want to think about AWS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fixed monthly pricing. &lt;/li&gt;
&lt;li&gt;Instances that just work. &lt;/li&gt;
&lt;li&gt;Networking that's already configured. &lt;/li&gt;
&lt;li&gt;A console that doesn't require a PhD to navigate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this project, Lightsail gave me everything I needed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An OpenClaw Instance:&lt;/strong&gt; AWS has an official OpenClaw blueprint. Click create, pick the blueprint, and you have a running AI gateway in two minutes. &lt;br&gt;
&lt;em&gt;No Docker, no environment variables, no dependency hell.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A WordPress Instance:&lt;/strong&gt; same deal. Bitnami WordPress blueprint. &lt;em&gt;Click, wait, done.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Static IPs:&lt;/strong&gt; free while attached to an instance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Firewall Rules:&lt;/strong&gt; built into the instance management page. &lt;br&gt;
&lt;em&gt;No security group rabbit holes.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Browser-Based SSH:&lt;/strong&gt; click "Connect using SSH" and you're in. &lt;br&gt;
&lt;em&gt;No key management, no PuTTY, no terminal setup.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The only other AWS services I touched were &lt;strong&gt;IAM&lt;/strong&gt; (to grant the OpenClaw instance permission to create Lightsail resources and use Bedrock) and &lt;strong&gt;Route 53&lt;/strong&gt; (for DNS, and only if you want a custom domain). &lt;br&gt;
That's it. &lt;br&gt;
&lt;strong&gt;Three services.&lt;/strong&gt; &lt;br&gt;
The rest of AWS might as well not exist for this project.&lt;/p&gt;


&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;An OpenClaw agent with two skills:&lt;/p&gt;
&lt;h3&gt;
  
  
  Skill 1
&lt;/h3&gt;
&lt;h4&gt;
  
  
  WordPress Multisite Builder
&lt;/h4&gt;

&lt;p&gt;An 8-phase guided workflow that walks beginners through creating a Lightsail instance, configuring WordPress, enabling multisite, setting up domains, and installing SSL. Uses browser automation to navigate the AWS console and SSH to configure the server.&lt;/p&gt;
&lt;h3&gt;
  
  
  Skill 2
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Blog Content Engine
&lt;/h4&gt;

&lt;p&gt;A 6-phase content generation system that researches what people are searching for in your niche, writes SEO-optimized blog posts, and publishes them directly to your WordPress site via WP-CLI.&lt;/p&gt;

&lt;p&gt;The whole thing runs on a Lightsail OpenClaw instance. &lt;br&gt;
No local install. &lt;br&gt;
You chat with the agent from a browser dashboard, Telegram, or WhatsApp, and it builds your website and writes your content.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Architecture
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You (browser / Telegram / WhatsApp)
    │
    ▼
Lightsail OpenClaw Instance (~$12/mo)
    │  Amazon Bedrock → Claude Opus 4.6
    │
    ├── browser tool ──► AWS Lightsail Console + WordPress Admin
    ├── exec tool ─────► SSH + WP-CLI on the WordPress server
    ├── web_search ────► Keyword research + trending topics
    ├── web_fetch ─────► Competitor analysis + content research
    └── file I/O ──────► Progress tracking, content calendar, posts
    │
    ▼
Lightsail WordPress Instance (~$12/mo)
    └── WordPress Multisite with auto-generated content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Two Lightsail instances. &lt;br&gt;
One IAM role. &lt;br&gt;
Optionally Route 53 for DNS.&lt;/p&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step By Step Guide
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 1:&lt;/strong&gt; &lt;em&gt;The OpenClaw Instance&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;I went to the Lightsail console, clicked "Create instance," picked the OpenClaw blueprint under Linux/Unix, selected the 2 GB plan (~$12/month), named it &lt;code&gt;WordPressAgent&lt;/code&gt;, and clicked Create. &lt;br&gt;
Two minutes later it was running.&lt;/p&gt;

&lt;p&gt;The pairing process was straightforward: click "Connect using SSH" to open a browser terminal, copy the dashboard URL and access token from the welcome message, paste the token into the dashboard, and approve the pairing prompts in the SSH terminal. Dashboard showed "OK." &lt;br&gt;
&lt;strong&gt;Connected.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 2:&lt;/strong&gt; &lt;em&gt;Enabling Bedrock&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;The Lightsail OpenClaw blueprint comes pre-configured to use Amazon Bedrock you just need to grant the permissions. On the instance's "Getting started" tab, there's a "Copy the script" button. I copied it, launched CloudShell, pasted, and ran it. The script creates an IAM role (&lt;code&gt;LightsailRoleFor-i-xxxxx&lt;/code&gt;) with Bedrock access.&lt;/p&gt;

&lt;p&gt;I tested it by typing "Hello" in the OpenClaw dashboard chat. Got a response. Bedrock was working.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 3:&lt;/strong&gt; &lt;em&gt;The IAM Permission That Made Everything Click&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;Here's where I hit my first snag. The agent needed to create Lightsail resources (the WordPress instance, static IPs, etc.) but the IAM role only had Bedrock permissions. When I ran &lt;code&gt;aws lightsail get-instances&lt;/code&gt; from the SSH terminal, I got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;AccessDeniedException: User is not authorized to perform: lightsail:GetInstances
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix was simple: I went to the IAM console, found the &lt;code&gt;AmazonLightsailInstance&lt;/code&gt; role, clicked "Add permissions" → "Attach policies," searched for &lt;code&gt;AmazonLightsailFullAccess&lt;/code&gt;, and attached it.&lt;/p&gt;

&lt;p&gt;One policy. That's all it took. &lt;br&gt;
Lightsail is self-contained: instances, networking, DNS, snapshots, key pairs, everything is under one permission set. &lt;br&gt;
No cross-service IAM policies.&lt;br&gt;
No resource-based permissions.&lt;br&gt;
No condition keys. &lt;br&gt;
&lt;strong&gt;This is what makes Lightsail great for beginners:&lt;/strong&gt; &lt;em&gt;the permission model is as simple as the service itself.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 4:&lt;/strong&gt; &lt;em&gt;Escaping The Sandbox&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;The next issue: OpenClaw told me it was "running in a sandboxed environment" and couldn't execute commands on the host. By default, OpenClaw runs tools inside a Docker container for security. That's smart for untrusted inputs, but it blocks the agent from doing real work.&lt;/p&gt;

&lt;p&gt;Three config lines fixed it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;tools.exec.host gateway
openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;tools.exec.ask off
openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;tools.exec.security full
openclaw gateway restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells OpenClaw to run tools directly on the gateway host instead of in the sandbox. For a personal agent that only you control, this is the right tradeoff.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Alternatively, you can give your agent permission to use your browser and run commands during the initial Security setup process.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 5:&lt;/strong&gt; &lt;em&gt;Building The Agent Workspace&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;OpenClaw agents are defined by markdown files in a workspace directory. No code, no SDK, no compilation. &lt;br&gt;
Just files that tell the agent who it is, how to behave, and what to do.&lt;/p&gt;

&lt;p&gt;I created five core files by pasting them directly into the SSH terminal using &lt;code&gt;cat &amp;gt; file &amp;lt;&amp;lt; 'ENDOFFILE'&lt;/code&gt; blocks. &lt;br&gt;
No git clone. &lt;br&gt;
No file transfers.&lt;br&gt;
No local tools needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IDENTITY.md&lt;/strong&gt; — defines who the agent is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;You are WP Multisite Builder + Content Engine, an AI assistant that helps
complete beginners:
&lt;span class="p"&gt;
1.&lt;/span&gt; Set up WordPress Multisite on Amazon Lightsail
&lt;span class="p"&gt;2.&lt;/span&gt; Research trending search topics and generate SEO-optimized blog posts
&lt;span class="p"&gt;3.&lt;/span&gt; Publish content directly to their WordPress sites
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SOUL.md&lt;/strong&gt; — defines the personality and safety rules. This was important because the agent is aimed at beginners:&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="gu"&gt;## Personality&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Warm and encouraging, like a helpful friend who happens to know tech
&lt;span class="p"&gt;-&lt;/span&gt; Never condescending. If someone doesn't know what SSH means, that's fine
&lt;span class="p"&gt;-&lt;/span&gt; Celebrate small wins ("Nice, your instance is running!")

&lt;span class="gu"&gt;## Safety rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; NEVER proceed without user confirmation on actions that:
&lt;span class="p"&gt;  -&lt;/span&gt; Create or modify AWS resources (costs money)
&lt;span class="p"&gt;  -&lt;/span&gt; Change passwords or security settings
&lt;span class="p"&gt;  -&lt;/span&gt; Install software on their server
&lt;span class="p"&gt;  -&lt;/span&gt; Modify WordPress configuration
&lt;span class="p"&gt;-&lt;/span&gt; Always explain what something will cost before creating resources
&lt;span class="p"&gt;-&lt;/span&gt; Never store or display AWS credentials in chat history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AGENTS.md&lt;/strong&gt; — the operating instructions and greeting message. When a user first connects, the agent says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Hey! I can help you with two things: set up a WordPress website that can run multiple sites all on Amazon Lightsail, or write blog posts based on what people are actually searching for. We can do both, or just one. What sounds good?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;TOOLS.md&lt;/strong&gt; — a reference for which OpenClaw tools the agent uses: &lt;code&gt;browser&lt;/code&gt; for navigating the Lightsail console and WordPress admin, &lt;code&gt;exec&lt;/code&gt; for SSH and WP-CLI commands, &lt;code&gt;web_search&lt;/code&gt; and &lt;code&gt;web_fetch&lt;/code&gt; for keyword research and troubleshooting, and file I/O for saving progress.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 6:&lt;/strong&gt; &lt;em&gt;The WordPress Multisite Skill&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;This is the core of the project. I created &lt;code&gt;skills/wp-multisite-lightsail/SKILL.md&lt;/code&gt; — an 8-phase guided workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: Pre-flight Checks.&lt;/strong&gt; Confirms the user has an AWS account and login ready. If not, walks them through creating one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Create a Lightsail Instance.&lt;/strong&gt; The agent opens the Lightsail console using the &lt;code&gt;browser&lt;/code&gt; tool, takes a snapshot of the page to identify clickable elements, and walks through instance creation: Linux/Unix platform, WordPress blueprint, $5/month plan. It stops and confirms the cost before clicking "Create instance," then waits for "Running" status.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3: Get WordPress Credentials.&lt;/strong&gt; SSHs into the new instance and reads the default Bitnami password. Tells the user to write it down. Verifies WordPress loads in the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 4: Static IP.&lt;/strong&gt; Creates and attaches a free static IP so the address doesn't change on restart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 5: Enable Multisite.&lt;/strong&gt; This is the complex part that usually trips people up. The agent edits &lt;code&gt;wp-config.php&lt;/code&gt; via SSH to add &lt;code&gt;WP_ALLOW_MULTISITE&lt;/code&gt;, navigates to Tools → Network Setup in wp-admin via the browser, recommends subdirectories for beginners, applies the multisite configuration constants, updates &lt;code&gt;.htaccess&lt;/code&gt;, and restarts Apache. It makes a backup of &lt;code&gt;wp-config.php&lt;/code&gt; before any edits. But, it will choose the Multisite Instance from the get go, unless it doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 6: Domain Setup.&lt;/strong&gt; If the user has a domain, the agent guides them through DNS configuration at their registrar (using &lt;code&gt;web_search&lt;/code&gt; to find the right settings page for GoDaddy, Namecheap, Cloudflare, Route53 etc.) and updates WordPress via WP-CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 7: SSL certificate.&lt;/strong&gt; Runs Bitnami's &lt;code&gt;bncert-tool&lt;/code&gt; for free Let's Encrypt SSL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 8: Wrap-up.&lt;/strong&gt; Saves a summary with all URLs, login info, monthly costs, and next steps.&lt;/p&gt;

&lt;p&gt;The skill also includes error handling for every common failure such database connection errors, SSH failures, SSL issues, missing config entries. The agent searches the web for solutions when it hits something unexpected.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 7:&lt;/strong&gt; &lt;em&gt;The Content Engine Skill&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;After the site is built, the agent offers to generate blog content. I created &lt;code&gt;skills/blog-content-engine/SKILL.md&lt;/code&gt; with 6 phases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: Keyword Research.&lt;/strong&gt; Asks the user their niche, then fires off multiple &lt;code&gt;web_search&lt;/code&gt; queries to find trending questions, "People Also Ask" data, long-tail keywords, and competitor content. Compiles 10-15 keyword opportunities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Content Planning.&lt;/strong&gt; Creates outlines with titles, meta descriptions, target keywords, H2 structure, and FAQ sections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3: Writing.&lt;/strong&gt; Generates 1,200-2,000 word SEO-optimized posts with proper heading structure, keyword placement, short paragraphs, and 8th-grade reading level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 4: Publishing.&lt;/strong&gt; Pushes to WordPress via WP-CLI (or browser fallback). For multisite, asks which subsite to target. Never auto-publishes without explicit approval.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 5: Batch Generation.&lt;/strong&gt; Handles multiple posts with a content calendar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 6: Ongoing Suggestions.&lt;/strong&gt; Offers fresh trending topics each time the user comes back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Connecting Telegram and WhatsApp
&lt;/h3&gt;

&lt;p&gt;After everything was working in the dashboard, I connected messaging channels so I could manage the site from my phone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Telegram&lt;/strong&gt; took about three minutes: create a bot via &lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt; on Telegram, copy the bot token, run &lt;code&gt;openclaw channels add&lt;/code&gt; in SSH, select Telegram, paste the token. Then configure the allowlist with my numeric Telegram user ID (get it from @userinfobot) and approve the pairing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp&lt;/strong&gt; was even simpler: run &lt;code&gt;openclaw channels add&lt;/code&gt;, select WhatsApp, scan the QR code with my phone's Linked Devices feature. Done.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now I can message my bot on Telegram or WhatsApp and say "write a blog post about beginner gardening tips" and it researches, writes, and publishes &lt;strong&gt;all from my phone.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment It All Worked
&lt;/h2&gt;

&lt;p&gt;I opened the OpenClaw dashboard, typed "Help me set up WordPress Multisite," and the agent greeted me. It asked if I had an AWS account. It opened the Lightsail console in its browser. &lt;/p&gt;

&lt;p&gt;It walked me through creating the instance, explaining every step. It confirmed the cost before clicking Create. It waited for the instance to boot. It SSHed in, grabbed the credentials, set up the static IP, edited the config files, enabled multisite, and verified everything worked.&lt;/p&gt;

&lt;p&gt;Under 30 minutes. The whole thing. An AI agent, running on Lightsail, building a WordPress Multisite on another Lightsail instance, using browser automation and SSH. And I never left the Lightsail console (plus one quick trip to IAM to attach a policy).&lt;/p&gt;




&lt;h2&gt;
  
  
  What OpenClaw Gets Right
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Skills Are Just Markdown.&lt;/strong&gt; Each skill is a &lt;code&gt;SKILL.md&lt;/code&gt; file with YAML frontmatter and instructions. No SDK, no compilation, no deployment pipeline. Write markdown, drop it in the workspace, restart the gateway. The agent picks it up on the next session. This is the right abstraction for teaching an agent how to do something complex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser Automation Is Built In.&lt;/strong&gt; Most AI agents are limited to APIs and CLIs. OpenClaw's &lt;code&gt;browser&lt;/code&gt; tool controls an isolated Chromium instance: the agent can navigate web consoles, click buttons, fill forms, and take screenshots. That's a massive unlock for automating things that don't have an API, like the Lightsail console's instance creation wizard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Lightsail Blueprint Is A Cheat Code.&lt;/strong&gt; Going from zero to a running AI gateway in two minutes, with Bedrock pre-configured, HTTPS auto-managed, and daily token rotation...that's the kind of developer experience that makes you want to build things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-Channel Is Trivial.&lt;/strong&gt; Dashboard, Telegram, WhatsApp all connected in minutes. The agent doesn't care where the message comes from. Same skills, same personality, same workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Confirm Before Acting" Pattern Works.&lt;/strong&gt; My agent never creates resources or spends money without asking first. This isn't just good UX. It's the difference between a tool beginners trust and one they're afraid of.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned About Staying In The Shallow End
&lt;/h2&gt;

&lt;p&gt;The biggest lesson: &lt;strong&gt;you don't need the deep ocean of AWS to build real things.&lt;/strong&gt; The instinct is always to reach for EC2, VPCs, security groups, load balancers, auto-scaling groups. But for a personal AI agent running a WordPress site? &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lightsail does everything, and it does it without the cognitive overhead.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The permission model tells the story. I needed exactly two IAM policies: one for Bedrock (auto-created by the setup script) and &lt;code&gt;AmazonLightsailFullAccess&lt;/code&gt; (one click in the IAM console). Compare that to the typical EC2 setup where you're writing JSON policy documents with resource ARNs and condition keys.&lt;/p&gt;

&lt;p&gt;Route 53 was optional. Only needed if you want a custom domain. And even then, the agent handles the DNS configuration by guiding the user through their existing registrar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three services.&lt;/strong&gt; That's the whole stack. And it's not a toy. &lt;br&gt;
It's a production-capable AI agent that builds infrastructure, manages &lt;em&gt;servers, and generates content.&lt;/em&gt;&lt;/p&gt;


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

&lt;p&gt;&lt;em&gt;Everything is reproducible.&lt;/em&gt; You need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;An AWS account&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Lightsail OpenClaw instance&lt;/strong&gt; (2 GB plan, ~$12/month)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AmazonLightsailFullAccess&lt;/code&gt;&lt;/strong&gt; attached to the instance's IAM role&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The workspace files&lt;/strong&gt; pasted into &lt;code&gt;~/.openclaw/workspace/&lt;/code&gt; via SSH&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full deployment takes about 15 minutes. Here's the condensed version:&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;# After creating the OpenClaw instance, pairing, and enabling Bedrock:&lt;/span&gt;

&lt;span class="c"&gt;# Create skill directories&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.openclaw/workspace/skills/wp-multisite-lightsail
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.openclaw/workspace/skills/blog-content-engine

&lt;span class="c"&gt;# Paste each workspace file (IDENTITY.md, SOUL.md, AGENTS.md, TOOLS.md,&lt;/span&gt;
&lt;span class="c"&gt;# and both SKILL.md files) using cat &amp;gt; file &amp;lt;&amp;lt; 'ENDOFFILE' blocks&lt;/span&gt;

&lt;span class="c"&gt;# Configure tool permissions&lt;/span&gt;
openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;tools.exec.host gateway
openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;tools.exec.ask off
openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;tools.exec.security full
openclaw gateway restart

&lt;span class="c"&gt;# Open the dashboard and type: "Help me set up WordPress Multisite"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No GitHub repo needed. &lt;br&gt;
No local install. &lt;br&gt;
No dependencies. &lt;br&gt;
Just an SSH terminal and some copy-paste.&lt;/p&gt;




&lt;h2&gt;
  
  
  The cost breakdown
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Monthly cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenClaw Lightsail instance (2 GB)&lt;/td&gt;
&lt;td&gt;~$12 (first 90 days free)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WordPress Lightsail instance (2 GB)&lt;/td&gt;
&lt;td&gt;~$12 (first 90 days free)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Bedrock (Claude Opus 4.6)&lt;/td&gt;
&lt;td&gt;Pay per token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static IPs&lt;/td&gt;
&lt;td&gt;Free (while attached)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL certificates&lt;/td&gt;
&lt;td&gt;Free (Let's Encrypt, auto-renews)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain name&lt;/td&gt;
&lt;td&gt;~$15/year (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;About $25/month for the infrastructure, plus whatever Bedrock charges for the AI conversations. For a self-hosted AI agent that builds and manages WordPress sites and writes your blog content, that's a pretty good deal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where To Next?
&lt;/h2&gt;

&lt;p&gt;Well, the foundation is solid. Some things I'm thinking about, dependent on your use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;More Skills:&lt;/strong&gt; WooCommerce setup, backup automation, security hardening, performance optimization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenant:&lt;/strong&gt; let the agent manage multiple WordPress Multisites for different clients if you're the entrepreneaurial sort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled Content:&lt;/strong&gt; use OpenClaw's cron tool to auto-research and draft posts on a schedule&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics Integration:&lt;/strong&gt; pull Google Analytics data to inform content strategy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The possibilities are endless!&lt;/em&gt;&lt;br&gt;
But honestly, the most exciting part is how accessible this is. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A beginner with an AWS account can have a working WordPress Multisite with AI-generated content in under an hour&lt;/strong&gt;. &lt;br&gt;
No terminal on their laptop. &lt;br&gt;
No config files on their desktop.&lt;br&gt;
No "install these 7 prerequisites first."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Just Lightsail, IAM, and Route 53.&lt;/strong&gt;&lt;br&gt;
The shallow end of AWS. &lt;br&gt;
And it's plenty deep enough.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with OpenClaw on Amazon Lightsail. Powered by Amazon Bedrock (Claude Opus 4.6). Three AWS services. Zero Local Installs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>openclawchallenge</category>
    </item>
    <item>
      <title>Developer Associate Exam Guide</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Fri, 24 Apr 2026 19:27:55 +0000</pubDate>
      <link>https://dev.to/aws-builders/developer-associate-exam-guide-56ln</link>
      <guid>https://dev.to/aws-builders/developer-associate-exam-guide-56ln</guid>
      <description>&lt;h2&gt;
  
  
  What Is the AWS Certified Developer – Associate?
&lt;/h2&gt;

&lt;p&gt;The AWS Certified Developer – Associate (DVA-C02) validates your ability to develop, test, deploy, and debug cloud-based applications using AWS. It's aimed at developers with at least one year of hands-on experience building and maintaining AWS applications.&lt;/p&gt;

&lt;p&gt;This isn't a theory-only exam. The questions are scenario-based. You'll be given a situation and asked to pick the best approach. &lt;/p&gt;

&lt;p&gt;That means you need to understand not just &lt;em&gt;what&lt;/em&gt; a service does, but &lt;em&gt;when&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; to use it in real code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exam Format
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exam code&lt;/td&gt;
&lt;td&gt;DVA-C02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Questions&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duration&lt;/td&gt;
&lt;td&gt;130 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Format&lt;/td&gt;
&lt;td&gt;Multiple choice + multiple select&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passing score&lt;/td&gt;
&lt;td&gt;720 / 1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;$150 USD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validity&lt;/td&gt;
&lt;td&gt;3 years&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Multiple choice questions have one correct answer out of four. &lt;br&gt;
Multiple select questions tell you how many answers to pick (usually two out of five).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Four Domains
&lt;/h2&gt;

&lt;p&gt;The exam is split into four domains, each with a different weight:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;th&gt;~Questions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Development with AWS Services&lt;/td&gt;
&lt;td&gt;32%&lt;/td&gt;
&lt;td&gt;~21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Security&lt;/td&gt;
&lt;td&gt;26%&lt;/td&gt;
&lt;td&gt;~17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Deployment&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;td&gt;~16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Troubleshooting and Optimization&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;td&gt;~11&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Development&lt;/strong&gt; is the biggest chunk, but &lt;strong&gt;Security&lt;/strong&gt; is close behind. &lt;br&gt;
Don't sleep on &lt;strong&gt;Deployment.&lt;/strong&gt;&lt;br&gt;
Nearly a quarter of the exam is CI/CD, IaC, and deployment strategies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Domain 1
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Development with AWS Services
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(32%)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the core of the exam. &lt;br&gt;
You need to write code that works with AWS services, not just click through the console.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 1.1 Develop code for applications hosted on AWS&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Architectural patterns: event-driven, microservices, fanout, choreography vs orchestration&lt;/li&gt;
&lt;li&gt;Stateful vs stateless, tightly vs loosely coupled, sync vs async&lt;/li&gt;
&lt;li&gt;Building and maintaining APIs with API Gateway&lt;/li&gt;
&lt;li&gt;Writing unit tests with SAM&lt;/li&gt;
&lt;li&gt;Using messaging services (SQS, SNS, EventBridge)&lt;/li&gt;
&lt;li&gt;Working with AWS SDKs&lt;/li&gt;
&lt;li&gt;Handling streaming data (Kinesis)&lt;/li&gt;
&lt;li&gt;Resilient code: retry logic, circuit breakers, error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 1.2: Develop code for AWS Lambda&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;VPC access from Lambda&lt;/li&gt;
&lt;li&gt;Configuration: memory, timeout, concurrency, layers, extensions, triggers, destinations&lt;/li&gt;
&lt;li&gt;Error handling: DLQs, Lambda Destinations&lt;/li&gt;
&lt;li&gt;Testing Lambda functions&lt;/li&gt;
&lt;li&gt;Performance tuning&lt;/li&gt;
&lt;li&gt;Real-time data processing&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 1.3: Use data stores in application development&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;DynamoDB: partition keys, GSIs, LSIs, query vs scan, consistency models&lt;/li&gt;
&lt;li&gt;Data serialization and persistence&lt;/li&gt;
&lt;li&gt;Data lifecycle management (TTL)&lt;/li&gt;
&lt;li&gt;Caching with ElastiCache and DAX&lt;/li&gt;
&lt;li&gt;Specialized stores like OpenSearch&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Domain 2
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(26%)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Security is woven into everything on this exam. &lt;br&gt;
Expect questions that combine security with development scenarios.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 2.1: Implement authentication and authorization&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Cognito User Pools and Identity Pools&lt;/li&gt;
&lt;li&gt;JWT tokens and bearer token validation&lt;/li&gt;
&lt;li&gt;IAM roles, policies, and STS AssumeRole&lt;/li&gt;
&lt;li&gt;Lambda authorizers&lt;/li&gt;
&lt;li&gt;Cross-service auth in microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 2.2: Implement encryption using AWS services&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Encryption at rest vs in transit&lt;/li&gt;
&lt;li&gt;KMS: key creation, rotation, cross-account access&lt;/li&gt;
&lt;li&gt;Client-side vs server-side encryption&lt;/li&gt;
&lt;li&gt;AWS Encryption SDK&lt;/li&gt;
&lt;li&gt;Certificate management&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 2.3: Manage sensitive data in application code&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Secrets Manager vs SSM Parameter Store&lt;/li&gt;
&lt;li&gt;Encrypting Lambda environment variables&lt;/li&gt;
&lt;li&gt;Data classification (PII, PHI)&lt;/li&gt;
&lt;li&gt;Data masking and sanitization&lt;/li&gt;
&lt;li&gt;Multi-tenant data isolation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Domain 3
&lt;/h2&gt;

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

&lt;p&gt;&lt;strong&gt;(24%)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This domain is all about getting code from your machine to production safely and repeatably.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 3.1: Prepare application artifacts for deployment&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Packaging Lambda functions with dependencies&lt;/li&gt;
&lt;li&gt;Container images and ECR&lt;/li&gt;
&lt;li&gt;AWS AppConfig for feature flags and configuration&lt;/li&gt;
&lt;li&gt;Project structure for SAM/CloudFormation&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 3.2: Test applications in development environments&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;SAM local testing&lt;/li&gt;
&lt;li&gt;Integration tests against API Gateway stages&lt;/li&gt;
&lt;li&gt;Mocking external dependencies&lt;/li&gt;
&lt;li&gt;Testing event-driven applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 3.3: Automate deployment testing&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Test events for Lambda and API Gateway&lt;/li&gt;
&lt;li&gt;Lambda aliases and versions&lt;/li&gt;
&lt;li&gt;IaC templates (SAM, CloudFormation)&lt;/li&gt;
&lt;li&gt;Environment management&lt;/li&gt;
&lt;li&gt;Amazon Q Developer for test generation&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 3.4: Deploy code using AWS CI/CD services&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;CodePipeline, CodeBuild, CodeDeploy&lt;/li&gt;
&lt;li&gt;Deployment strategies: blue/green, canary, rolling&lt;/li&gt;
&lt;li&gt;Rollback strategies&lt;/li&gt;
&lt;li&gt;API Gateway stages and custom domains&lt;/li&gt;
&lt;li&gt;Dynamic deployments with staging variables&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Domain 4
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Troubleshooting and Optimization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(18%)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The smallest domain, but the questions can be tricky because they require you to diagnose problems from logs and metrics.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 4.1: Assist in root cause analysis&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Debugging code and interpreting error messages&lt;/li&gt;
&lt;li&gt;CloudWatch Logs, metrics, and traces&lt;/li&gt;
&lt;li&gt;CloudWatch Logs Insights queries&lt;/li&gt;
&lt;li&gt;Custom metrics with Embedded Metric Format&lt;/li&gt;
&lt;li&gt;Troubleshooting deployment failures&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 4.2: Instrument code for observability&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Logging vs monitoring vs observability&lt;/li&gt;
&lt;li&gt;Structured logging with correlation IDs&lt;/li&gt;
&lt;li&gt;X-Ray tracing: segments, subsegments, annotations&lt;/li&gt;
&lt;li&gt;CloudWatch alarms and SNS notifications&lt;/li&gt;
&lt;li&gt;Health checks and readiness probes&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Task 4.3: Optimize applications&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Lambda concurrency and performance tuning&lt;/li&gt;
&lt;li&gt;CloudFront caching strategies&lt;/li&gt;
&lt;li&gt;ElastiCache for application-level caching&lt;/li&gt;
&lt;li&gt;SNS subscription filter policies&lt;/li&gt;
&lt;li&gt;Identifying bottlenecks from logs and metrics&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key AWS Services to Know
&lt;/h2&gt;

&lt;p&gt;Here's a quick reference of the services that come up most on this exam:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compute:&lt;/strong&gt; Lambda, EC2, ECS, Fargate, Elastic Beanstalk&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API &amp;amp; Integration:&lt;/strong&gt; API Gateway, EventBridge, Step Functions, SQS, SNS, Kinesis&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data:&lt;/strong&gt; DynamoDB, S3, ElastiCache, DAX, RDS, OpenSearch&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security:&lt;/strong&gt; IAM, Cognito, KMS, Secrets Manager, SSM Parameter Store, ACM&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD:&lt;/strong&gt; CodePipeline, CodeBuild, CodeDeploy, CloudFormation, SAM, AppConfig&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability:&lt;/strong&gt; CloudWatch (Logs, Metrics, Alarms, Dashboards), X-Ray, CloudTrail&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer Tools:&lt;/strong&gt; AWS SDK, AWS CLI, SAM CLI, Amazon Q Developer&lt;/p&gt;




&lt;h2&gt;
  
  
  How This Series Is Structured
&lt;/h2&gt;

&lt;p&gt;Each article in this series maps directly to an exam task. We'll (You and I...and perhaps your AI Assistant, since that's how we do things these days) cover the concepts you need to know, then get hands-on with real code and CLI commands. &lt;/p&gt;

&lt;p&gt;The goal is that by the end of the series, you've not only studied the material but you've built things with it...&lt;/p&gt;




&lt;h2&gt;
  
  
  Before You Start
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Recommended experience:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At least 1 year developing and maintaining AWS applications&lt;/li&gt;
&lt;li&gt;Comfortable with at least one programming language (Python, JavaScript/TypeScript, Java, C#, or Go)&lt;/li&gt;
&lt;li&gt;Familiar with the AWS Console, CLI, and at least one AWS SDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What you'll need to follow along:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; An AWS account (free tier covers most of what we'll do)&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; AWS CLI v2 installed and configured&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; SAM CLI installed&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; A code editor (VS Code recommended)&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; Python 3.12+ or Node.js 20+ (we'll use both in examples)&lt;/p&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://d1.awsstatic.com/training-and-certification/docs-dev-associate/AWS-Certified-Developer-Associate_Exam-Guide.pdf" rel="noopener noreferrer"&gt;AWS Certified Developer Associate Exam Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/aws-certification/latest/developer-associate-02/developer-associate-02-domain1.html" rel="noopener noreferrer"&gt;AWS Certified Developer - Associate Exam Guide (DVA-C02)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🚀&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>developer</category>
    </item>
    <item>
      <title>My Solutions Architect Associate Certification Journey and Resources to Certify With Confidence</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Thu, 23 Apr 2026 18:46:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/my-solutions-architect-associate-certification-journey-and-resources-to-certify-with-confidence-5h16</link>
      <guid>https://dev.to/aws-builders/my-solutions-architect-associate-certification-journey-and-resources-to-certify-with-confidence-5h16</guid>
      <description>&lt;p&gt;☁️ &lt;strong&gt;Exam Guide:&lt;/strong&gt; Solutions Architect Associate &lt;br&gt;
&lt;strong&gt;Resources to Certify With Confidence&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;My Solutions Architect Associate Certification Journey&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧭 First Things First
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The First Attempt
&lt;/h3&gt;

&lt;p&gt;The first time I sat the Solutions Architect Associate exam, I was anxious.  &lt;/p&gt;

&lt;p&gt;This time? Still anxious.  &lt;/p&gt;

&lt;p&gt;Outcome? Passed both times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.credly.com/badges/35a2a375-cb7e-48d3-8692-fc9f4f16d8cc" rel="noopener noreferrer"&gt;Ntombizakhona Mabaso - AWS Certified Solutions Architect Associate&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So no, the nerves don’t magically disappear just because you’ve done it before. You just get better at performing &lt;em&gt;while&lt;/em&gt; anxious which feels like a life skill AWS forgot to list in the exam guide.&lt;/p&gt;

&lt;p&gt;That said, I do wish I hadn’t let my certification expire. The smarter move would’ve been to renew by going straight for the Professional exam.&lt;/p&gt;

&lt;p&gt;But: we live, we learn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Don’t let your Associate cert expire. Use it as a stepping stone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Renew with Professional certifications and&lt;/li&gt;
&lt;li&gt;Build into Specialties
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Future you&lt;/em&gt; will appreciate the efficiency. &lt;br&gt;
&lt;strong&gt;Present you&lt;/strong&gt; might procrastinate anyway, but at least now you’ve been warned.&lt;/p&gt;

&lt;h2&gt;
  
  
  📘Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If You Took Cloud Practitioner Seriously, You’re Already Ahead
&lt;/h3&gt;

&lt;p&gt;If you didn’t cut corners preparing for Cloud Practitioner, you’re in a strong position for the Solutions Architect Associate.&lt;/p&gt;

&lt;p&gt;These exams aren’t isolated. They evolve together.&lt;/p&gt;

&lt;p&gt;As cloud knowledge becomes more mainstream and general:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Foundational exams get more demanding and&lt;/li&gt;
&lt;li&gt;Associate exams become a bridge to Professional-level thinking
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if SAA-C03 feels familiar at times, that’s not a coincidence. It’s intentional.&lt;/p&gt;

&lt;p&gt;You’re not starting from scratch. You’re building on what you already know.&lt;br&gt;
So, revisit the resources here: &lt;a href="https://dev.to/aws-builders/my-cloud-practitioner-certification-journey-and-the-resources-to-certify-with-confidence-81n"&gt;Resources&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ The Resource That Actually Made a Difference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CloudPulse
&lt;/h3&gt;

&lt;p&gt;The most valuable part of my preparation wasn’t another practice test. It was building something real.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ntombizakhona.medium.com/capstone-project-architecture-review-38db82027d5f" rel="noopener noreferrer"&gt;Capstone Project: Architecture Review&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hands-on work changes everything.&lt;/p&gt;

&lt;p&gt;It turns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“This looks complicated” → “Oh, I’ve seen this before”
&lt;/li&gt;
&lt;li&gt;“Tricky question” → “This is obviously the better design”
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you rely purely on theory, you might scrape a pass or not pass at all. This exam expects you to think like an architect, not a glossary.&lt;/p&gt;

&lt;p&gt;So yes, build something. &lt;br&gt;
Even a small project is better than memorizing services like you’re cramming for a trivia night.&lt;/p&gt;

&lt;h2&gt;
  
  
  💸 The Frugal Architect
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Domain Four Feels Repetitive… Because It’s Important
&lt;/h3&gt;

&lt;p&gt;Domain Four focuses on cost optimization. Being a &lt;strong&gt;Frugal Architect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If it feels repetitive, that’s because cost is embedded in nearly every AWS decision. AWS wants to make absolutely sure you don’t forget that running unnecessary resources is basically setting money on fire.&lt;/p&gt;

&lt;p&gt;And unlike your bad subscription habits, this one actually matters.&lt;/p&gt;

&lt;p&gt;You may not see cost emphasized this heavily again in other exams, so pay attention here. Not just for the exam, but because in real-world architecture, cost-efficient solutions are what separate good engineers from expensive ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 What the SAA-C03 Really Teaches You
&lt;/h2&gt;

&lt;p&gt;It’s not just about services or patterns.&lt;/p&gt;

&lt;p&gt;It’s about &lt;strong&gt;resilience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The exam is broad. &lt;br&gt;
Concepts overlap. &lt;/p&gt;

&lt;p&gt;At some point, you’ll wonder if you’re:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;overthinking everything
&lt;/li&gt;
&lt;li&gt;studying the wrong topics
&lt;/li&gt;
&lt;li&gt;or slowly losing your grip on reality
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All normal.&lt;/p&gt;

&lt;p&gt;Push through anyway.&lt;/p&gt;

&lt;p&gt;Associate exams aren’t just testing knowledge. They’re building your endurance for continuous learning. And if you’re aiming for Professional or Specialty certifications, you’ll need that stamina.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Finally
&lt;/h2&gt;

&lt;p&gt;You don’t pass this exam by knowing everything.&lt;/p&gt;

&lt;p&gt;You pass by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understanding enough
&lt;/li&gt;
&lt;li&gt;recognizing patterns
&lt;/li&gt;
&lt;li&gt;and not panicking when AWS gives you four answers that all seem correct
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which they will. Repeatedly. For fun.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Additional Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/pdfs/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03.pdf" rel="noopener noreferrer"&gt;AWS SAA-C03 Exam Guide (PDF)&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; &lt;a href="https://dev.to/ntombizakhona/series/35366"&gt;Exam Guide - Solutions Architect Associate&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; &lt;a href="https://ntombizakhona.medium.com/capstone-project-architecture-review-38db82027d5f" rel="noopener noreferrer"&gt;Capstone Project (CloudPulse)&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; &lt;a href="https://thefrugalarchitect.com/" rel="noopener noreferrer"&gt;The Frugal Architect&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; &lt;a href="https://dev.to/aws-builders/my-cloud-practitioner-certification-journey-and-the-resources-to-certify-with-confidence-81n"&gt;My Cloud Practitioner Certification Journey and the Resources to Certify with Confidence&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Good luck with your exam! 🚀&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>solutionsarchitect</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Technologies And Concepts: Cheat Sheet for Solutions Architect Associate (SAA-C03)</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Wed, 22 Apr 2026 17:31:28 +0000</pubDate>
      <link>https://dev.to/aws-builders/technologies-and-concepts-cheat-sheet-for-solutions-architect-associate-saa-c03-h52</link>
      <guid>https://dev.to/aws-builders/technologies-and-concepts-cheat-sheet-for-solutions-architect-associate-saa-c03-h52</guid>
      <description>&lt;p&gt;☁️ &lt;strong&gt;Exam Guide:&lt;/strong&gt; Solutions Architect Associate&lt;br&gt;
&lt;strong&gt;Technologies And Concepts Cheat Sheet&lt;/strong&gt;&lt;br&gt;
📘 &lt;em&gt;Cheat Sheet&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The SAA-C03 exam guide lists technologies and concepts across all four domains. This cheat sheet consolidates that information into a &lt;strong&gt;compact, exam-aligned reference.&lt;/strong&gt; Organized domain by domain.  Designed for quick review and efficient study.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📖 Exam Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;th&gt;Info&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Exam Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SAA-C03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Questions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;65 total (50 scored, 15 unscored)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Passing Score&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;720 / 1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Question Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple choice &amp;amp; Multiple response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Experience Required&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1+ year hands-on designing cloud solutions on AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Domain Weightings
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Design Secure Architectures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Design Resilient Architectures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;26%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Design High-Performing Architectures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Design Cost-Optimized Architectures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔒 Domain 1
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Design Secure Architectures
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1.1&lt;/strong&gt; Secure Access to AWS Resources
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;IAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Users, Groups, Roles, Policies: Design flexible authorization models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;IAM Identity Center&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Centralized SSO across multiple AWS accounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MFA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apply to IAM users and root users as a security best practice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cross-Account Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use IAM Roles + STS for role switching and cross-account patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Organizations &amp;amp; SCPs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manage multi-account security strategy with Service Control Policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Control Tower&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automate landing zones and guardrails across accounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Resource Policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Determine when to use resource-based vs identity-based policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Federated Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Directory service + IAM roles for external identity federation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Least Privilege&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core security principle: grant only minimum required permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Shared Responsibility Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS secures the cloud &amp;amp; you secure what's in it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1.2&lt;/strong&gt; Secure Workloads and Applications
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPC Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security groups, route tables, NACLs, NAT gateways&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Subnets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Public vs private subnet segmentation strategies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Shield&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DDoS protection (Standard free, Advanced paid)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS WAF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web Application Firewall for Layer 7 (SQL injection, XSS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Secrets Manager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rotate, manage, retrieve secrets (DB credentials, API keys)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Cognito&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User authentication for web/mobile apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS GuardDuty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Threat detection using ML on logs/events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Macie&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Discover and protect sensitive data (PII) in S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Site-to-Site VPN and Client VPN for encrypted connectivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Direct Connect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dedicated private network connection to AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1.3&lt;/strong&gt; Data Security Controls
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed key creation, rotation, and control for encryption at rest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ACM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Certificate Manager: TLS/SSL for encryption in transit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CloudHSM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hardware Security Module for customer-managed key control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Data Classification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Categorize data by sensitivity to apply appropriate controls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;S3 Versioning &amp;amp; MFA Delete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protect object data from accidental deletion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Backup &amp;amp; Replication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Implement data backup, point-in-time recovery, cross-region replication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Data Lifecycle Policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manage retention and expiry of data at rest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Align AWS services to regulatory requirements (GDPR, HIPAA, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🏗️ Domain 2
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Design Resilient Architectures
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.1&lt;/strong&gt; Scalable and Loosely Coupled Architectures
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decouple components with message queuing (Standard and FIFO)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon SNS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pub/sub messaging for fan-out patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon EventBridge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Event-driven routing across AWS services and SaaS apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Step Functions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Workflow orchestration for distributed applications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create, publish, and manage REST/HTTP/WebSocket APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon AppFlow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed data integration between SaaS apps and AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS AppSync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed GraphQL API service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Serverless Patterns&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lambda + API Gateway + SQS/SNS for event-driven design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Microservices&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stateless vs stateful workloads &amp;amp; Independent scaling of components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Caching Strategies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reduce load &amp;amp; know when to use caching vs direct reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Horizontal vs Vertical Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scale out (add instances) vs scale up (bigger instance)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Load Balancers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ALB (Layer 7), NLB (Layer 4), GLB (Layer 3/4 for appliances)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon MQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed message broker (ActiveMQ/RabbitMQ) for migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Multi-tier Architectures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web / App / DB tiers with distinct roles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CDN / Edge Accelerators&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CloudFront for caching, Global Accelerator for routing performance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.2&lt;/strong&gt; Highly Available and Fault-Tolerant Architectures
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Availability Zones&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deploy across ≥2 AZs for high availability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Regions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Choose regions based on latency, compliance, and redundancy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Disaster Recovery Strategies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Backup &amp;amp; Restore → Pilot Light → Warm Standby → Active-Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RPO / RTO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recovery Point Objective (data loss tolerance) vs Recovery Time Objective (downtime tolerance)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Route 53&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DNS with health checks, failover routing, latency-based routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RDS Proxy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pooled DB connections for Lambda and high-concurrency apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Distributed Design Patterns&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retry with backoff, circuit breaker, bulkhead patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Service Quotas &amp;amp; Throttling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Plan for limits in standby environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS X-Ray&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distributed tracing for workload visibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Immutable Infrastructure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Replace rather than patch: ensures consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Auto Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;EC2 Auto Scaling + AWS Auto Scaling for elastic capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Storage Durability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3 (11 9s), EBS (99.999%), choose appropriate tier&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚡ Domain 3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Design High-Performing Architectures
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3.1&lt;/strong&gt; Storage Solutions
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Service / Concept&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Object storage: scalable, durable, lifecycle policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon EBS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Block storage for EC2: SSD (gp3, io2) or HDD (st1, sc1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon EFS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed NFS: shared file storage for Linux workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon FSx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed file systems: Windows (SMB), Lustre (HPC), NetApp, OpenZFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Storage Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hybrid storage: file, volume, tape gateway types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Storage Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Object vs File vs Block: know performance and use-case differences&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;S3 Storage Classes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standard, Intelligent-Tiering, IA, Glacier, Glacier Deep Archive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3.2&lt;/strong&gt; Compute Solutions
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Service / Concept&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon EC2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Virtual machines: choose instance type/family for workload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;EC2 Auto Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatically add/remove instances based on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless functions: event-driven, scale to zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Fargate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless containers: no EC2 management needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon ECS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Container orchestration on EC2 or Fargate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon EKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed Kubernetes: supports Anywhere and Distro variants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Batch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed batch processing: compute-intensive jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon EMR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Big data on managed Hadoop/Spark clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Elastic Beanstalk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PaaS: deploy web apps without managing infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Outposts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS infrastructure on-premises (hybrid)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Wavelength&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deploy workloads at the edge of 5G networks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  3.3 Database Solutions
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Service / Concept&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon RDS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed relational DB: MySQL, PostgreSQL, SQL Server, Oracle, MariaDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Aurora&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High-performance relational DB (MySQL/PostgreSQL compatible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Aurora Serverless&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On-demand autoscaling for Aurora (v2 generally available)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless NoSQL: millisecond latency at any scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon ElastiCache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In-memory caching: Redis (complex data) vs Memcached (simple)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Redshift&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data warehouse: columnar storage for analytics queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon DocumentDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed MongoDB-compatible document database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Neptune&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Graph database for connected data (social graphs, fraud detection)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Keyspaces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed Apache Cassandra-compatible service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Read Replicas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Offload read traffic &amp;amp; know when to use vs Multi-AZ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Caching Patterns&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cache-aside, write-through, TTL strategies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DB Capacity Planning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Capacity Units (DynamoDB), Provisioned IOPS, instance sizing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3.4&lt;/strong&gt; Network Architectures
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Service / Concept&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon VPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated virtual network: subnets, route tables, IGW, NAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon CloudFront&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CDN: cache content at edge locations globally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Global Accelerator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Route users to optimal endpoints using AWS global network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Elastic Load Balancing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ALB (HTTP/S), NLB (TCP/UDP), GLB (appliances)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Direct Connect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dedicated private line to AWS (predictable performance)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Transit Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hub-and-spoke for connecting many VPCs and on-prem networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPC Peering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct VPC-to-VPC connectivity (no transitive routing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS PrivateLink&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Private access to AWS services and third-party services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Route 53&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DNS. Routing policies: simple, weighted, latency, failover, geolocation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Network Topology&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Global, hybrid, multi-tier &amp;amp; design for scale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3.5&lt;/strong&gt; Data Ingestion and Transformation
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Service / Concept&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Kinesis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time streaming data: Data Streams, Data Firehose, Video Streams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Data Firehose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Load streaming data to S3, Redshift, OpenSearch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Glue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless ETL: transform and catalog data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Athena&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless SQL queries on S3 data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Lake Formation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build, secure, and manage data lakes on S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon EMR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Process large datasets with Hadoop, Spark, Hive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon MSK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed Apache Kafka for streaming pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS DataSync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automate data transfer between on-prem and AWS storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Transfer Family&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed SFTP/FTPS/FTP to S3 or EFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon QuickSuite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;BI and data visualization service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon OpenSearch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Search and analytics &amp;amp; also supports vector similarity (RAG)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Redshift&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query structured data at petabyte scale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  💰 Domain 4
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Design Cost-Optimized Architectures
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4.1&lt;/strong&gt; Cost-Optimized Storage
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;S3 Storage Classes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Match class to access frequency &amp;amp; Glacier for archival&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;S3 Lifecycle Policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automate transitions between storage classes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;S3 Intelligent-Tiering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auto-move objects between tiers based on access patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;EBS Volume Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;gp3 vs io2 vs st1 vs sc1 &amp;amp; match to IOPS and cost needs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Requester Pays&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transfer cost charged to requester, not bucket owner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Data Lifecycle Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retain only what's needed &amp;amp; expire or archive the rest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Hybrid Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DataSync, Transfer Family, Storage Gateway for on-prem cost reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Backup Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Balance recovery needs with cost (snapshots, replication)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  4.2 Cost-Optimized Compute
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;On-Demand Instances&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pay per use: highest flexibility, highest per-hour cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Reserved Instances&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1 or 3 year commitment: up to 72% savings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Savings Plans&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Flexible commitment (Compute, EC2, SageMaker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Spot Instances&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Up to 90% savings for fault-tolerant/interruptible workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Compute Optimizer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ML-based recommendations for right-sizing EC2, Lambda, EBS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Serverless Application Repository&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pre-built serverless apps: reduce build cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;EC2 Hibernation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Save instance state to EBS: resume without full reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Containerization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ECS/EKS/Fargate for higher density and cost efficiency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Instance Families&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;General purpose, compute optimized, memory optimized, storage optimized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VMware Cloud on AWS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extend VMware workloads to AWS without refactoring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4.3&lt;/strong&gt; Cost-Optimized Databases
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB On-Demand vs Provisioned&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On-demand for unpredictable; provisioned for predictable + cheaper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Aurora Serverless&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pay per ACU-hour: ideal for intermittent workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RDS Reserved Instances&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commit to 1 or 3 years for significant savings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Read Replicas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Offload reads to reduce primary DB load (and cost)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DB Snapshot Policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Balance frequency vs storage cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ElastiCache reduces DB query load and cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Data Retention Policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Define how long to keep data: archive vs delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Right-Sized DB Instances&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Don't over-provision: use metrics to guide sizing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  4.4 Cost-Optimized Network Architectures
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What to Know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;NAT Gateway vs NAT Instance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NAT Gateway scales automatically but costs more &amp;amp; NAT instance is cheaper at low traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPC Endpoints&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Eliminate NAT costs for S3/DynamoDB &amp;amp; use Gateway Endpoints (free)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Direct Connect vs VPN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct Connect more expensive but predictable; VPN cheaper for low volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Region-to-Region Transfer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data egress fees apply &amp;amp; minimize cross-region traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Same-AZ Traffic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free &amp;amp; architect to keep traffic within same AZ where possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CloudFront&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reduce origin data transfer costs with edge caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Transit Gateway Pricing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Attachment + data processing fees &amp;amp; evaluate vs VPC peering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Throttling Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use API Gateway throttling to control overuse and cost spikes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🛠️ AWS Cost Management Tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Cost Explorer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Visualize and analyze historical spend and forecast costs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Budgets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Set spend/usage thresholds with alerts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Cost and Usage Report&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Granular billing data exportable to S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Savings Plans&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Flexible commitment model for compute savings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost Allocation Tags&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tag resources to attribute costs to teams/projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Compute Optimizer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Right-sizing recommendations based on usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Trusted Advisor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Best-practice checks across cost, security, performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Well-Architected Tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Review architecture against the Well-Architected Framework&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  💡 Disaster Recovery Strategy Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;RPO&lt;/th&gt;
&lt;th&gt;RTO&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backup &amp;amp; Restore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;💰 Lowest&lt;/td&gt;
&lt;td&gt;Back up to S3/Glacier &amp;amp; restore on failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pilot Light&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;10s of minutes&lt;/td&gt;
&lt;td&gt;💰💰&lt;/td&gt;
&lt;td&gt;Core services always running &amp;amp;scale up on failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Warm Standby&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Seconds/Minutes&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;💰💰💰&lt;/td&gt;
&lt;td&gt;Scaled-down live environment &amp;amp; quickly scale to full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Active-Active&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Near zero&lt;/td&gt;
&lt;td&gt;Near zero&lt;/td&gt;
&lt;td&gt;💰💰💰💰 Highest&lt;/td&gt;
&lt;td&gt;Full duplicate environment &amp;amp; traffic split between sites&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔑 Key Abbreviations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Abbreviation&lt;/th&gt;
&lt;th&gt;Full Term&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Identity and Access Management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Service Control Policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MFA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-Factor Authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;STS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security Token Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS Certificate Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Key Management Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Virtual Private Cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NACL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Network Access Control List&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ALB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Application Load Balancer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NLB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Network Load Balancer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GLB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gateway Load Balancer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CDN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Content Delivery Network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RPO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recovery Point Objective&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recovery Time Objective&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Disaster Recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EBS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Elastic Block Store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EFS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Elastic File System&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FSx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Amazon FSx (managed file systems)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple Queue Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SNS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple Notification Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ETL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extract, Transform, Load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HDD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard Disk Drive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Solid State Drive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IOPS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Input/Output Operations Per Second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reserved Instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Aurora Capacity Unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PII&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Personally Identifiable Information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single Sign-On&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🚀 In Scope AWS Services Quick Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Compute
&lt;/h3&gt;

&lt;p&gt;Amazon EC2 · EC2 Auto Scaling · AWS Lambda · AWS Fargate · AWS Elastic Beanstalk · AWS Batch · AWS Outposts · VMware Cloud on AWS · AWS Wavelength · AWS Serverless Application Repository&lt;/p&gt;

&lt;h3&gt;
  
  
  Containers
&lt;/h3&gt;

&lt;p&gt;Amazon ECR · Amazon ECS · ECS Anywhere · Amazon EKS · EKS Anywhere · Amazon EKS Distro&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage
&lt;/h3&gt;

&lt;p&gt;Amazon S3 · Amazon EBS · Amazon EFS · Amazon FSx · AWS Storage Gateway · AWS Snow Family&lt;/p&gt;

&lt;h3&gt;
  
  
  Database
&lt;/h3&gt;

&lt;p&gt;Amazon RDS · Amazon Aurora · Aurora Serverless · Amazon DynamoDB · Amazon ElastiCache · Amazon Redshift · Amazon DocumentDB · Amazon Neptune · Amazon Keyspaces&lt;/p&gt;

&lt;h3&gt;
  
  
  Networking &amp;amp; Content Delivery
&lt;/h3&gt;

&lt;p&gt;Amazon VPC · Amazon CloudFront · AWS Direct Connect · Elastic Load Balancing · AWS Global Accelerator · AWS PrivateLink · Amazon Route 53 · AWS Site-to-Site VPN · AWS Client VPN · AWS Transit Gateway&lt;/p&gt;

&lt;h3&gt;
  
  
  Analytics
&lt;/h3&gt;

&lt;p&gt;Amazon Athena · Amazon EMR · AWS Glue · Amazon Kinesis · Amazon Data Firehose · Amazon Kinesis Video Streams · Amazon MSK · Amazon OpenSearch Service · Amazon QuickSuite · Amazon Redshift · AWS Lake Formation · AWS Data Exchange&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Integration
&lt;/h3&gt;

&lt;p&gt;Amazon SQS · Amazon SNS · Amazon EventBridge · Amazon MQ · AWS Step Functions · Amazon AppFlow · AWS AppSync&lt;/p&gt;

&lt;h3&gt;
  
  
  Security, Identity &amp;amp; Compliance
&lt;/h3&gt;

&lt;p&gt;AWS IAM · AWS IAM Identity Center · Amazon Cognito · AWS KMS · AWS CloudHSM · AWS ACM · Amazon GuardDuty · Amazon Macie · Amazon Detective · AWS Shield · AWS WAF · AWS Secrets Manager · AWS Directory Service · AWS Artifact · AWS Audit Manager&lt;/p&gt;

&lt;h3&gt;
  
  
  Management &amp;amp; Governance
&lt;/h3&gt;

&lt;p&gt;AWS Organizations · AWS Control Tower · AWS CloudFormation · AWS CloudTrail · Amazon CloudWatch · AWS Config · AWS Systems Manager · AWS Auto Scaling · AWS Compute Optimizer · AWS Trusted Advisor · AWS Well-Architected Tool · AWS Service Catalog · AWS Health Dashboard · AWS License Manager · Amazon Managed Grafana · Amazon Managed Service for Prometheus&lt;/p&gt;

&lt;h3&gt;
  
  
  Migration &amp;amp; Transfer
&lt;/h3&gt;

&lt;p&gt;AWS DMS · AWS DataSync · AWS Snow Family · AWS Transfer Family · AWS Application Migration Service&lt;/p&gt;

&lt;h3&gt;
  
  
  Machine Learning
&lt;/h3&gt;

&lt;p&gt;Amazon SageMaker AI · Amazon Comprehend · Amazon Kendra · Amazon Lex · Amazon Polly · Amazon Rekognition · Amazon Textract · Amazon Transcribe · Amazon Translate&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost Management
&lt;/h3&gt;

&lt;p&gt;AWS Budgets · AWS Cost Explorer · AWS Cost and Usage Report · Savings Plans&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer Tools
&lt;/h3&gt;

&lt;p&gt;AWS X-Ray&lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless
&lt;/h3&gt;

&lt;p&gt;AWS Lambda · AWS Fargate · Amazon API Gateway · Amazon DynamoDB · Amazon EventBridge · Amazon SQS · Amazon SNS&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important:&lt;/strong&gt; Always refer to the official exam guide for the most up-to-date list of in-scope and out-of-scope services.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📚 Additional Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/pdfs/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03.pdf" rel="noopener noreferrer"&gt;AWS Certified Solutions Architect – Associate (SAA-C03) Exam Guide (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/pdfs/aws-certification/latest/examguides/aws-certification-exam-guides.pdf" rel="noopener noreferrer"&gt;AWS Certification: All Exam Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/ntombizakhona/series/35366"&gt;Exam Guide: Solutions Architect Associate Series&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Good luck with your exam! 🚀&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>solutionsarchitect</category>
    </item>
    <item>
      <title>Jog Squad | A Gamified Eco-Jogging App: Fix the Earth. Fix Your Health. One Run at a Time. 🏃</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Sun, 19 Apr 2026 13:55:29 +0000</pubDate>
      <link>https://dev.to/ntombizakhona/jog-squad-a-gamified-eco-jogging-app-fix-the-earth-fix-your-health-one-run-at-a-time-3j5f</link>
      <guid>https://dev.to/ntombizakhona/jog-squad-a-gamified-eco-jogging-app-fix-the-earth-fix-your-health-one-run-at-a-time-3j5f</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-04-16"&gt;Weekend Challenge: Earth Day Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm a jogger. Not the "I ran a marathon once" kind but the &lt;em&gt;"I need to run or my brain stops working"&lt;/em&gt; kind. Running is how I think, how I decompress, how I stay sane.&lt;/p&gt;

&lt;p&gt;But here's the thing that drives me crazy: every single run, I pass litter. &lt;br&gt;
Plastic bottles in the grass. &lt;br&gt;
Wrappers caught in fences. &lt;br&gt;
Cans rolling down the sidewalk. &lt;br&gt;
It's literally everywhere, and most people  myself included, honestly just jog right past it...hoping the Municipality takes care of it.&lt;/p&gt;

&lt;p&gt;So, I love this planet. I love being outside in it. And it frustrates me that the places I run through are slowly being buried in trash that nobody takes responsibility for.&lt;/p&gt;

&lt;p&gt;So when this Earth Day challenge dropped, I knew exactly what to build. Not another carbon calculator. Not another awareness app. Something that actually connects the act of running. Something I and many others already do every day. Something that makes picking up trash feel rewarding instead of inconvenient... &lt;br&gt;
Enter: &lt;strong&gt;🏃 #JogSquad&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;



&lt;p&gt;&lt;strong&gt;Jog Squad&lt;/strong&gt; is a gamified eco-jogging app that turns every outdoor run into an environmental cleanup mission.&lt;/p&gt;

&lt;p&gt;The app connects your personal health to the planet's health through three pillars:&lt;/p&gt;

&lt;p&gt;🗑️ &lt;strong&gt;Litter Detection &amp;amp; Cleanup:&lt;/strong&gt; Snap a photo of your route and Gemini Vision AI identifies the litter, scores the area's cleanliness, and explains the environmental impact. Pick up what you find, log it, and earn points.&lt;/p&gt;

&lt;p&gt;⚡ &lt;strong&gt;Electricity Saved:&lt;/strong&gt; Every outdoor run you do instead of using a treadmill saves &lt;em&gt;real&lt;/em&gt; electricity. Especially in South Africa, where LoadShedding (Controlled Power Cuts) can rear its head any second. The app tracks and quantifies this because treadmills consume roughly 0.7 kWh per hour, and running outside costs zero.&lt;/p&gt;

&lt;p&gt;🏃 &lt;strong&gt;Health &amp;amp; Fitness:&lt;/strong&gt; GPS-tracked runs with live mapping, pace calculation, and AI-powered coaching insights that get smarter as you log more runs.&lt;/p&gt;

&lt;p&gt;The twist? &lt;strong&gt;You lose points for running on a treadmill and for skipping litter cleanup.&lt;/strong&gt; &lt;em&gt;Accountability through gamification&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Key features:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live GPS&lt;/strong&gt; run tracking with real-time map (or demo mode for presentations)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI environment scanning:&lt;/strong&gt; upload a photo, get litter detection and  cleanliness score&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Before/after photo comparison:&lt;/strong&gt; Gemini narrates your cleanup impact&lt;/li&gt;
&lt;li&gt;Points system with rewards AND penalties&lt;/li&gt;
&lt;li&gt;AI-generated &lt;strong&gt;daily missions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Squad &lt;strong&gt;leaderboard&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full impact dashboard:&lt;/strong&gt; CO₂ saved, electricity saved, litter removed&lt;/li&gt;
&lt;li&gt;AI &lt;strong&gt;run reflections&lt;/strong&gt; with pattern detection across your run history&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;




&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://jog-squad-3921552307.africa-south1.run.app/"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;strong&gt;🔗 Live on Google Cloud Run:&lt;/strong&gt; &lt;a href="https://jog-squad-3921552307.africa-south1.run.app/" rel="noopener noreferrer"&gt;Jog Squad&lt;/a&gt;

&lt;p&gt;&lt;strong&gt;To try the full flow without going outside:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1.&lt;/strong&gt; Go to &lt;strong&gt;Log Run&lt;/strong&gt; → click &lt;strong&gt;"Demo Run (simulated GPS)"&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Watch the live map track a simulated jog around Johannesburg&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Finish the run → get AI analysis → log some litter cleanup&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; Check the &lt;strong&gt;Impact&lt;/strong&gt; page to see your environmental stats&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; Try &lt;strong&gt;Scan&lt;/strong&gt; → upload any outdoor photo → Gemini analyzes the litter&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Ntombizakhona" rel="noopener noreferrer"&gt;
        Ntombizakhona
      &lt;/a&gt; / &lt;a href="https://github.com/Ntombizakhona/jog-squad" rel="noopener noreferrer"&gt;
        jog-squad
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Fix the Earth. Fix Your Health. Gamified eco-jogging with Gemini AI.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🏃 Jog Squad&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Fix the Earth. Fix Your Health. One Run at a Time.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Jog Squad is a gamified eco-jogging app that turns every outdoor run into an environmental cleanup opportunity. Using Google's Gemini AI, it detects litter from photos, tracks your environmental impact, and rewards you for making the planet cleaner — one jog at a time.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🌍 The Problem&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Litter is everywhere. Treadmills waste electricity. People jog past trash every day without thinking about it.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;💡 The Solution&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Jog Squad connects your health to the earth's health:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run outdoors&lt;/strong&gt; instead of on a treadmill → save electricity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scan your route&lt;/strong&gt; with AI → see the litter problem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick up trash&lt;/strong&gt; during your run → earn points&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Before/after photos&lt;/strong&gt; → prove your impact with AI comparison&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track everything&lt;/strong&gt; → CO₂ saved, electricity saved, litter removed&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Features&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;🏃 &lt;strong&gt;Run Logging&lt;/strong&gt; — Distance, time, indoor/outdoor, mood tracking&lt;/li&gt;
&lt;li&gt;📸 &lt;strong&gt;AI Environment&lt;/strong&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Ntombizakhona/jog-squad" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React 18 + Vite, with Leaflet for live mapping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js + Express&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; Google Gemini 2.5 Flash (text generation + vision)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; Google Cloud Firestore (Native mode)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy:&lt;/strong&gt; Google Cloud Run (containerized with Docker)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Gemini Integration (6 distinct uses)
&lt;/h3&gt;

&lt;p&gt;This isn't "AI sprinkled on top." Gemini is doing real work across the entire app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run Reflections:&lt;/strong&gt; After each run, Gemini receives your distance, pace, mood, location type, and your past run history. It generates personalized performance insights, improvement tips, and eco-impact statements. With enough runs, it detects patterns like "you slow down after 3km" or "you perform better in cooler weather."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Environment Scanning:&lt;/strong&gt; Upload a photo of your running route. Gemini Vision analyzes it and returns structured JSON: litter types detected (plastic, metal, paper, glass), estimated counts, a cleanliness score from 1-10, and an environmental impact statement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Before/After Comparison:&lt;/strong&gt;  Upload two photos (before and after cleanup). Gemini compares them and narrates the improvement, estimates items removed, and generates an impact statement. This is the feature that makes the cleanup feel real.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cleanup Summaries:&lt;/strong&gt; When you log collected litter, Gemini explains how long those items would take to decompose and what the real-world impact of removing them is.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Daily Missions:&lt;/strong&gt; Gemini generates personalized daily challenges scaled to your experience level. New users get "Run 0.5km and pick up 1 item." Experienced users get harder goals.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Squad Leaderboard:&lt;/strong&gt; Gemini generates a realistic mock leaderboard that places you among fictional squad members, making the app feel social even as a single-user POC.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every Gemini call returns structured JSON that the app parses and renders. The prompts are carefully designed to get consistent, parseable responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  GPS Tracking
&lt;/h3&gt;

&lt;p&gt;The app uses the browser's Geolocation API with &lt;code&gt;watchPosition&lt;/code&gt; for real-time tracking. Key decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Haversine formula&lt;/strong&gt; for distance calculation between GPS coordinates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accuracy filtering:&lt;/strong&gt; readings over 30m accuracy are discarded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jitter filtering:&lt;/strong&gt; movements under 3m are ignored (GPS noise)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jump filtering:&lt;/strong&gt; movements over 500m in one reading are rejected (GPS glitches)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo mode:&lt;/strong&gt; a simulated 20-point route around Emmarentia Dam, Johannesburg, with points dropped every 1.5 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Points System
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Distance run&lt;/td&gt;
&lt;td&gt;+10 per km&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outdoor run&lt;/td&gt;
&lt;td&gt;+20 bonus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Litter collected&lt;/td&gt;
&lt;td&gt;+5 per item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Treadmill run&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;-15 penalty&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skipped cleanup&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;-10 penalty&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The penalties are the key design decision. Most eco apps only reward. Jog Squad also punishes because running on a treadmill wastes electricity, and jogging past litter without picking it up is a missed opportunity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloud Run Deployment
&lt;/h3&gt;

&lt;p&gt;The Dockerfile uses a multi-stage build: first stage builds the React client with Vite, second stage runs the Express server and serves the static files. Deployed to &lt;code&gt;africa-south1&lt;/code&gt; for low latency from South Africa, with Firestore in the same region.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Google Gemini:&lt;/strong&gt; Gemini is the backbone of the app, powering 6 distinct features: run reflections with pattern detection, environment photo scanning, before/after cleanup comparison, cleanup impact summaries, daily mission generation, and squad leaderboard generation. It uses both text generation and vision capabilities.&lt;/p&gt;




&lt;p&gt;To fix your health, you have to fix the earth. To fix the earth, you have to get out there. Run. Clean. Repeat.&lt;/p&gt;

&lt;p&gt;Happy Earth Day 🌍&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🏃 #JogSquad&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>Design Cost Optimized Network Architectures</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Wed, 15 Apr 2026 15:56:59 +0000</pubDate>
      <link>https://dev.to/aws-builders/design-cost-optimized-network-architectures-5fg9</link>
      <guid>https://dev.to/aws-builders/design-cost-optimized-network-architectures-5fg9</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Solutions Architect - Associate&lt;br&gt;
&lt;strong&gt;⚡ Domain 4: Design Cost-Optimized Architectures&lt;/strong&gt;&lt;br&gt;
📘 &lt;em&gt;Task Statement 4.4&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🎯 &lt;strong&gt;&lt;em&gt;Designing Cost-Optimized Network Architectures&lt;/em&gt;&lt;/strong&gt; is about selecting the &lt;strong&gt;cheapest networking design that still meets requirements for performance, availability, security, and scalability&lt;/strong&gt;.
&lt;/h3&gt;

&lt;p&gt;Start by understanding:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Traffic flow&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Connectivity needs&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Data transfer patterns&lt;/p&gt;

&lt;p&gt;Then choose:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Networking services&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Routing model&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Edge strategy&lt;/p&gt;

&lt;p&gt;Finally optimise using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;CDN&lt;/li&gt;
&lt;li&gt;NAT strategy&lt;/li&gt;
&lt;li&gt;Traffic reduction techniques&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are often deciding between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Internet Gateway vs VPN vs Direct Connect&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; NAT Gateway vs NAT Instance&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; VPC Peering vs Transit Gateway&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;4&lt;/em&gt;&lt;/strong&gt; Edge caching vs origin traffic&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;5&lt;/em&gt;&lt;/strong&gt; Cross-AZ vs single-AZ traffic  &lt;/p&gt;




&lt;h2&gt;
  
  
  📘 Knowledge
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1&lt;/strong&gt; | AWS Cost Management Features
&lt;/h3&gt;

&lt;p&gt;Network cost optimization starts with visibility into data transfer and routing charges.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cost Allocation Tags &amp;amp; Multi-Account Billing
&lt;/h4&gt;

&lt;p&gt;Network cost should be tracked by:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Environment (prod/dev/test)&lt;br&gt;
&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; Application or service&lt;br&gt;
&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; Network layer (VPC, NAT, ALB)&lt;/p&gt;

&lt;h4&gt;
  
  
  1.1 &lt;strong&gt;Cost Allocation Tags&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Used to track:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; NAT Gateway cost&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Load Balancer usage&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; VPC traffic patterns&lt;/p&gt;

&lt;h4&gt;
  
  
  1.2 &lt;strong&gt;Multi-Account Billing (AWS Organizations)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Centralized billing for:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Multiple accounts&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Environment separation&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Cost visibility across teams&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2&lt;/strong&gt; | AWS Cost Management Tools
&lt;/h3&gt;

&lt;h4&gt;
  
  
  2.1 &lt;strong&gt;Cost Explorer&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Analyze data transfer trends&lt;/li&gt;
&lt;li&gt;Identify expensive network paths&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2.2 &lt;strong&gt;AWS Budgets&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Alerts for unexpected network cost spikes&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2.3 &lt;strong&gt;Cost and Usage Report (CUR)&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Deep-level network cost analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3&lt;/strong&gt; | Load Balancing Concepts
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Application Load Balancer (ALB)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Layer 7 routing&lt;/li&gt;
&lt;li&gt;Cost based on usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4&lt;/strong&gt; | NAT Gateways
&lt;/h3&gt;

&lt;h4&gt;
  
  
  NAT Gateways vs NAT Instances
&lt;/h4&gt;

&lt;h4&gt;
  
  
  4.1 &lt;strong&gt;NAT Gateway&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Managed&lt;/li&gt;
&lt;li&gt;Highly available&lt;/li&gt;
&lt;li&gt;Higher cost (per hour + per GB)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Production&lt;/em&gt; → &lt;strong&gt;NAT Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Avoid NAT Gateway usage for AWS services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gateway Endpoint (S3, DynamoDB) → &lt;strong&gt;FREE&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Interface Endpoint → &lt;strong&gt;Private connectivity&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.2 &lt;strong&gt;NAT Instance&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;EC2-based&lt;/li&gt;
&lt;li&gt;Cheaper but requires management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Dev/Test&lt;/em&gt; → &lt;strong&gt;NAT Instance&lt;/strong&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5&lt;/strong&gt; | Network Connectivity Options
&lt;/h3&gt;

&lt;h4&gt;
  
  
  5.1 &lt;strong&gt;Internet Gateway (IGW)&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Public internet access&lt;/li&gt;
&lt;li&gt;No additional hourly cost &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5.2 &lt;strong&gt;AWS Site-to-Site VPN&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Encrypted over internet&lt;/li&gt;
&lt;li&gt;Quick setup&lt;/li&gt;
&lt;li&gt;Lower cost than Direct Connect&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5.3 &lt;strong&gt;AWS Direct Connect&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Dedicated private connection&lt;/li&gt;
&lt;li&gt;High performance&lt;/li&gt;
&lt;li&gt;Higher fixed cost&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;6&lt;/strong&gt; | Network Routing, Topology, and Peering
&lt;/h3&gt;

&lt;h4&gt;
  
  
  6.1 &lt;strong&gt;VPC Peering&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Direct VPC-to-VPC connection&lt;/li&gt;
&lt;li&gt;Low cost&lt;/li&gt;
&lt;li&gt;Not scalable for large architectures&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  6.2 &lt;strong&gt;AWS Transit Gateway&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Central routing hub&lt;/li&gt;
&lt;li&gt;Scalable but adds cost per attachment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;7&lt;/strong&gt; | Network Services
&lt;/h3&gt;

&lt;h4&gt;
  
  
  7.1 &lt;strong&gt;Amazon Route 53&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Route 53 is a scalable DNS and domain management service used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route user traffic to applications&lt;/li&gt;
&lt;li&gt;Improve availability with health checks&lt;/li&gt;
&lt;li&gt;Optimize routing decisions (latency, geography, failover)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  7.2 &lt;strong&gt;AWS Global Accelerator&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Optimizes routing&lt;/li&gt;
&lt;li&gt;Reduces latency&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  7.3 &lt;strong&gt;Amazon CloudFront&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Caches content globally&lt;/li&gt;
&lt;li&gt;Reduces origin load and data transfer costs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Skills
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;A&lt;/strong&gt; | NAT Strategy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Single NAT Gateway → &lt;strong&gt;cost efficient&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Multi-AZ NAT → &lt;strong&gt;high availability&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;B&lt;/strong&gt; | Connectivity Selection
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Internet access&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;IGW&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid connection&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPN&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise private link&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Direct Connect&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;C&lt;/strong&gt; | Routing Optimization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Reduce cross-AZ traffic&lt;br&gt;
&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; Use VPC endpoints&lt;br&gt;
&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; Use CloudFront for caching&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Cross-AZ Traffic&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Avoid unnecessary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-AZ calls&lt;/li&gt;
&lt;li&gt;Distributed chatty microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Cross-Region Traffic&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Use only when needed for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DR&lt;/li&gt;
&lt;li&gt;Global users&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;VPC Endpoints&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Avoid NAT Gateway usage for AWS services.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gateway Endpoint (S3, DynamoDB) → &lt;strong&gt;FREE&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Interface Endpoint → &lt;strong&gt;Private connectivity&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;D&lt;/strong&gt; | CDN Strategy
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Amazon CloudFront&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Caches content globally&lt;/li&gt;
&lt;li&gt;Reduces origin load and data transfer costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use CloudFront for:&lt;br&gt;
&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Global users&lt;br&gt;
&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; Static assets&lt;br&gt;
&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; API acceleration&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;E&lt;/strong&gt; | Workload Optimization
&lt;/h3&gt;

&lt;p&gt;Look for:&lt;br&gt;
&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Unused NAT Gateways&lt;br&gt;
&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; Cross-region traffic waste&lt;br&gt;
&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; Missing caching layers&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;F&lt;/strong&gt; | Throttling Strategy
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;RDS Proxy&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Connection pooling&lt;/li&gt;
&lt;li&gt;Reduces database/network pressure&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;API Throttling&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Prevents traffic spikes&lt;/li&gt;
&lt;li&gt;Reduces scaling cost&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;G&lt;/strong&gt; | Bandwidth Allocation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;VPN = small workloads
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Direct Connect = high traffic workloads  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Single VPN&lt;/em&gt; → &lt;strong&gt;low throughput&lt;/strong&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Multiple VPNs&lt;/em&gt; → &lt;strong&gt;higher throughput&lt;/strong&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Direct Connect&lt;/em&gt; → &lt;strong&gt;stable high bandwidth&lt;/strong&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reduce NAT cost&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPC Endpoints&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global traffic&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CloudFront&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid networking&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPN / Direct Connect&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-VPC architecture&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Transit Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple connectivity&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPC Peering&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduce DB/network load&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RDS Proxy + caching&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High bandwidth private link&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Direct Connect&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Recap Checklist ✅
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; [ ] I can choose NAT Gateway vs NAT Instance&lt;br&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; [ ] I understand VPN vs Direct Connect trade-offs&lt;br&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; [ ] I can reduce cost using VPC Endpoints&lt;br&gt;&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; [ ] I understand cross-AZ and cross-region cost impact&lt;br&gt;&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; [ ] I can apply CloudFront for edge optimization&lt;br&gt;&lt;br&gt;
&lt;strong&gt;6.&lt;/strong&gt; [ ] I understand Transit Gateway vs VPC Peering&lt;br&gt;&lt;br&gt;
&lt;strong&gt;7.&lt;/strong&gt; [ ] I can optimize ALB usage&lt;br&gt;&lt;br&gt;
&lt;strong&gt;8.&lt;/strong&gt; [ ] I can identify expensive routing patterns  &lt;/p&gt;




&lt;h2&gt;
  
  
  AWS Whitepapers and Official Documentation
&lt;/h2&gt;

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

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/aws-cost-management/aws-cost-explorer/" rel="noopener noreferrer"&gt;Cost Explorer&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/aws-cost-management/aws-budgets/" rel="noopener noreferrer"&gt;AWS Budgets&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/cur/latest/userguide/what-is-cur.html" rel="noopener noreferrer"&gt;CUR&lt;/a&gt; &lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/organizations/" rel="noopener noreferrer"&gt;Organizations&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Networking Core Services&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/vpc/" rel="noopener noreferrer"&gt;VPC&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/transit-gateway/" rel="noopener noreferrer"&gt;Transit Gateway&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html" rel="noopener noreferrer"&gt;VPC Peering&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/directconnect/" rel="noopener noreferrer"&gt;Direct Connect&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/vpn/latest/s2svpn/VPC_VPN.html" rel="noopener noreferrer"&gt;VPN&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Optimization Services&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/cloudfront/" rel="noopener noreferrer"&gt;Cloudfront&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/global-accelerator/" rel="noopener noreferrer"&gt;Global Accelerator&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints.html" rel="noopener noreferrer"&gt;VPC Endpoints&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" rel="noopener noreferrer"&gt;Load Balancing&lt;/a&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Performance Optimization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html" rel="noopener noreferrer"&gt;RDS Proxy&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;🚀&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>solutionsarchitect</category>
    </item>
  </channel>
</rss>
