<?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: Randika Madhushan Perera</title>
    <description>The latest articles on DEV Community by Randika Madhushan Perera (@randiakm).</description>
    <link>https://dev.to/randiakm</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F392119%2Fbf079ce5-b92e-4341-8672-ec54d93ed960.jpeg</url>
      <title>DEV Community: Randika Madhushan Perera</title>
      <link>https://dev.to/randiakm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/randiakm"/>
    <language>en</language>
    <item>
      <title>Our Synthetic Monitors Were Timing Out. The Scripts Were Fine.</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Sun, 18 Oct 2026 07:00:51 +0000</pubDate>
      <link>https://dev.to/randiakm/our-synthetic-monitors-were-timing-out-the-scripts-were-fine-a4m</link>
      <guid>https://dev.to/randiakm/our-synthetic-monitors-were-timing-out-the-scripts-were-fine-a4m</guid>
      <description>&lt;p&gt;How two compounding infrastructure problems masked a test-quality problem, and the dashboard we built so we'd never debug it blind again. &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;The complaint&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;code&gt;"Most of our synthetic monitors are timing out. It's not the scripts"&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's how it started. Around 140 scripted-browser monitors, a smoke test suite covering two environments running against a &lt;strong&gt;New Relic Synthetics private location&lt;/strong&gt; backed by t2.large 8 EC2s. &lt;br&gt;
Monitors were failing in bulk with &lt;code&gt;JobTimeoutError, Element never became visible, Timed-out waiting for element&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The QA team said scripts worked when run by hand. When SRE team looked at the servers and they seems fine, CPU wasn't pinned, memory was fine, nothing was crash-looping. &lt;/p&gt;

&lt;p&gt;Both were right, which is why it took a while. &lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Background: How a private location actually works&lt;/strong&gt;
&lt;/h3&gt;



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

&lt;p&gt;Three properties matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A 'lane' is one concurrent heavyweight check.&lt;/strong&gt; The job manager reserves one CPU for it self, so a 4-vCPU host gets 3 lanes. A browser check needs a full core, Chrome is no a background task. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Job managers pull. You cannot predict or control which host runs which check.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Telemetry arrives on completion. While a check is queued or running, nothing is reported. This is why "it looks fine" and "it's broken" can be both true. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;The measurement that settled the argument&lt;/strong&gt;
&lt;/h3&gt;



&lt;p&gt;Everyone was staring at CPU graphs. The decisive query took thirty seconds and nobody had run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;executionDuration&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`nr.internalQueueDuration`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`nr.externalQueueDuration`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;SyntheticCheck&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;locationLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;our-location&amp;gt;'&lt;/span&gt;
&lt;span class="n"&gt;SINCE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt; &lt;span class="n"&gt;ago&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;average execution time          77 s
average internal queue wait      2.5 s
average EXTERNAL queue wait    553 s   ← 9.2 MINUTES
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Queue wait was 7x the execution time. Worst observed 1,733 seconds, a check that sat for 29 minutes before it started. &lt;/p&gt;

&lt;p&gt;That single ratio ends the "is the scripts or the servers?" argument permanently. The scripts weren't slow. The checks weren't running. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 1:&lt;/strong&gt; measure queue wait separately from execution time. It is the one diagnostic that distinguishes a capacity problem from a code problem, and most dashboards don't show it. &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Root cause #1: Burstable instances and CPU-bound work&lt;/strong&gt;
&lt;/h3&gt;




&lt;p&gt;The fleet was 8 &lt;code&gt;t2.large&lt;/code&gt;. From AWS. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;t2.large&lt;/code&gt; property  :  value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;vCPUs:  2 → lanes = 2 − 1 = &lt;strong&gt;1 per host&lt;/strong&gt;&lt;br&gt;
CPU credits earned/hour:    36&lt;br&gt;
Baseline utilization:   30% per vCPU → &lt;strong&gt;0.6 vCPU sustained, total&lt;/strong&gt;&lt;br&gt;
Credit accrual cap: 864 (≈24 hours' worth)&lt;br&gt;
Default credit mode:    &lt;strong&gt;Standard **(T2 — not Unlimited)&lt;br&gt;
Credits on stop/start:  **Lost&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;two separate problems in one line of that table:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only 8 lanes across the whole fleet.&lt;/strong&gt; 8 hosts x 1 lane. For 140 monitors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 2:&lt;/strong&gt;  never put CPU-bond sustained work on burstable instances. And check &lt;code&gt;CPUCreditBalance&lt;/code&gt; in CloudWatch before believing a CPU graph. &lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Root cause #2: queuing in non-linear&lt;/strong&gt;
&lt;/h3&gt;



&lt;p&gt;Demand was measurable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;demand                      = 6.35 jobs/min
load-weighted avg runtime   = 77.3 s
lanes needed at 100% busy   = 6.35 × (77.3 / 60) = 8.18
lanes available             = 8
                            ────────────────────
utilization                 = 102%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not 300% over. Not catastrophically mis-sized. &lt;strong&gt;2%&lt;/strong&gt; over.&lt;/p&gt;

&lt;p&gt;And that's the trap. Queue wait doesn't rise linearly with utilization, it rises asymptotically:&lt;/p&gt;

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

&lt;p&gt;Utilisation 20% 40% 50% 60% 70% 80% 85% 90% 95% 100%&lt;br&gt;
Queue wait  ~0 s    1 s 2 s 5 s 14 s    38 s    80 s    170 s   330 s   470 s&lt;/p&gt;

&lt;p&gt;Our two measured points sit at either end of that curve: &lt;strong&gt;48% utilization -&amp;gt; 0.4 s&lt;/strong&gt;, and &lt;strong&gt;102% -&amp;gt; 438 s&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 3:&lt;/strong&gt; size for 40-55% average utilization, not 90%. The spare capacity isn't waste; It's what keeps latency bounded under random arrivals. &lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;The counter-intuitive fix: Fever servers&lt;/strong&gt;
&lt;/h3&gt;



&lt;p&gt;We replaced &lt;strong&gt;8 × t2.large&lt;/strong&gt; with &lt;strong&gt;4 × m6i.xlarge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Half the instance count. Here's why that's more capacity, not less:&lt;/p&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;8 × t2.large&lt;/th&gt;
&lt;th&gt;4 × m6i.xlarge&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;vCPU per host&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lanes per host&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total lanes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sustained vCPU per host&lt;/td&gt;
&lt;td&gt;0.6 (throttled)&lt;/td&gt;
&lt;td&gt;4.0 (full)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fleet sustained vCPU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly cost&lt;/td&gt;
&lt;td&gt;~$574&lt;/td&gt;
&lt;td&gt;~$577&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same spend. 2.5× the lanes. 3.3× the sustained CPU. The old fleet was paying a premium for capacity it was never allowed to use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average queue wait&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;438 s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.4 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average execution time&lt;/td&gt;
&lt;td&gt;78.8 s&lt;/td&gt;
&lt;td&gt;54.2 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job timeouts&lt;/td&gt;
&lt;td&gt;4.7% of checks&lt;/td&gt;
&lt;td&gt;3.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lane utilisation&lt;/td&gt;
&lt;td&gt;~102%&lt;/td&gt;
&lt;td&gt;~48%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly cost&lt;/td&gt;
&lt;td&gt;~$574&lt;/td&gt;
&lt;td&gt;~$577&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A 99.9% reduction in queue wait at flat cost.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;The surprise that changed the sizing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Execution time dropped 31% from 78.8 s to 54.2 s for the same scripts. That was the CPU throttling leaving. And it created a feedback loop in the arithmetic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lanes needed = demand × avg_runtime

Sized with THROTTLED runtime (77 s):  6.35 × 1.29 = 8.2 lanes → ~18 lanes for headroom → 6 hosts
Sized with REAL runtime     (54 s):  6.35 × 0.90 = 5.7 lanes → ~12 lanes for headroom → 4 hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;The strategy question: add capacity, or fix the schedule?&lt;/strong&gt;
&lt;/h3&gt;




&lt;p&gt;Monitors on harmonic periods (10/15/30 min) can collide. If all phases align, every monitor is due at the top of the hour at once:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdxw6nxy20hhns76plcyb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdxw6nxy20hhns76plcyb.png" alt="add capacity, or fix the schedule" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Average demand 5.8/min. Peak demand 116/min. A 20x ratio, and no amount of average capacity fixes a peak like that. &lt;/p&gt;

&lt;p&gt;So which problem did we have? &lt;/p&gt;

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




&lt;h3&gt;
  
  
  &lt;strong&gt;The Dashboard&lt;/strong&gt;
&lt;/h3&gt;




&lt;p&gt;The diagnosis above took days of ad-hoc NRQL. None of it was hard; all of it was &lt;em&gt;findable only if you knew which five queries to run&lt;/em&gt;. We didn't want to rediscover that next quarter, so we built a dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The key architectural decision: don't build a database&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The obvious design is a collector writing to Postgres, an API over it, and a UI. We nearly built that. Then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;New Relic is already the database.&lt;/strong&gt; NRDB stores all the history and NRQL queries it well. Duplicating it into RDS buys an invoice and a sync bug.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The only reason a backend exists at all is that a browser &lt;em&gt;cannot&lt;/em&gt; hold a New Relic User key, that key can read &lt;strong&gt;and modify&lt;/strong&gt; the entire account. So the backends' entire job is to hold one credential and expose a narrow, read-only surface.&lt;/p&gt;

&lt;p&gt;That reduced v1 to: static files, one Lambda, no database.&lt;/p&gt;

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

&lt;p&gt;AWS services, and why each one&lt;/p&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;Role&lt;/th&gt;
&lt;th&gt;Why this one&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;static UI&lt;/td&gt;
&lt;td&gt;Account-level Block Public Access made website hosting impossible, which forced OAC, the better pattern anyway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudFront&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTPS, auth, single origin&lt;/td&gt;
&lt;td&gt;Serving the API as a second origin on the &lt;em&gt;same&lt;/em&gt; distribution means no CORS, and the auth function covers the API too&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudFront Functions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTP Basic at the edge&lt;/td&gt;
&lt;td&gt;Cheaper and faster than Lambda@Edge for a header check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway HTTP API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;routing, throttling, authorizer&lt;/td&gt;
&lt;td&gt;~70% cheaper than REST API; everything here is a simple proxy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; (arm64)&lt;/td&gt;
&lt;td&gt;NerdGraph proxy&lt;/td&gt;
&lt;td&gt;Holds the credential. arm64 is ~20% cheaper per ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secrets Manager + KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;the New Relic key&lt;/td&gt;
&lt;td&gt;Customer-managed key so usage is auditable and revocable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSM Parameter Store&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;non-secret config + generated origin secret&lt;/td&gt;
&lt;td&gt;Changeable without a redeploy; SecureString for the machine-only secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudWatch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;alarms + ops dashboard&lt;/td&gt;
&lt;td&gt;Alarms watch the &lt;em&gt;plumbing&lt;/em&gt;; the fleet's own alarms belong in New Relic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudFormation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;five separate stacks&lt;/td&gt;
&lt;td&gt;Separate, not nested,  they change at very different rates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Strategies that mattered&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allow-listed panels, never raw NRQL.&lt;/strong&gt; The browser sends &lt;code&gt;?panel=fleet,queue&lt;/code&gt;. If it could send NRQL, the dashboard would be an open NRDB query endpoint for anyone who got past the password. Adding a panel is a code change and a code review, deliberately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One batched NerdGraph document per refresh.&lt;/strong&gt; All panels in a single request using GraphQL aliases, cached 20 s server-side. Ten viewers cost the same New Relic quota as one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alias every NRQL aggregate.&lt;/strong&gt; This one bit us hard enough to earn its own section below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Defence in depth on the API.&lt;/strong&gt; CloudFront injects a shared secret header; an API Gateway authorizer validates it; the proxy Lambda validates it &lt;em&gt;again&lt;/em&gt;. If the authorizer is ever accidentally detached, the API doesn't silently open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reject before compute.&lt;/strong&gt; Because the authorizer declares &lt;code&gt;IdentitySource: $request.header.x-origin-secret&lt;/code&gt;, API Gateway returns 401 &lt;em&gt;itself&lt;/em&gt; when the header is absent — no Lambda invocation at all. We proved it: 15 unauthenticated requests produced &lt;strong&gt;zero&lt;/strong&gt; invocations on either function. Combined with a 5 req/s throttle, worst-case abuse cost fell from ~$97/month to ~$13/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the Lambda out of the VPC.&lt;/strong&gt; It only calls public endpoints. In-VPC would add ENI cold starts and NAT data charges for nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Poll; don't build SSE.&lt;/strong&gt; Underlying data only changes once a minute. The UI polls every 20 s and animates between snapshots client-side — visually live, with no WebSocket machinery.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What the dashboard shows that New Relic doesn't&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;New Relic ships a perfectly good private-location dashboard. We import it. The custom app exists only for what doesn't exist elsewhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Queue wait as a first-class column&lt;/strong&gt; next to execution time — the ratio that ends the capacity-versus-code argument&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A live flow view&lt;/strong&gt;: queue → lanes → results, one dot per real check, coloured by outcome&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-monitor capacity consumption&lt;/strong&gt; — which monitors eat your lanes, ranked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing-check detection&lt;/strong&gt; — expected (from configured period) versus observed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A what-if simulator&lt;/strong&gt; — change lane count and period, see predicted queue before buying anything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One honest limitation: &lt;code&gt;SyntheticCheck.minion&lt;/code&gt; returns &lt;code&gt;"unknown"&lt;/code&gt; in our account, so per-check host attribution isn't available from telemetry. The flow view shows lane &lt;em&gt;occupancy&lt;/em&gt; accurately but fills slots left-to-right by count, not identity. True per-lane attribution needs an agent on the hosts, which we deliberately deferred, because reading the Docker socket is root-equivalent and that's a conversation with the security team, not a code change.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Dashboard UI&lt;/strong&gt;
&lt;/h3&gt;

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

&lt;p&gt;Video: &lt;a href="https://youtu.be/rN8jl0jQT7o" rel="noopener noreferrer"&gt;https://youtu.be/rN8jl0jQT7o&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>monitoring</category>
      <category>sre</category>
      <category>performance</category>
    </item>
    <item>
      <title>A Pet Re-homing platform that costs $29 a month to run</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Sun, 11 Oct 2026 16:24:16 +0000</pubDate>
      <link>https://dev.to/randiakm/a-pet-re-homing-platform-that-costs-29-a-month-to-run-hmm</link>
      <guid>https://dev.to/randiakm/a-pet-re-homing-platform-that-costs-29-a-month-to-run-hmm</guid>
      <description>&lt;p&gt;The Architecture of PawReHome  - 24 lambdas, 68 routes, Aurora Serverless v2 with no connection pool anywhere and the single constraint that decided all of it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Series: Building PawReHome Ep-01&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PawReHome is a pet-rehoming platform: people post stray or surrendered animals, adopters apply, admins verify listings, rescue organizations manage their own rosters. &lt;/p&gt;

&lt;p&gt;The interesting part is the constraint I built it under: it had to cost almost nothing while idle. No funding, no revenue, no guarantee anyone would use it. &lt;/p&gt;

&lt;p&gt;That one requirement decided the database, the way lambda talks to it, the networking and the error handling. This post is the map. This links to are where the actual difficulty lives. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Stack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Layer and Choice&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend - Next.js 14.2.35, App Router, Tailwind, 30+ pages&lt;/li&gt;
&lt;li&gt;Hosting - AWS Amplify Hosting (CloudFront underneath)&lt;/li&gt;
&lt;li&gt;API - API Gateway REST plus WebSocket API&lt;/li&gt;
&lt;li&gt;Compute - 24 Lambda functions, 256 MB 29s timeout&lt;/li&gt;
&lt;li&gt;Database - Aurora PostgreSQL Serverless v2 via RDS Data API&lt;/li&gt;
&lt;li&gt;Auth - Cognito user pool, group based roles&lt;/li&gt;
&lt;li&gt;Storage - S3 for listing images&lt;/li&gt;
&lt;li&gt;Email - SES&lt;/li&gt;
&lt;li&gt;IaC - AWS SAM/ CloudFormation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Shape of it&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;One Request, end to end&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What is costs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Line - Cost (&lt;strong&gt;2026 September&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;Aurora Serverless v2 ACU-hours - $29.21&lt;br&gt;
Aurora storage I/O             - $0.33&lt;br&gt;
Aurora storage                 - $0.01&lt;br&gt;
RDS Data API requests          - $0.00&lt;br&gt;
Lambda, API Gateway, S3, Cognito, SES - $0.00 at this time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;See you in the episode 02 :)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>rds</category>
      <category>serverless</category>
      <category>pet</category>
    </item>
    <item>
      <title>Building a Serverless Life Visualization App with Next.js, AWS Lambda, and DynamoDB Single-Table Design - #01</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Mon, 01 Jun 2026 15:09:09 +0000</pubDate>
      <link>https://dev.to/randiakm/building-a-serverless-life-visualization-app-with-nextjs-aws-lambda-and-dynamodb-single-table-o</link>
      <guid>https://dev.to/randiakm/building-a-serverless-life-visualization-app-with-nextjs-aws-lambda-and-dynamodb-single-table-o</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;code&gt;How we built yourdeath.date - a memento mori tool that renders your life as a grid of weeks - on a fully serverless AWS stack&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;




&lt;p&gt;Most apps are built to keep you engaged as long as possible. We built one designed to remind you that your time is finite. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://yourdeath.date" rel="noopener noreferrer"&gt;yourdeath.date&lt;/a&gt; visualizes your entire life from birth to projected death as a grid of small squares. Each square is one week. Filled squares are weeks already spent. The rest are blank. &lt;/p&gt;

&lt;p&gt;The app also lets users log "good deeds" onto specific weeks, pin life milestones to the grid, share their grid publicly, and connect with a community of people thinking about how they spend their time. &lt;/p&gt;

&lt;p&gt;This article covers the technical architecture behind it. The AWS services, the serverless design decisions, the DynamoDB single-table schema, the auth flow, and the frontend rendering approach. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is this Application?
&lt;/h2&gt;




&lt;p&gt;Frontend &lt;strong&gt;-&amp;gt;&lt;/strong&gt; Next.js 14 (App Router), React 18, TypeScript, Tailwind CSS&lt;br&gt;
Backend  &lt;strong&gt;-&amp;gt;&lt;/strong&gt; AWS Lambda (Node.js 22.x), API Gateway REST&lt;br&gt;
Database &lt;strong&gt;-&amp;gt;&lt;/strong&gt; DynamoDB (single-table design, 3 GSIs)&lt;br&gt;
Auth &lt;strong&gt;-&amp;gt;&lt;/strong&gt; Amazon Cognito (User Pool + Identity Pool)&lt;br&gt;
Email &lt;strong&gt;-&amp;gt;&lt;/strong&gt; Amazon SES + Secrets Manager&lt;br&gt;
Storage Amazon &lt;strong&gt;-&amp;gt;&lt;/strong&gt; S3&lt;br&gt;
Scheduling &lt;strong&gt;-&amp;gt;&lt;/strong&gt; Amazon EventBridge&lt;br&gt;
IaC &lt;strong&gt;-&amp;gt;&lt;/strong&gt; AWS SAM (Serverless Application Model)&lt;br&gt;
Hosting &lt;strong&gt;-&amp;gt;&lt;/strong&gt; AWS Amplify (frontend), API Gateway (backend)&lt;/p&gt;

&lt;h2&gt;
  
  
  High Level Architecture
&lt;/h2&gt;




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

&lt;h2&gt;
  
  
  Application FLow
&lt;/h2&gt;




&lt;h3&gt;
  
  
  User Onboarding Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;User visits yourdeath.date&lt;/li&gt;
&lt;li&gt;Clicks "See your life →"&lt;/li&gt;
&lt;li&gt;Redirected to /auth (Cognito hosted UI or email/password)&lt;/li&gt;
&lt;li&gt;OAuth callback → /auth/callback&lt;/li&gt;
&lt;li&gt;Amplify exchanges code for tokens (access + id + refresh)&lt;/li&gt;
&lt;li&gt;POST /auth/sync → Lambda syncs Cognito identity to DynamoDB&lt;/li&gt;
&lt;li&gt;Redirect to /onboarding → user enters DOB + life expectancy&lt;/li&gt;
&lt;li&gt;PUT /user/profile → stores profile in DynamoDB&lt;/li&gt;
&lt;li&gt;Redirect to /dashboard → LifeGrid renders with real data&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Deed Logging Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;User opens community page&lt;/li&gt;
&lt;li&gt;Frontend reads current week index: Math.floor(msAlive / msPerWeek)&lt;/li&gt;
&lt;li&gt;User fills deed form (description, category, helpedWho, isPublic)&lt;/li&gt;
&lt;li&gt;POST /deeds → Lambda validates, stores in DynamoDB

&lt;ul&gt;
&lt;li&gt;PK: USER#{userId}  SK: DEED#{weekIndex:6-padded}#{deedId}&lt;/li&gt;
&lt;li&gt;GSI1PK: COMMUNITY#PUBLIC (if public) or USER#{userId}#PRIVATE&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dashboard: GET /deeds/me returns {weekIndex → count} map&lt;/li&gt;
&lt;li&gt;LifeGrid highlights that week column green&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Weekly Email Flow
&lt;/h3&gt;

&lt;p&gt;EventBridge cron(0 6 ? * MON *) → WeeklyEmailFunction&lt;br&gt;
  → ScanCommand: FilterExpression = emailOptIn = true&lt;br&gt;
  → For each user:&lt;br&gt;
      → Fetch weeks lived, weeks remaining&lt;br&gt;
      → Generate HMAC token: sha256(userId:email, secret)&lt;br&gt;
      → Build HTML email with unsubscribe link&lt;br&gt;
      → SES SendEmail (10-message batches, 100ms throttle)&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Services Connectivity Flow
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Interesting Engineering Decisions
&lt;/h2&gt;




&lt;ol&gt;
&lt;li&gt;Single-Table DynamoDB vs Multi-Table&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We choose single table design to minimize cold-start latency (one connection pool) and reduce operational overhead. The trade-off is query complexity, GSI3 exists purely to solve the deed-by-ID lookup problem that arises from putting deeds under &lt;code&gt;USER#{id}&lt;/code&gt; as primary key. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;HMAC Unsubscribe Tokens&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Rather than storing one-time tokens in DynamoDB, the unsubscribe system uses a stateless HMAC approach:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;token = base64url( HMAC-SHA256( "{userId}:{email}", secret ) )&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Validation is a single HMAC re-computation + constant-time comparison. No database lookup required. The secret lives in Secrets Manager and is fetched once per Lambda cold start.&lt;/p&gt;

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




&lt;p&gt;The stack — Next.js on Amplify, API Gateway, Lambda, DynamoDB, Cognito — is a proven serverless pattern that scales to thousands of concurrent users with near-zero operational overhead. The interesting parts were the DynamoDB single-table schema design (especially GSI3 for deed lookup), the theme-aware LifeGrid renderer, and keeping analytics collection genuinely privacy-respecting.&lt;/p&gt;

&lt;p&gt;The full codebase lives at &lt;a href="https://yourdeath.date" rel="noopener noreferrer"&gt;yourdeath.date&lt;/a&gt;. If you have questions about any of the architecture decisions, drop them in the comments.&lt;/p&gt;




&lt;p&gt;Built with AWS SAM · Next.js 14 · DynamoDB · Cognito · TypeScript&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>nextjs</category>
      <category>life</category>
    </item>
    <item>
      <title>I Found a Critical Security Bug on Foundit.sg — Here's What Happened</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Mon, 18 May 2026 15:44:10 +0000</pubDate>
      <link>https://dev.to/randiakm/i-found-a-critical-security-bug-on-founditsg-heres-what-happened-2a8l</link>
      <guid>https://dev.to/randiakm/i-found-a-critical-security-bug-on-founditsg-heres-what-happened-2a8l</guid>
      <description>&lt;p&gt;&lt;strong&gt;How It Started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It began as a completely ordinary moment. A user (let's call them User A) received a job suggestion email from Foundit.sg — a job platform widely used across Singapore and Southeast Asia. Nothing unusual there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;But here's where it got interesting.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When User A &lt;strong&gt;forwarded **that job suggestion email to a friend (User B), something unexpected happened. User B could click the link inside that forwarded email and — without entering any credentials — **be automatically logged into User A's account.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not just to view a job listing. Into the &lt;strong&gt;full account&lt;/strong&gt;. With access to view, edit, and modify all of User A's personal details, job preferences, resume, and profile information.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Bug Explained&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The root cause is a classic but serious security flaw: &lt;strong&gt;unprotected magic links / auto-login tokens.&lt;/strong&gt;&lt;br&gt;
&lt;a href="//foundit.sg"&gt;Foundit's&lt;/a&gt; job suggestion emails contained a one-click authentication link that:&lt;/p&gt;

&lt;p&gt;Was not single-use (it could be clicked multiple times)&lt;br&gt;
Was not bound to the recipient's device or IP address&lt;br&gt;
Had no secondary verification step&lt;/p&gt;

&lt;p&gt;This means anyone who received the forwarded email — intentionally or accidentally — could gain full unauthorized access to another person's account.&lt;br&gt;
Impact:&lt;/p&gt;

&lt;p&gt;✅ Unauthorized account access&lt;br&gt;
✅ Personal data exposure (name, contact details, resume, etc.)&lt;br&gt;
✅ Profile editing without consent&lt;br&gt;
✅ Potential misuse of saved and applied jobs&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Severity: CRITICAL&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I Did — Responsible Disclosure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I did not exploit the bug. I did not share it publicly. I followed the responsible *&lt;em&gt;disclosure *&lt;/em&gt; process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Emailed Foundit at &lt;a href="mailto:info@foundit.sg"&gt;info@foundit.sg&lt;/a&gt; with a full, detailed bug report including steps to reproduce, impact analysis, and suggested fixes&lt;/li&gt;
&lt;li&gt;Waited for a response — no reply came via Email&lt;/li&gt;
&lt;li&gt;Posted a gentle nudge on LinkedIn asking if they received my report — After several days they replied to me&lt;/li&gt;
&lt;li&gt;Submitted to Open Bug Bounty to create an official public record of responsible disclosure&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;The Bug Was Fixed — But Not Properly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Eventually, the bug was addressed. However, the "fix" implemented was not a proper security patch.&lt;br&gt;
Instead of fixing the underlying authentication token issue, they added an error message that reads:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"Ooops! We couldn't find this job under your current country preference. It might be listed under a different location. Try switching your country in Settings to view the job."&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;This is a UI-level workaround, not a true security fix. The root cause — insecure auto-login tokens — may still exist in other parts of the platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero Acknowledgment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Throughout this entire process:&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;No reply to my email&lt;/strong&gt;&lt;br&gt;
✅ Response to my LinkedIn post - After several Days, But no mentioning further details.&lt;br&gt;
❌ &lt;strong&gt;No acknowledgment of the report&lt;/strong&gt;&lt;br&gt;
❌ &lt;strong&gt;No credit or mention anywhere&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm not sharing this to shame Foundit. I'm sharing this because responsible disclosure deserves acknowledgment, and the security community deserves to know how platforms respond (or don't respond) to good-faith reports.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Lessons for Developers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're building a platform with email-based authentication links, please ensure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Magic links are single-use — expire immediately after first click&lt;/li&gt;
&lt;li&gt;Bind tokens to the recipient's identity — not just a URL&lt;/li&gt;
&lt;li&gt;Add device/IP verification for sensitive actions&lt;/li&gt;
&lt;li&gt;Have a clear security reporting channel — and actually respond to it&lt;/li&gt;
&lt;li&gt;Acknowledge responsible disclosures — even a simple "thank you" email goes a long way&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Relevant Authorities (Singapore)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you encounter similar issues on Singapore-based platforms, you can report to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cyber Security Agency of Singapore (CSA) — csa.gov.sg&lt;/li&gt;
&lt;li&gt;Personal Data Protection Commission (PDPC) — pdpc.gov.sg&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cybersecurity is a community effort. When someone takes the time to responsibly report a vulnerability instead of exploiting it, that deserves recognition — not silence.&lt;/p&gt;

&lt;p&gt;I hope this story encourages platforms to take security reports seriously, and encourages more people to report vulnerabilities responsibly rather than ignore or exploit them.&lt;/p&gt;




&lt;p&gt;Found this useful? Share it with your network. The more awareness, the safer our digital spaces become.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>bugbounty</category>
      <category>singapore</category>
      <category>dataprotection</category>
    </item>
    <item>
      <title>Building a Scalable Pet Rehoming Platform with AWS Serverless Architecture</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Fri, 09 Jan 2026 09:49:08 +0000</pubDate>
      <link>https://dev.to/randiakm/building-a-scalable-pet-rehoming-platform-with-aws-serverless-architecture-195b</link>
      <guid>https://dev.to/randiakm/building-a-scalable-pet-rehoming-platform-with-aws-serverless-architecture-195b</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;PawReHome&lt;/em&gt; is a modern, full-featured pet rehoming platform that connects loving families with pets in need. Build with a serverless-first approach, the platform leverages AWS managed services to provide a scalable, cost-effective and secure solution for animal rescue organizations and induvial users. &lt;/p&gt;

&lt;p&gt;This article explores the AWS architecture behind the PawReHome, how several AWS services work together to create a robust, role based access control, real-time data management. &lt;/p&gt;

&lt;h3&gt;
  
  
  Project Overview
&lt;/h3&gt;

&lt;p&gt;PawReHome is a application fully integrated with AWS backend services. The platform supports:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User authentication and authorization with role-based access control&lt;/li&gt;
&lt;li&gt;Multi-tenant organization management for rescue groups&lt;/li&gt;
&lt;li&gt;Pet listing management with approval workflows&lt;/li&gt;
&lt;li&gt;Image upload for pet photos&lt;/li&gt;
&lt;li&gt;Adoption applications and inquiry systems&lt;/li&gt;
&lt;li&gt;Platform administration for content moderation &lt;/li&gt;
&lt;li&gt;Featured pets curation for homepage visibility&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  AWS Architecture Overview
&lt;/h3&gt;

&lt;p&gt;This utilizes a serverless architecture that eliminates server management overhead and provides automatic scaling. &lt;/p&gt;

&lt;p&gt;Complete AWS services stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Amplify: Frontend hosting, CI/CD and deployment&lt;/li&gt;
&lt;li&gt;Amazon Cognito: User authentication and authorization&lt;/li&gt;
&lt;li&gt;AWS Lambda: Serverless backend functions&lt;/li&gt;
&lt;li&gt;Amazon API Gateway: RESTFUL API management and routing &lt;/li&gt;
&lt;li&gt;DynamoDB: NoSQL database for all application data&lt;/li&gt;
&lt;li&gt;S3: Object storage for pet images&lt;/li&gt;
&lt;li&gt;IAM: Identity and access management&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture Flow
&lt;/h3&gt;

&lt;h4&gt;
  
  
  High-Level Architecture Diagram
&lt;/h4&gt;

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

&lt;h3&gt;
  
  
  Deep Dive: AWS Services Implementation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. AWS Amplify - Frontend Hosting &amp;amp; CI/CD&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Purpose: As you can see Amplify hosts the Next.js frontend application and provides continues deployment capabilities. &lt;/p&gt;

&lt;p&gt;Implementation Details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The amplify.yml configuration defines the build process&lt;/li&gt;
&lt;li&gt;Automatic builds triggered on Git commits&lt;/li&gt;
&lt;li&gt;Environment variables managed through Amplify Console&lt;/li&gt;
&lt;li&gt;Global CDN ensures low latency worldwide&lt;/li&gt;
&lt;li&gt;Built-in SSL certificate provisioning via AWS Certificate Manager&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits of the Amplify&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero server management, you do not need to worry about.&lt;/li&gt;
&lt;li&gt;Automatic scaling when traffic spikes.&lt;/li&gt;
&lt;li&gt;Preview deployment.&lt;/li&gt;
&lt;li&gt;Rollback capabilities when you have an issue. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Cognito - Authentication &amp;amp; Authorization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cognito provides secure user access and authentication across your application. This has role-based access control (RBAC) thorough Cognito groups. &lt;/p&gt;

&lt;p&gt;Let me explain the Authorization Flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User logs in via Cognito&lt;/li&gt;
&lt;li&gt;Cognito issues JWT token with user claims&lt;/li&gt;
&lt;li&gt;Token includes cognito:groups claim with user's groups&lt;/li&gt;
&lt;li&gt;API Gateway validates token&lt;/li&gt;
&lt;li&gt;Lambda functions check group membership for authorization&lt;/li&gt;
&lt;li&gt;DynamoDB queries are filtered based on user permissions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;3. Lambda - Serverless Backend Functions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS Lambda functions handle all the business logic, data processing and API responses without managing servers. How relief :) &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example Lambda function (pet listing):&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand, ScanCommand } = require("@aws-sdk/lib-dynamodb");

const docClient = DynamoDBDocumentClient.from(new DynamoDBClient({}));
const TABLE_NAME = "PawReHome-Pets";

exports.handler = async (event) =&amp;gt; {
  // Handle pet rehoming submission
  if (event.httpMethod === "POST") {
    const pet = {
      id: uuidv4(),
      ...JSON.parse(event.body),
      status: "pending",
      createdAt: new Date().toISOString()
    };

    await docClient.send(new PutCommand({
      TableName: TABLE_NAME,
      Item: pet
    }));

    return { statusCode: 201, body: JSON.stringify(pet) };
  }

  // Get approved pets
  if (event.httpMethod === "GET") {
    const result = await docClient.send(new ScanCommand({
      TableName: TABLE_NAME,
      FilterExpression: "#status = :status",
      ExpressionAttributeNames: { "#status": "status" },
      ExpressionAttributeValues: { ":status": "approved" }
    }));

    return { statusCode: 200, body: JSON.stringify(result.Items) };
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Amazon API Gateway - RESTful API Management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;API gateway servers as the front door for all backend API requests, providing routing, authentication and rate limiting. (Do not forget about the rate limit, your pocket will empty 😉 and will have some concerns. 🛡️)&lt;/p&gt;

&lt;p&gt;API Gateway Configuration:&lt;/p&gt;

&lt;p&gt;REST API endpoint&lt;br&gt;
CORS enabled for frontend cross-origin requests&lt;br&gt;
Cognito Authorizer for automatic JWT validation&lt;br&gt;
Lambda Proxy Integration for seamless function invocation&lt;br&gt;
Stage Variables for environment management&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. DynamoDB - NoSQL Database&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;DynamoDB stores all application data with single-digit millisecond latency and automatic scaling. &lt;/p&gt;

&lt;p&gt;Table Design as below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;PawReHome-Pets&lt;/em&gt;&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;Partition Key: id (String)
Attributes: name, species, breed, age, size, gender, description, 
           images[], userId, organizationId, status, featured, 
           createdAt, updatedAt
GSIs:
  - UserIdIndex (userId) - Query user's pets
  - OrgIdIndex (organizationId) - Query organization's pets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;PawReHome-Organizations&lt;/em&gt;&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;Partition Key: id (String)
Attributes: name, slug, email, phone, description, logo, 
           status, verified, createdAt, reviewedBy, reviewedAt
GSIs:
  - SlugIndex (slug) - Query by URL slug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;PawReHome-OrgMembers&lt;/em&gt;&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;Partition Key: organizationId (String)
Sort Key: userId (String)
Attributes: role (admin/member), joinedAt, invitedBy
GSIs:
  - UserIdIndex (userId) - Query user's organizations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;PawReHome-Applications&lt;/em&gt;&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;Partition Key: id (String)
Attributes: petId, userId, status, applicantInfo, 
           submittedAt, reviewedAt
GSIs:
  - UserIdIndex (userId) - Query user's applications
  - PetIdIndex (petId) - Query pet's applications
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;PawReHome-Questions&lt;/em&gt;&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;Partition Key: id (String)
Attributes: petId, userId, status, applicantInfo, 
           submittedAt, reviewedAt
GSIs:
  - UserIdIndex (userId) - Query user's applications
  - PetIdIndex (petId) - Query pet's applications
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;PawReHome-Favorites&lt;/em&gt;&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;Partition Key: userId (String)
Sort Key: petId (String)
Attributes: addedAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. S3 - Image Storage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;S3 uses to stores pet photos with public read access for displaying images on the website. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;to be continued.....&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Made with ❤️ for pets everywhere&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Building a Full-Stack E-Commerce Platform with AWS</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Sat, 27 Dec 2025 10:59:22 +0000</pubDate>
      <link>https://dev.to/randiakm/building-a-full-stack-e-commerce-platform-with-aws-3hki</link>
      <guid>https://dev.to/randiakm/building-a-full-stack-e-commerce-platform-with-aws-3hki</guid>
      <description>&lt;h3&gt;
  
  
  A Technical Deep-Dive into Modern Serverless Architecture
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Introduction
&lt;/h4&gt;

&lt;p&gt;I recently build an e-commerce website that handles custom gifts orders with image uploads, payment processing, order tracking, and admin dashboard and all running on a completely serverless architecture. This blog walks through the complete technical decisions, architecture patterns and lessons learned. &lt;/p&gt;

&lt;h4&gt;
  
  
  The Tech Stack that I used
&lt;/h4&gt;

&lt;p&gt;Layer: Frontend Framework&lt;br&gt;
Technology: Next.js 15&lt;br&gt;
Why I choose it: Server components, built in API routs&lt;/p&gt;

&lt;p&gt;Layer: Database&lt;br&gt;
Technology: Amazon DynamoDB&lt;br&gt;
Why I choose it: Serverless, pay-per-request, automatic scaling &lt;/p&gt;

&lt;p&gt;Layer: File Storage&lt;br&gt;
Technology: S3&lt;br&gt;
Why I choose it: Reliable, cheap, presigned URLs for secure access&lt;/p&gt;

&lt;p&gt;Layer: Authentication&lt;br&gt;
Technology: Cognito&lt;br&gt;
Why I choose it: Managed auth, MFA support, integrates with Amplify&lt;/p&gt;

&lt;p&gt;Layer: Email Service&lt;br&gt;
Technology: Amazon SES&lt;br&gt;
Why I choose it: Transactional emails, high deliverability, low cost&lt;/p&gt;

&lt;p&gt;Layer: Hosting&lt;br&gt;
Technology: Amazon Amplify Gen 2&lt;br&gt;
Why I choose it: Git based deployment, automates SSL, CDN included.&lt;/p&gt;

&lt;h4&gt;
  
  
  Architecture Overview
&lt;/h4&gt;

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

&lt;h4&gt;
  
  
  The Customer Journey
&lt;/h4&gt;

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

&lt;p&gt;Step by Step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Landing Page - Customer learns about the product and clicks "Order Now"&lt;/li&gt;
&lt;li&gt;Customization - Customer writes their custom message and optionally uploads a photo&lt;/li&gt;
&lt;li&gt;Address Entry - Customer enters recipient shipping address and their billing details&lt;/li&gt;
&lt;li&gt;Payment - Redirected to Payment Gateway&lt;/li&gt;
&lt;li&gt;Success - After payment, customer sees their order ID and tracking link&lt;/li&gt;
&lt;li&gt;Email - Customer receives confirmation email with order details&lt;/li&gt;
&lt;li&gt;Tracking - Customer can check order status anytime using their order ID&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Payment Flow (The Tricky Part)
&lt;/h4&gt;

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

&lt;h4&gt;
  
  
  Image Upload Architecture
&lt;/h4&gt;

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

&lt;h4&gt;
  
  
  Why I Used Presigned URLs?
&lt;/h4&gt;

&lt;p&gt;Instead of making the S3 bucket public, I use presigned URLs that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expire after 1 hour&lt;/li&gt;
&lt;li&gt;Are generated on-demand&lt;/li&gt;
&lt;li&gt;Keep the bucket private and secure&lt;/li&gt;
&lt;li&gt;Work with CloudFront CDN&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Email Notification System
&lt;/h4&gt;

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

&lt;p&gt;Email Features&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML templates with incline CSS for email client compatibility&lt;/li&gt;
&lt;li&gt;Multiple admin recipients&lt;/li&gt;
&lt;li&gt;Reply-to headers, so admin can respond directly to customers&lt;/li&gt;
&lt;li&gt;Order details formatted for easy reading and fulfillment&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Deployment Pipeline
&lt;/h4&gt;

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

&lt;p&gt;The entire deployment is automated, &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Push the code to GitHub&lt;/li&gt;
&lt;li&gt;Amplify detects the change&lt;/li&gt;
&lt;li&gt;Builds the Next.js application&lt;/li&gt;
&lt;li&gt;Injects environment variables&lt;/li&gt;
&lt;li&gt;Deploys to CloudFront CDN&lt;/li&gt;
&lt;li&gt;Site is live in 305 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Deep Dive: AWS Services Powering This Application
&lt;/h2&gt;

&lt;h4&gt;
  
  
  AWS Services Architecture
&lt;/h4&gt;

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

&lt;p&gt;&lt;strong&gt;1. AWS Amplify Gen 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is it?&lt;/p&gt;

&lt;p&gt;AWS Amplify Gen 2 is a complete development platform that handles hosting, CI/CD, and backend infrastructure for web applications. It's the second generation of Amplify. &lt;/p&gt;

&lt;p&gt;Why I chose it? &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Git based deployments: Push to GitHub --&amp;gt; Automatically deployed into AWS&lt;/li&gt;
&lt;li&gt;Built in CI/CD: No Jenkins, No GitHub Actions&lt;/li&gt;
&lt;li&gt;Free SSL certificates: HTTPS enabled automatically&lt;/li&gt;
&lt;li&gt;Global CDN: CloudFront is included&lt;/li&gt;
&lt;li&gt;Custom domains: Easy DNS configurations&lt;/li&gt;
&lt;li&gt;Environment variables: Secure secrets management&lt;/li&gt;
&lt;li&gt;Preview Deployment: Test PRs before merging&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How It's Used in This Project&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;2. Amazon DynamoDB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is it? &lt;/p&gt;

&lt;p&gt;DynamoDB is a fully managed NoSQL database service that provides single-digit millisecond performance at any scale. It's serverless, no provisioning, no patching or management required. &lt;/p&gt;

&lt;p&gt;Why I chose it? &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Serverless, Auto-scaling&lt;/li&gt;
&lt;li&gt;Pay-per request&lt;/li&gt;
&lt;li&gt;Single digit ms latency&lt;/li&gt;
&lt;li&gt;AWS-native integration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;3. Amazon S3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is it? &lt;/p&gt;

&lt;p&gt;S3 is object storage built for storing and retrieving any amount of data. It's the backbone of AWS storage. &lt;/p&gt;

&lt;p&gt;Why I chose it?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Unlimited storage&lt;/li&gt;
&lt;li&gt;Presigned URLs&lt;/li&gt;
&lt;li&gt;Cheap&lt;/li&gt;
&lt;li&gt;Direct SDK access&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;S3 Features Used&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private Bucket: Images not publicly accessible&lt;/li&gt;
&lt;li&gt;Presigned URLs: Temporary access links (1 hour)&lt;/li&gt;
&lt;li&gt;Server-side Upload: API route handles upload, not browser&lt;/li&gt;
&lt;li&gt;Content-Type: Proper MIME types for images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security Configuration&lt;/p&gt;

&lt;p&gt;Bucket Policy: Private by default&lt;br&gt;
CORS: Enabled for upload endpoints&lt;br&gt;
Presigned URLs: Time-limited access&lt;br&gt;
IAM: Scoped permissions for put/get only&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Amazon Cognito&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is it? &lt;/p&gt;

&lt;p&gt;Amazon Cognito provides authentication, authorization and user management for web and mobile apps. Users can sign in directly or through third party identity providers. &lt;/p&gt;

&lt;p&gt;Why I chose it? &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS-native&lt;/li&gt;
&lt;li&gt;MFA support&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How It's Used&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;5. Amazon SES (Simple Email Service)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is it? &lt;/p&gt;

&lt;p&gt;Amazon SES is a cloud based email service for sending transactional, marketing and notification emails. &lt;/p&gt;

&lt;p&gt;How It's Used&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;6. Amazon CloudFront (via Amplify)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is it? &lt;/p&gt;

&lt;p&gt;CloudFront is a content delivery network that delivers content with low latency from 400+ edge locations worldwide. &lt;/p&gt;

&lt;p&gt;How It's Used&lt;/p&gt;

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

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>nextjs</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>Terraform-CodeGen0: A Terraform Code Generator</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Sun, 12 Jan 2025 20:58:48 +0000</pubDate>
      <link>https://dev.to/randiakm/terraform-codegen0-a-terraform-code-generator-55aa</link>
      <guid>https://dev.to/randiakm/terraform-codegen0-a-terraform-code-generator-55aa</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github"&gt;GitHub Copilot Challenge &lt;/a&gt;: New Beginnings&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;When we are deploying an application, especially in a cloud environment, we often need to manage the various infrastructure components, such as VPC, Servers, Databases, CDN, etc. Doing this manually is somewhat time-consuming and challenging. To overcome this, we can use terraform. &lt;/p&gt;

&lt;p&gt;Terraform allows the development of the architecture that deploys the application in a cloud environment. &lt;/p&gt;

&lt;p&gt;But writing the terraform is also somewhat time-consuming we need to create those codes manually, plus we need to know the terraform. &lt;/p&gt;

&lt;p&gt;Using this &lt;strong&gt;Terraform-CodeGen0&lt;/strong&gt; you can easily generate the terraform codes. All you need to input the VPC name, Subnets, Route Tabels, IGW, and NAT details into this application. This &lt;strong&gt;Terraform-CodeGen0&lt;/strong&gt; provides your terraform code. &lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/wtaEJJ3XcZc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Repo
&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/RandiakM" rel="noopener noreferrer"&gt;
        RandiakM
      &lt;/a&gt; / &lt;a href="https://github.com/RandiakM/Terraform-CodeGen0" rel="noopener noreferrer"&gt;
        Terraform-CodeGen0
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      GitHub Copilot 1-Day Build Challenge: This is a Terraform Code generation application for VPC
    &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;VPC Terraform Generator&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Introduction&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The VPC Terraform Generator is a tool designed to generate Terraform code for AWS infrastructure components such as VPCs, Subnets, Route Tables, Internet Gateways (IGWs), and NAT Gateways. This project uses a microservices architecture and is hosted on Netlify, with MongoDB as the database.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How to Use&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The VPC Terraform Generator allows users to generate Terraform code for various AWS components through a web interface. Users can input the necessary details for each component, and the application will generate the corresponding Terraform code.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation Steps&lt;/h2&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Clone the repository:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;git clone https://github.com/your-repo/vpc-terraform-generator.git
cd vpc-terraform-generator
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Install backend dependencies:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;cd api
npm install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Install frontend dependencies:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;cd ../frontend
npm install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Set up environment variables: Create a .env file in the api directory with the following content:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;MONGODB_URI=your_mongodb_connection_string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Start the backend server:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;cd ../api
npm run dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="6"&gt;
&lt;li&gt;Start the frontend server:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;cd ../frontend
npm start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Netlify Hosting Steps&lt;/h2&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a…&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RandiakM/Terraform-CodeGen0" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Copilot Experience
&lt;/h2&gt;

&lt;p&gt;So for the initial start, I have used the below prompt to get an idea of this project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a Terraform code generator for AWS infrastructure components (VPCs, Subnets, Route Tables, IGWs, NAT, etc.) using a microservices architecture. The application will be hosted on Netlify, and MongoDB will be used as the database.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Starting project structure:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Terraform Code Generation:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask to implement the utility functions to generate Terraform code-based user inputs for each resource type. &lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Edits:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Auto Complete:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For this code auto-completion, I provide the description as a comment and start to create the function, then Copilot starts suggesting the code sections that are related to my task.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Documentation Creation:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Used the below prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@workspace create a README with the below details:

1. Introduction 
2. How to use 
3. Installation Steps 
4. Netlify Hosting Steps 
5. User guide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Through this challenge and the journey, Copilot was an AI pair programmer who helped me build this application. From the start and even from the ending of this project Copilot was really helped me. &lt;/p&gt;

&lt;p&gt;Copilot reviewed the codes and provide alternative solutions to overcome some issues when developing this application. &lt;/p&gt;

&lt;p&gt;With the help of Copilot I was able to create this amazing application within just 24 hours, Wihtout copilot help this may take long. &lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Configuring AWS WAF, CloudFront, and S3 Bucket for Secure Access</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Fri, 03 Jan 2025 08:49:10 +0000</pubDate>
      <link>https://dev.to/randiakm/configuring-aws-waf-cloudfront-and-s3-bucket-for-secure-access-24a3</link>
      <guid>https://dev.to/randiakm/configuring-aws-waf-cloudfront-and-s3-bucket-for-secure-access-24a3</guid>
      <description>&lt;p&gt;How to Set Up AWS WAF, CloudFront and an S3 Bucket to serve content securely and Restrict content Access to certain originating sources only. This guide looks at some issues, for instance, 403 Forbidden, in order to troubleshoot the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Objective&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The purpose of this assignment is to deploy AWS WAF, CloudFront and S3 bucket:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restrict Access to certain origins, for example, &lt;a href="https://dev.test.com" rel="noopener noreferrer"&gt;https://dev.test.com&lt;/a&gt; and &lt;a href="https://int.test.com" rel="noopener noreferrer"&gt;https://int.test.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;When the CloudFront distribution is accessed, the index.html page should be served by default.&lt;/li&gt;
&lt;li&gt;To prevent unauthorized access and to correct common errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. You will require.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And AWS S3 bucket called, sample-bucket, where the files for the static website are stored.&lt;/p&gt;

&lt;p&gt;An AWS CloudFront distribution created with OAC linked to the S3 bucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Configuration Steps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Step 1: Make Sure S3 Bucket Policy has below&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the S3 Console.&lt;/li&gt;
&lt;li&gt;Select your bucket (e.g., sample-bucket).&lt;/li&gt;
&lt;li&gt;Check the following bucket policy to allow access from CloudFront:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::sample-bucket/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::AWS_ACC_ID:distribution/CLOUDFRONT_ID"
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step 2: Configure CORS for the S3 Bucket&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the S3 Console, go to the Permissions tab.&lt;/li&gt;
&lt;li&gt;Edit the CORS configuration and set the following:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["GET", "HEAD", "PUT", "POST", "DELETE"],
        "AllowedOrigins": [
            "https://dev.test.com",
            "https://int.test.com"
        ]
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replace AllowedOrigins with your allowed domains.&lt;/li&gt;
&lt;li&gt;Restrict AllowedHeaders to necessary headers for production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step 3: Configure AWS WAF&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create WAF Rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the AWS WAF Console and select your Web ACL.&lt;/li&gt;
&lt;li&gt;Add the following rules:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Rule 1: Allow-Traffic&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Description: Allows requests from specific origins.&lt;/li&gt;
&lt;li&gt;Rule Definition:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Name": "Allow-Traffic",
    "Priority": 1,
    "Statement": {
        "OrStatement": {
            "Statements": [
                {
                    "ByteMatchStatement": {
                        "SearchString": "https://dev.test.com",
                        "FieldToMatch": {
                            "SingleHeader": {
                                "Name": "origin"
                            }
                        },
                        "TextTransformations": [
                            {
                                "Priority": 0,
                                "Type": "NONE"
                            }
                        ],
                        "PositionalConstraint": "EXACTLY"
                    }
                },
                {
                    "ByteMatchStatement": {
                        "SearchString": "https://int.test.com",
                        "FieldToMatch": {
                            "SingleHeader": {
                                "Name": "origin"
                            }
                        },
                        "TextTransformations": [
                            {
                                "Priority": 0,
                                "Type": "NONE"
                            }
                        ],
                        "PositionalConstraint": "EXACTLY"
                    }
                }
            ]
        }
    },
    "Action": {
        "Allow": {}
    },
    "VisibilityConfig": {
        "SampledRequestsEnabled": true,
        "CloudWatchMetricsEnabled": true,
        "MetricName": "Allow-Traffic"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rule 2: Block-All-Other-Traffic&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Description: Blocks all requests that don’t match the allowed origins.&lt;/li&gt;
&lt;li&gt;Rule Definition:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Name": "Block-All-Other-Traffic",
    "Priority": 2,
    "Statement": {
        "NotStatement": {
            "Statement": {
                "OrStatement": {
                    "Statements": [
                        {
                            "ByteMatchStatement": {
                                "SearchString": "https://dev.test.com",
                                "FieldToMatch": {
                                    "SingleHeader": {
                                        "Name": "origin"
                                    }
                                },
                                "TextTransformations": [
                                    {
                                        "Priority": 0,
                                        "Type": "NONE"
                                    }
                                ],
                                "PositionalConstraint": "EXACTLY"
                            }
                        },
                        {
                            "ByteMatchStatement": {
                                "SearchString": "https://int.test.com",
                                "FieldToMatch": {
                                    "SingleHeader": {
                                        "Name": "origin"
                                    }
                                },
                                "TextTransformations": [
                                    {
                                        "Priority": 0,
                                        "Type": "NONE"
                                    }
                                ],
                                "PositionalConstraint": "EXACTLY"
                            }
                        }
                    ]
                }
            }
        }
    },
    "Action": {
        "Block": {}
    },
    "VisibilityConfig": {
        "SampledRequestsEnabled": true,
        "CloudWatchMetricsEnabled": true,
        "MetricName": "Block-All-Other-Traffic"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Testing and Verification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test Allowed Origins&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X GET https://cloudfront_domain_name \
     -H "Origin: https://dev.test.com" \
     -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected Result: HTTP 200 OK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test Disallowed Origins&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X GET https://cloudfront_domain_name \
     -H "Origin: https://unauthorized.com" \
     -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected Result: HTTP 403 Forbidden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check WAF Logs&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable logging for the Web ACL in the AWS WAF Console.&lt;/li&gt;
&lt;li&gt;Monitor logs in CloudWatch to verify rule matches.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>waf</category>
      <category>cloudfront</category>
      <category>s3</category>
    </item>
    <item>
      <title>Full Stack Application Hosting in AWS</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Mon, 30 Dec 2024 16:09:30 +0000</pubDate>
      <link>https://dev.to/randiakm/full-stack-application-hosting-in-aws-2kc3</link>
      <guid>https://dev.to/randiakm/full-stack-application-hosting-in-aws-2kc3</guid>
      <description>&lt;h2&gt;
  
  
  Todo Application Documentation
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Architecture Overview&lt;/li&gt;
&lt;li&gt;Backend Setup&lt;/li&gt;
&lt;li&gt;Frontend Setup&lt;/li&gt;
&lt;li&gt;AWS Configuration&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;li&gt;Maintenance and Updates&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;This document provides a comprehensive guide for setting up, deploying, and maintaining a full-stack Todo application using Node.js, React, Express, and MongoDB. The application is deployed on AWS, utilizing services such as Lambda, API Gateway, S3, and CloudFront.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Architecture Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Backend: Node.js with Express, deployed as an AWS Lambda function&lt;/li&gt;
&lt;li&gt;Frontend: React, built with Vite, hosted on S3 and served via CloudFront&lt;/li&gt;
&lt;li&gt;Database: MongoDB Atlas&lt;/li&gt;
&lt;li&gt;API: AWS API Gateway&lt;/li&gt;
&lt;li&gt;Authentication: (To be implemented)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Backend Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Lambda Function
&lt;/h3&gt;

&lt;p&gt;Create a new file named &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cachedDb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;connectToDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedDb&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="nx"&gt;cachedDb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;useNewUrlParser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;useUnifiedTopology&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;cachedDb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todoSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-cloudfront-domain.cloudfront.net&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET,POST,PUT,DELETE,OPTIONS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpMethod&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OPTIONS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CORS preflight request successful&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;connectToDatabase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&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;{httpMethod} &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;{resource}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;createdAt&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="p"&gt;{&lt;/span&gt; 
          &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST /todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;savedTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
          &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT /todos/{id}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatedTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByIdAndUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;updatedTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; 
          &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
          &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DELETE /todos/{id}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deletedTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByIdAndDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;deletedTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; 
          &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
          &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo deleted successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; 
        &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
          &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; 
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;corsHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal server error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; 
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 Package Configuration
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todo-api-lambda"&lt;/span&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;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Todo API Lambda Function"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&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;"mongoose"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.0.0"&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;h3&gt;
  
  
  3.3 Deployment Package
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Install dependencies: &lt;code&gt;npm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a ZIP file: &lt;code&gt;zip -r function.zip index.js node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4. Frontend Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Create React App
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a new Vite project: &lt;code&gt;npm create vite@latest client -- --template react&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to the project directory: &lt;code&gt;cd client&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install dependencies: &lt;code&gt;npm install&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4.2 API Service
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;src/services/todoService.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-api-gateway-url.execute-api.us-west-2.amazonaws.com/prod/todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;withCredentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&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="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&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;{id}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&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;{id}`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.3 Main App Component
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;src/App.jsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getTodos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deleteTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./services/todoService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setNewTodo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;fetchTodos&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchedTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getTodos&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchedTodos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleCreateTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createdTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;createdTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="nf"&gt;setNewTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleUpdateTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatedTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;updatedTodo&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleDeleteTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;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="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Todo App&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCreateTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setNewTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Add a new todo"&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Add Todo&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;
              &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nf"&gt;handleUpdateTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;textDecoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;line-through&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nf"&gt;handleDeleteTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. AWS Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Lambda Function
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a new Lambda function in the AWS Console.&lt;/li&gt;
&lt;li&gt;Upload the ZIP file created in step 3.3.&lt;/li&gt;
&lt;li&gt;Set the handler to &lt;code&gt;index.handler&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add environment variable: &lt;code&gt;MONGODB_URI&lt;/code&gt; with your MongoDB connection string.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5.2 API Gateway
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a new API in API Gateway.&lt;/li&gt;
&lt;li&gt;Create resources and methods for /todos and /todos/{id}.&lt;/li&gt;
&lt;li&gt;Integrate each method with your Lambda function.&lt;/li&gt;
&lt;li&gt;Enable CORS for each method.&lt;/li&gt;
&lt;li&gt;Deploy the API to a new stage (e.g., "prod").&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5.3 S3 and CloudFront
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create an S3 bucket for hosting the React app.&lt;/li&gt;
&lt;li&gt;Create a CloudFront distribution with the S3 bucket as the origin.&lt;/li&gt;
&lt;li&gt;Set up Origin Access Control (OAC) for secure access to the S3 bucket.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  6. Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 Backend Deployment
&lt;/h3&gt;

&lt;p&gt;Update the Lambda function code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zip &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;.zip index.js node_modules
aws lambda update-function-code &lt;span class="nt"&gt;--function-name&lt;/span&gt; YourFunctionName &lt;span class="nt"&gt;--zip-file&lt;/span&gt; fileb://function.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.2 Frontend Deployment
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Build the React app: &lt;code&gt;npm run build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Upload to S3:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;build/ s3://your-bucket-name &lt;span class="nt"&gt;--delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Invalidate CloudFront cache:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   aws cloudfront create-invalidation &lt;span class="nt"&gt;--distribution-id&lt;/span&gt; YourDistributionID &lt;span class="nt"&gt;--paths&lt;/span&gt; &lt;span class="s2"&gt;"/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1 CORS Issues
&lt;/h3&gt;

&lt;p&gt;If encountering CORS errors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ensure CORS is enabled in API Gateway for all methods.&lt;/li&gt;
&lt;li&gt;Verify CORS headers in the Lambda function response.&lt;/li&gt;
&lt;li&gt;Check that the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header matches your CloudFront domain.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  7.2 API Gateway 5XX Errors
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Check Lambda function logs in CloudWatch.&lt;/li&gt;
&lt;li&gt;Verify that the Lambda function has the correct permissions to access other AWS services.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  7.3 MongoDB Connection Issues
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ensure the &lt;code&gt;MONGODB_URI&lt;/code&gt; environment variable is set correctly in Lambda.&lt;/li&gt;
&lt;li&gt;Verify that the Lambda function has network access to MongoDB Atlas (may require VPC configuration).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  8. Maintenance and Updates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8.1 Updating the Backend
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Make changes to the Lambda function code.&lt;/li&gt;
&lt;li&gt;Redeploy using the steps in section 6.1.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  8.2 Updating the Frontend
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Make changes to the React application.&lt;/li&gt;
&lt;li&gt;Rebuild and redeploy using the steps in section 6.2.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  8.3 Monitoring
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Use CloudWatch to monitor Lambda function performance and errors.&lt;/li&gt;
&lt;li&gt;Set up CloudWatch Alarms for critical metrics.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  8.4 Scaling
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Adjust Lambda function memory and timeout settings as needed.&lt;/li&gt;
&lt;li&gt;Consider implementing caching at the API Gateway level for frequently accessed data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This documentation provides a comprehensive guide for setting up, deploying, and maintaining your Todo application. Remember to keep your dependencies updated and regularly review AWS best practices for potential improvements to your architecture.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>node</category>
      <category>s3</category>
      <category>cloudfron</category>
    </item>
    <item>
      <title>Docker Hands-On Part 06</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Wed, 14 Feb 2024 12:51:54 +0000</pubDate>
      <link>https://dev.to/randiakm/docker-hands-on-part-06-2a2p</link>
      <guid>https://dev.to/randiakm/docker-hands-on-part-06-2a2p</guid>
      <description>&lt;h2&gt;
  
  
  06. Docker Container Networking
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The process of containerizing applications represents a significant leap forward in how we deploy and manage software. The Flask framework, popular for its simplicity and flexibility for web application development, is an excellent candidate for Dockerization. This transformative journey from a centralized server setup to a containerized deployment offers numerous advantages, including streamlined deployment processes, improved scalability, and enhanced isolation for development and production environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Journey to Dockerize a Flask Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The task at hand involves moving a Flask application, aptly named "Notes App," into a Docker container. This application, designed for employees to store notes on current projects, will benefit from the portability and efficiency that containers provide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-by-Step Guide to Containerization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;01. Initial Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Begin by writing the necessary configuration code, including a Dockerfile and .dockerignore file, to build a clean and efficient Docker image for the Notes App.&lt;/p&gt;

&lt;p&gt;Step 01: Create Dokcerignore file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vim .dockerignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;add below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Exclude files
.dockeringnore
Dockerfile
.gitignore
Pipfile.lock
migrations/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;Dockerfile&lt;/code&gt; and add below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# version 01
FROM python:3
ENV PYBASE /pybase
ENV PYTHONUSERBASE $PYBASE
ENV PATH $PYABASE/bin:$PATH
#install pipenv
RUN pip install pipenv

WORKDIR /tmp
COPY Pipfile .
RUN pipenv lock
RUN PIP_USER=1 PIP_IGNORE_INSTALLED=1 pipenv install -d --system --ignore-pipfile

COPY . /app/notes
WORKDIR /app/notes
EXPOSE 80
CMD ["flask", "run", "--port=80", "--host=0.0.0.0"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Building the First Image:&lt;/p&gt;

&lt;p&gt;With the Dockerfile in place, build the initial image version of the Notes App. This step includes setting up the database within a container derived from the first image, ensuring that the application's data layer is ready for use.&lt;/p&gt;

&lt;p&gt;Step 02: Build the image and container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build -t notesapp:0.1 .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 03: Create and run a container for postgres:12.1-alpine&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker pull postgres:12.1-alpine

$ docker run -d --name notesdb postgres:12.1-alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 04: Create a network and attach it to the bridge network&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker network create -d bridge notes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 05: List the docker networks&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker network ls

NETWORK ID     NAME      DRIVER    SCOPE
df7f5f15c7ab   bridge    bridge    local
7405f25b96f6   host      host      local
ee4c71834f71   none      null      local
14219a8cf079   notes     bridge    local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 06: Run a container using the notesapp image and mount the migrations directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run --rm -it --network notes -v /home/ec2-user/notes/migrations:/app/notes/migrations notesapp:0.1 bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 07:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# flask db migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the Application:&lt;/p&gt;

&lt;p&gt;Deploy the Notes App container to verify its functionality. During this phase, monitoring the logs for any issues, such as the application running in debug mode, is crucial. Debug mode is not suitable for production due to potential security and performance implications.&lt;br&gt;
Enhancing for Production:&lt;/p&gt;

&lt;p&gt;Address the debug mode issue by switching the Flask application to run on a more robust WSGI (Web Server Gateway Interface) server, such as Gunicorn. This change involves updating the environment variables, modifying the pip file to include Gunicorn, and adjusting the Dockerfile to execute Gunicorn instead of Flask directly.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>aws</category>
      <category>docker</category>
    </item>
    <item>
      <title>Docker Hands-On Part 05</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Wed, 14 Feb 2024 12:51:28 +0000</pubDate>
      <link>https://dev.to/randiakm/docker-hands-on-part-05-18hh</link>
      <guid>https://dev.to/randiakm/docker-hands-on-part-05-18hh</guid>
      <description>&lt;h2&gt;
  
  
  05. Building Container Images Using Dockerfiles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The evolution of container technology seeks to address and simplify the deployment process, making it more efficient and less prone to errors. Manual image creation, while possible, contradicts this objective by introducing unrepeatable processes fraught with potential mistakes. Dockerfiles represent Docker's solution to this challenge, enabling the automation of image creation through a process that is both reproducible and version-controllable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Dockerfile Advantage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Dockerfile is essentially a script composed of various commands, each instructing Docker how to build a specific image. This method allows developers to define their images as code, significantly reducing the manual labor involved in image creation and ensuring that the process can be easily repeated and integrated into continuous integration/continuous deployment (CI/CD) pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building an Image for a Web Server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll demonstrate how to utilize a Dockerfile to build a container image designed to serve a company's website. The process will be divided into three key versions, each improving upon the last:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Version 1: Updating the Base Image&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start by creating a Dockerfile that updates the base image using the package manager. While updating the base image is generally not required for every Dockerfile, it serves as an illustrative first step in this tutorial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Version 2: Removing the Default Apache Welcome Page&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The second version of the Dockerfile will modify the image to remove the default Apache welcome page. This step is crucial for preparing the image to host custom web content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Version 3: Adding Website Data to the Image&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The final version of the Dockerfile will incorporate the actual website data into the image. This ensures that the website is served directly from the container upon startup, providing a seamless deployment experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying the Final Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the Dockerfile for version 3 is complete, the next step involves building the image and running a container from it. This process will demonstrate the web server in action, serving the company's website from the containerized environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting Started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To begin, create a Dockerfile in a directory dedicated to this project. The Dockerfile will evolve through each version, culminating in a container image that is ready for deployment. This method not only streamlines the development and deployment process but also aligns with best practices for managing application infrastructure as code.&lt;/p&gt;

&lt;p&gt;Step 01: Create a Docker file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd content-widget-factory-inc

$ vim Dokcerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;add the below codes to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#base image httpd
FROM httpd:latest   
# Run the app update
RUN apt update -y &amp;amp;&amp;amp; apt upgrade -y &amp;amp;&amp;amp; apt autoremove -y &amp;amp;&amp;amp; apt clean &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 02: Build the image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build -t widgetfactory:0.1 .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can see&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker images

REPOSITORY              TAG       IMAGE ID       CREATED          SIZE
widgetfactory           0.1       b1f9112e68d5   22 seconds ago   180MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 03: Set variables to examine the image's size and layers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ export showLayers='{{ range .RootFS.Layers }}{{ println . }}{{end}}'
$ export showSize='{{ .Size }}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 04: Show the widgetfactory image's size&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker inspect -f "$showSize" widgetfactory:0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 05: We are going to change Dockerfile&lt;/p&gt;

&lt;p&gt;Add the below two lines &lt;/p&gt;

&lt;p&gt;&lt;code&gt;WORKDIR /usr/local/apache2/htdocs&lt;br&gt;
COPY ./web .&lt;/code&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 image httpd
FROM httpd:latest   
# Run the app update
RUN apt update -y &amp;amp;&amp;amp; apt upgrade -y &amp;amp;&amp;amp; apt autoremove -y &amp;amp;&amp;amp; apt clean &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists*
WORKDIR /usr/local/apache2/htdocs
COPY ./web . 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build -t widgetfactory:0.2 .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 06: Get the widgetfactory:0.2 container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run --rm -it widgetfactory:0.3 bash

$ ls -l
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 07: Run the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -d --name web1 -p 80:80 widgetfactory:0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 08: Let's test&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wget localhost

--2024-02-14 10:31:37--  http://localhost/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3276 (3.2K) [text/html]
Saving to: ‘index.html’

index.html                                          100%[=================================================================================================================&amp;gt;]   3.20K  --.-KB/s    in 0s

2024-02-14 10:31:37 (376 MB/s) - ‘index.html’ saved [3276/3276]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tutorial</category>
      <category>aws</category>
      <category>docker</category>
    </item>
    <item>
      <title>Docker Hands-On Part 04</title>
      <dc:creator>Randika Madhushan Perera</dc:creator>
      <pubDate>Fri, 09 Feb 2024 17:15:47 +0000</pubDate>
      <link>https://dev.to/randiakm/docker-hands-on-part-04-1224</link>
      <guid>https://dev.to/randiakm/docker-hands-on-part-04-1224</guid>
      <description>&lt;h2&gt;
  
  
  04. Storing Container Data In Docker Volumes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this lesson, you are going to learn how to store your container data in Docker Volumes. You can store config files, static shared files, or any stateful information that you need. &lt;/p&gt;

&lt;p&gt;Here we are going to create 3 docker volumes. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-by-Step Guide Storing Container Data In Docker Volumes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Step 01: Run the &lt;code&gt;docker volume&lt;/code&gt; command to see if having existing docker volumes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker volume ls

DRIVER    VOLUME NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 02: Now pull the image for Postgres image for setting up the docker volumes. Here I'm using Postgres 12.1. Postgres needs volumes in the background for the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker pull postgres:12.1

$ docker run -d --name db1 postgres:12.1

$ docker run -d --name db2 postgres:12.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 03: Now run the docker volume command to see the volumes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker volume ls

DRIVER    VOLUME NAME
local     1ae4ee6124f0bee423bec887931d9d80aec330a50b5e4edbaf4f151a19c2f23a
local     3dd9a005bb83fc892e2098237fa2bb3ad26407b82bb28096b64a327f26f4eb89
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 04: It's very difficult to identify the volumes that using above. So we can run the below command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker inspect db1 -f '{{ json .Mounts }}' | python -m json.tool

[
    {
        "Type": "volume",
        "Name": "3dd9a005bb83fc892e2098237fa2bb3ad26407b82bb28096b64a327f26f4eb89",
        "Source": "/var/lib/docker/volumes/3dd9a005bb83fc892e2098237fa2bb3ad26407b82bb28096b64a327f26f4eb89/_data",
        "Destination": "/var/lib/postgresql/data",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create the Docker Volumes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Step 01: We can create docker volumes using the &lt;code&gt;create&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker volume create website

DRIVER    VOLUME NAME
local     1ae4ee6124f0bee423bec887931d9d80aec330a50b5e4edbaf4f151a19c2f23a
local            e534041b91b8b520b79a9f42c4f97fa260cae5e25a11517ce2eed0f92faefad2
local     website

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 02: Put data in the docker volumes. So we are going to put our web app codes under the &lt;em&gt;website&lt;/em&gt; volume&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$  sudo cp -r /home/ec2-user/content-widget-factory-inc/web/* /var/lib/docker/volumes/website/_data/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see the files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo ls -l /var/lib/docker/volumes/website/_data
total 16
drwxr-xr-x. 2 root root   76 Feb  9 16:51 img
-rw-r--r--. 1 root root 3276 Feb  9 16:51 index.html
-rw-r--r--. 1 root root 2910 Feb  9 16:51 quote.html
-rw-r--r--. 1 root root 2611 Feb  9 16:51 support.html
-rw-r--r--. 1 root root 2645 Feb  9 16:51 widgets.html

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remove Unused Docker Volumes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the &lt;code&gt;docker volume prune&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker volume prune

WARNING! This will remove anonymous local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
1ae4ee6124f0bee423bec887931d9d80aec330a50b5e4edbaf4f151a19c2f23a
b61fc8243670de085a3fb120fceec864893ec097838e8be67c05b2e47daaef99
107c5e2f43fb50271133c7df2868716f2f4080e65dcc5cf2367581be6425149e

Total reclaimed space: 0B

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backup and Restore data from Docker Volumes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To do this we need to switch to the root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# docker ps -a
CONTAINER ID   IMAGE           COMMAND                  CREATED        STATUS        PORTS                                   NAMES
77fc2b4639f4   postgres:12.1   "docker-entrypoint.s…"   4 hours ago    Up 4 hours    5432/tcp                                db2
dfa30582e464   postgres:12.1   "docker-entrypoint.s…"   4 hours ago    Up 4 hours    5432/tcp                                db1
53aace921e70   httpd:latest    "httpd-foreground"       12 hours ago   Up 12 hours   0.0.0.0:8080-&amp;gt;80/tcp, :::8080-&amp;gt;80/tcp   httpd


# docker volume ls
DRIVER    VOLUME NAME
local     3dd9a005bb83fc892e2098237fa2bb3ad26407b82bb28096b64a327f26f4eb89
local     e534041b91b8b520b79a9f42c4f97fa260cae5e25a11517ce2eed0f92faefad2
local     website

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to inspect the volume.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# docker volume inspect website
[
    {
        "CreatedAt": "2024-02-09T16:48:21Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/website/_data",
        "Name": "website",
        "Options": null,
        "Scope": "local"
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the backup we need to follow the below instructions. We are storing the backup under/tmp/ dir.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# tar czf /tmp/website_$(date +%Y-%m-%d-%H%M).tgz -C /var/lib/docker/volumes/website/_data .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ls -l /tmp/website_2024-02-09-1708.tgz
-rw-r--r--. 1 root root 11385 Feb  9 17:08 /tmp/website_2024-02-09-1708.tgz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we see how we are doing this using non-privileged users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -it --rm -v website:/website -v /tmp:/backup bash tar czf /backup/website_$(date +%Y-%m-%d-%H%M).tgz -C /website .

$ ls -l /tmp/website_*
-rw-r--r--. 1 root root 11385 Feb  9 17:08 /tmp/website_2024-02-09-1708.tgz
-rw-r--r--. 1 root root 11375 Feb  9 17:13 /tmp/website_2024-02-09-1713.tgz

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tutorial</category>
      <category>aws</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
