<?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: Hamid Shoja</title>
    <description>The latest articles on DEV Community by Hamid Shoja (@hash01).</description>
    <link>https://dev.to/hash01</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%2F538427%2Fb88dc832-018a-41a3-ad11-7dc58f0f15b1.jpeg</url>
      <title>DEV Community: Hamid Shoja</title>
      <link>https://dev.to/hash01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hash01"/>
    <language>en</language>
    <item>
      <title>5- AWS Serverless:: Caching and Event-Driven Design</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Tue, 16 Jun 2026 20:56:00 +0000</pubDate>
      <link>https://dev.to/hash01/aws-caching-and-event-driven-design-2cll</link>
      <guid>https://dev.to/hash01/aws-caching-and-event-driven-design-2cll</guid>
      <description>&lt;p&gt;When building high-scale, resilient systems on AWS, performance isn't just about writing fast code, it’s about avoiding unnecessary work. A Engineer’s philosophy often boils down to a simple truth: &lt;strong&gt;The fastest code is the code that never executes, and the fastest database query is the one you never have to make.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s unpack caching in AWS piece by piece with a few real-world examples.&lt;/p&gt;







&lt;h3&gt;
  
  
  Layer 1: Edge Caching (AWS CloudFront)
&lt;/h3&gt;

&lt;p&gt;AWS CloudFront is a Content Delivery Network (CDN). Instead of forcing every user around the world to request data from your main servers (say, located in Virginia), CloudFront replicates data across hundreds of data centers globally, called &lt;strong&gt;Edge Locations&lt;/strong&gt;. If a user in London requests an asset, CloudFront serves it from a London edge server.&lt;/p&gt;

&lt;p&gt;There are two types of data cached here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Static assets:&lt;/strong&gt; Images, CSS, JavaScript files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cacheable API responses:&lt;/strong&gt; Data from your backend that doesn't change frequently (e.g., a product catalog list).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is controlled using standard web instructions called &lt;strong&gt;&lt;code&gt;Cache-Control&lt;/code&gt; HTTP headers&lt;/strong&gt; sent by your application (e.g., &lt;code&gt;Cache-Control: public, max-age=3600&lt;/code&gt; tells CloudFront to hold onto this data for one hour).&lt;/p&gt;

&lt;h4&gt;
  
  
  Simple Example:
&lt;/h4&gt;

&lt;p&gt;Imagine an e-commerce store. The logo image (&lt;code&gt;logo.png&lt;/code&gt;) and the list of product categories (Shoes, Hats, Shirts) rarely change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Flow:&lt;/strong&gt; When User A requests the category list, CloudFront fetches it from your database once, saves a copy at the Edge, and delivers it. When Users B through Z request the same list, your database is never touched; CloudFront handles it entirely at the edge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pipeline Invalidation:&lt;/strong&gt; When the developers deploy a new version of the website code with a new logo, the deployment pipeline automatically runs a command (&lt;code&gt;aws cloudfront create-invalidation&lt;/code&gt;) to forcefully wipe the old logo out of CloudFront's memory globally so users immediately see the new one.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Layer 2: Application Layer Caching (Amazon ElastiCache Redis)
&lt;/h3&gt;

&lt;p&gt;When a request gets past CloudFront and hits your backend compute (AWS Lambda), you don't want the application to immediately hit the primary database if it doesn't have to. we can place &lt;strong&gt;Amazon ElastiCache Redis&lt;/strong&gt; here. Redis is an ultra-fast, in-memory data store.&lt;/p&gt;

&lt;p&gt;There are a few specific choices here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inside the same VPC:&lt;/strong&gt; A Virtual Private Cloud (VPC) is your private, isolated network in AWS. Putting Lambda and Redis in the same VPC ensures they can talk to each other securely and with incredibly low latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redis over Memcached:&lt;/strong&gt; Memcached is a simple key-value cache (e.g., store a string, get a string). Redis is much more powerful. It supports &lt;strong&gt;richer data structures&lt;/strong&gt; (like sorted sets, hashes, and lists) and &lt;strong&gt;Pub/Sub (Publish/Subscribe)&lt;/strong&gt;, which allows different parts of your application to broadcast events to each other in real-time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Simple Example:
&lt;/h4&gt;

&lt;p&gt;Think of a gaming leaderboard or a user's active shopping cart session.&lt;br&gt;
Instead of querying a slow disk-based database every time a player scores a point, you use a Redis &lt;em&gt;Sorted Set&lt;/em&gt;. Redis handles the math and sorting instantly in memory. If you want to notify other services that a score changed, you use Redis &lt;em&gt;Pub/Sub&lt;/em&gt; to broadcast a message: &lt;code&gt;"Player1 just scored!"&lt;/code&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Layer 3: Database Caching (Amazon DAX)
&lt;/h3&gt;

&lt;p&gt;Amazon DynamoDB is a NoSQL database that natively delivers single-digit millisecond latency. For 99% of applications, that is incredibly fast. However, for extreme scale, milliseconds aren't fast enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DAX (DynamoDB Accelerator)&lt;/strong&gt; is a specialized, fully managed write-through cache designed &lt;em&gt;exclusively&lt;/em&gt; for DynamoDB. It sits directly in front of your DynamoDB tables. While DynamoDB responds in milliseconds (1/1000th of a second), DAX responds in &lt;strong&gt;microseconds&lt;/strong&gt; (1/1,000,000th of a second). Because it is a "write-through" cache, when you write data to DAX, it automatically saves it to DynamoDB for you, ensuring the cache is always up to date.&lt;/p&gt;


&lt;h3&gt;
  
  
  Compute Optimization: Lambda Execution Context Reuse
&lt;/h3&gt;

&lt;p&gt;AWS Lambda is serverless. When a request comes in, AWS spins up a tiny container (an &lt;strong&gt;invocation&lt;/strong&gt;), runs your code, and then pauses it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a Lambda container is spun up from scratch, it's called a &lt;strong&gt;Cold Start&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If a container is reused for a subsequent request, it's called a &lt;strong&gt;Warm Start&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you initialize your database or Redis connection &lt;em&gt;inside&lt;/em&gt; the main function handler, your code will open and close a new connection on &lt;em&gt;every single request&lt;/em&gt;. This destroys performance. By declaring the Redis client outside the handler function (&lt;strong&gt;module scope&lt;/strong&gt;), AWS keeps that connection alive in memory. When a "warm" invocation happens, the Lambda instantly reuses the existing connection.&lt;/p&gt;
&lt;h4&gt;
  
  
  Simple Example (Code Context):
&lt;/h4&gt;

&lt;p&gt;Instead of doing this (Bad):&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="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;redisClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;connectToRedis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Opens a connection EVERY time the function runs&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redisClient&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="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Do this (Good):&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;redisClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;connectToRedis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Module Scope: Runs ONCE during cold start&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="c1"&gt;// Reuses the established connection on all warm starts!&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redisClient&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="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Event-Driven Cache Invalidation
&lt;/h3&gt;

&lt;p&gt;Caching has a legendary problem: &lt;em&gt;How do you know when cached data is old (stale) and needs to be deleted?&lt;/em&gt; The engineer uses two strategies to solve this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;TTL-based (Time-to-Live):&lt;/strong&gt; You set an expiration date on data. For example, "Keep this in Redis for 5 minutes, then delete it automatically."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-Driven Invalidation:&lt;/strong&gt; This is where Event-Driven Design shines. Instead of waiting for a timer to run out, your system reacts to data changes (&lt;strong&gt;mutations&lt;/strong&gt;) in real-time. When a user updates their data, an "event" is triggered. A Lambda function listens to this event and immediately deletes ("invalidates") the old cache key.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Simple Example:
&lt;/h4&gt;

&lt;p&gt;Let's look at a profile update feature:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user updates their profile bio from "Software Engineer" to "Principal Engineer."&lt;/li&gt;
&lt;li&gt;The change is saved to the primary database (DynamoDB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Event:&lt;/strong&gt; DynamoDB emits an event via a feature called &lt;em&gt;DynamoDB Streams&lt;/em&gt; saying: &lt;em&gt;"Hey, User 123 just changed their bio!"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The React:&lt;/strong&gt; An isolated Lambda function wakes up automatically because of that event. It doesn't handle the user's web request; its only job is cache maintenance.&lt;/li&gt;
&lt;li&gt;The Lambda runs a command to delete the old cache key for User 123 in Redis (&lt;code&gt;redis.del("user:123")&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The next time anyone views User 123's profile, the application sees the cache is empty, fetches the brand-new data from the database, and repopulates the cache.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;By combining these layers, we can build a system that is incredibly cost-efficient, lightning-fast, and decoupled&lt;/p&gt;

&lt;p&gt;I hope you find it helpful&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>aws</category>
      <category>redis</category>
      <category>elasticcache</category>
      <category>memcache</category>
    </item>
    <item>
      <title>4- AWS Serverless: Event-Driven Design: SQS, SNS, and EventBridge</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Mon, 15 Jun 2026 20:02:00 +0000</pubDate>
      <link>https://dev.to/hash01/aws-event-driven-design-sqs-sns-and-eventbridge-42j</link>
      <guid>https://dev.to/hash01/aws-event-driven-design-sqs-sns-and-eventbridge-42j</guid>
      <description>&lt;p&gt;When designing modern cloud architectures, &lt;strong&gt;Event-Driven Architecture (EDA)&lt;/strong&gt; is the gold standard for creating decoupled, scalable, and resilient systems. However, choosing the right AWS service for passing messages around can be daunting.&lt;/p&gt;

&lt;p&gt;Let’s break down exactly what this means, how each part works, and look at simple real-world examples for each.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: AWS SQS (Simple Queue Service)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;"For resilient pull-based processing with FIFO for ordering guarantees"&lt;/em&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What it means
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pull-Based Processing:&lt;/strong&gt; Instead of pushing an event directly onto a consumer (which might overwhelm it), messages sit safely in a storage queue. The consumer "pulls" (polls) messages from the queue only when it has the capacity to process them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilient:&lt;/strong&gt; If your consumer service crashes or experiences a sudden traffic spike, the messages aren’t lost. They wait in the queue until the service recovers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FIFO (First-In, First-Out):&lt;/strong&gt; Standard queues process messages roughly in order, but sometimes duplicate or out-of-order messages happen. A &lt;strong&gt;FIFO queue&lt;/strong&gt; guarantees strict ordering (Message A will &lt;em&gt;always&lt;/em&gt; be processed before Message B if A arrived first) and exactly-once processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Example: The Coffee Shop
&lt;/h3&gt;

&lt;p&gt;Imagine a busy coffee shop. Customers place orders, and instead of shouting them directly at one overworked barista (push-based), the orders are printed on paper tickets and lined up on a rail (the queue).&lt;/p&gt;

&lt;p&gt;The barista pulls the next ticket from the rail only when they finish the current drink (pull-based processing). If the barista steps away for a moment, the tickets don't vanish; they just wait on the rail (resilient).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why FIFO matters here:&lt;/strong&gt; If Customer A orders a Latte and Customer B orders an Espresso immediately after, the barista must make the Latte first to ensure fairness and prevent Customer A from waiting forever.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 2: AWS SNS (Simple Notification Service)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;"For fan-out to multiple consumers simultaneously"&lt;/em&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What it means
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fan-Out Pattern:&lt;/strong&gt; This is a "push-based" publish/subscribe (Pub/Sub) pattern. A publisher sends a message &lt;em&gt;once&lt;/em&gt; to an SNS "Topic," and SNS instantly clones and broadcast-pushes that message to multiple subscribers (consumers) simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling:&lt;/strong&gt; The service sending the message doesn't know (or care) who is listening. It just drops the message in the bucket, and SNS handles the distribution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Example: The New Author Announcement
&lt;/h3&gt;

&lt;p&gt;Imagine an author finishes a new book. Instead of emailing every single fan individually, they post an update to a subscription newsletter service (the SNS Topic).&lt;/p&gt;

&lt;p&gt;Instantly, that single update is broadcasted ("fanned out") to thousands of subscribers via different channels: some get an email, some get an SMS text, and another system automatically updates the bookstore website inventory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In a software example:&lt;/strong&gt; When a user buys a product on an e-commerce site, the &lt;code&gt;OrderPlaced&lt;/code&gt; event is sent to SNS. SNS instantly "fans out" this event to three different systems at the exact same time:&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Shipping Service&lt;/strong&gt; queue to pack the item.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Invoicing Service&lt;/strong&gt; queue to charge the card.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Analytics Service&lt;/strong&gt; to update marketing dashboards.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 3: AWS EventBridge
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;"For complex routing with pattern-based rules... my default for internal domain events"&lt;/em&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What it means
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complex Routing &amp;amp; Pattern-Based Rules:&lt;/strong&gt; EventBridge is a serverless event bus. Unlike SNS, which blasts messages to everyone subscribed, EventBridge inspects the &lt;em&gt;contents&lt;/em&gt; of the event payload. You can write specific rules like: &lt;em&gt;"Only forward this event if &lt;code&gt;status&lt;/code&gt; is 'FAILED' and &lt;code&gt;location&lt;/code&gt; is 'US'."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal Domain Events:&lt;/strong&gt; A "domain event" is something meaningful that happened in your business logic (e.g., &lt;code&gt;UserUpgradedToPremium&lt;/code&gt;). EventBridge is built to be the central nervous system connecting all your microservices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema Registry &amp;amp; TypeScript Auto-Generation:&lt;/strong&gt; As your team grows, keeping track of what an event looks like (its schema) becomes difficult. EventBridge’s Schema Registry automatically detects the structure of your events. It can then generate code bindings (like TypeScript types). This means developers get auto-complete in their code editors for events happening across the entire company!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Example: The Airport Sorting System
&lt;/h3&gt;

&lt;p&gt;Think of EventBridge like an automated airport baggage handling system. Every bag (event) has a tag indicating its destination, weight, and airline.&lt;/p&gt;

&lt;p&gt;The main conveyor belt (the Event Bus) scans the tag of every bag and uses rules to route it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the destination is "London," send it to Gate 4.&lt;/li&gt;
&lt;li&gt;If the bag is flagged as "Oversized," route it to the special handling team.&lt;/li&gt;
&lt;li&gt;If it belongs to a third-party partner airline, route it to their terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Furthermore, the airport maintains a central database of exactly what a valid baggage tag looks like (Schema Registry), so every airline's computer system knows precisely how to print a compatible tag without manual coordination.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to use which?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use SQS&lt;/strong&gt; when your primary goal is &lt;strong&gt;load-smoothing and safety&lt;/strong&gt;. You want to make sure your database or microservice doesn't break under high traffic, and you need to process items sequentially.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use SNS&lt;/strong&gt; when you want to &lt;strong&gt;broadcast a single event to multiple distinct systems&lt;/strong&gt; instantly, and you want a simple "fire-and-forget" mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use EventBridge&lt;/strong&gt; when you are building an &lt;strong&gt;enterprise microservice mesh&lt;/strong&gt;. It is the ideal choice when you need to route events based on what is &lt;em&gt;inside&lt;/em&gt; them, integrate with SaaS apps (like Zendesk or Stripe), or want strict code-level governance over your event data structures.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;hope you find it helpful&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sqs</category>
      <category>sns</category>
      <category>eventbridge</category>
    </item>
    <item>
      <title>3- AWS Serverless: REST API vs. HTTP API</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Sun, 14 Jun 2026 18:42:00 +0000</pubDate>
      <link>https://dev.to/hash01/rest-api-vs-http-api-aws-architecture-3pea</link>
      <guid>https://dev.to/hash01/rest-api-vs-http-api-aws-architecture-3pea</guid>
      <description>&lt;p&gt;There is common point of confusion. what's the different between REST API vs. HTTP API in AWS and what's the different between them and a traditional Rest API you write with e.g express in node&lt;/p&gt;

&lt;p&gt;in the broader software world, a "REST API" is just an architectural pattern built on top of HTTP requests.&lt;/p&gt;

&lt;p&gt;The confusion comes entirely from &lt;strong&gt;AWS-specific marketing terminology&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you are inside the AWS ecosystem, &lt;strong&gt;Amazon API Gateway&lt;/strong&gt; is a specific managed service, and AWS chose to split that service into two different flavors (or software products): one called "REST API" and one called "HTTP API."&lt;/p&gt;

&lt;p&gt;Here is exactly how they work under the hood, how they differ internally, and how it compares to traditional servers.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. How It Works: REST API vs. HTTP API (AWS Architecture)
&lt;/h2&gt;

&lt;p&gt;Think of Amazon API Gateway as a reverse proxy or a "front door" that sits in front of your Lambda functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS REST API (The Heavyweight)
&lt;/h3&gt;

&lt;p&gt;When a request hits an AWS REST API, AWS passes that request through a massive feature pipeline &lt;em&gt;before&lt;/em&gt; it ever touches your Lambda code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Client Request] ──&amp;gt; [Authentication (Cognito/IAM)] ──&amp;gt; [Request Validation] ──&amp;gt; [Data Transformation (VTL)] ──&amp;gt; [Your Lambda]

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What happens:&lt;/strong&gt; AWS decrypts the request, validates the JSON schema, checks API keys, runs any custom request transformations using a complex mapping language called VTL, and &lt;em&gt;then&lt;/em&gt; invokes your Lambda function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it costs more:&lt;/strong&gt; You are paying AWS for all that computing power happening inside the API Gateway layer itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS HTTP API (The Express Lane)
&lt;/h3&gt;

&lt;p&gt;When a request hits an AWS HTTP API, AWS strips out almost the entire middle pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Client Request] ──&amp;gt; [JWT/OAuth2 Authorization Only] ──&amp;gt; [Your Lambda]

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What happens:&lt;/strong&gt; The HTTP API acts as a lightning-fast router. It optionally checks a standard JWT token, converts the incoming HTTP request directly into a clean JSON object, and throws it straight into your Lambda function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it costs less:&lt;/strong&gt; Because AWS is doing almost zero processing or data manipulation. Your TypeScript Lambda function handles the validation and logic instead.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. The Traditional Service Comparison (Express.js / NestJS)
&lt;/h2&gt;

&lt;p&gt;In a traditional application (like a Node.js/Express app running on a virtual server or Docker container), you don't have this artificial split. You just write a "REST API."&lt;/p&gt;

&lt;p&gt;Here is how the architecture looks side-by-side:&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Server (Express.js on EC2/Docker)
&lt;/h3&gt;

&lt;p&gt;In a traditional setup, &lt;strong&gt;one single server&lt;/strong&gt; handles everything.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The server listens for an HTTP request.&lt;/li&gt;
&lt;li&gt;Your Express middleware handles routing (&lt;code&gt;app.get('/hello')&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Your Express middleware handles authorization and data validation.&lt;/li&gt;
&lt;li&gt;Your controller executes the business logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  AWS Serverless (HTTP API + Lambda)
&lt;/h3&gt;

&lt;p&gt;In AWS Serverless, the responsibilities are decoupled into completely separate services:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS HTTP API&lt;/strong&gt; handles the networking, SSL certificates, and basic routing (&lt;code&gt;GET /hello&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Lambda&lt;/strong&gt; acts as the isolated execution container that runs your TypeScript code to handle the business logic.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Summary: Unmixing the Terms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In the Real World:&lt;/strong&gt; A REST API is a conceptual design pattern. An HTTP request is the underlying protocol. A traditional Node.js backend handles both of these inside the application code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In AWS Language:&lt;/strong&gt; "REST API" and "HTTP API" are just two different &lt;strong&gt;pricing and feature tiers&lt;/strong&gt; of the Amazon API Gateway service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don't need AWS to do complex request manipulation before your code runs, you choose the &lt;strong&gt;HTTP API&lt;/strong&gt; tier to save 70% on your infrastructure bill and let your TypeScript code do the heavy lifting!&lt;/p&gt;

&lt;p&gt;I hope you find it useful&lt;br&gt;
HASH&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>apigateway</category>
      <category>httpapi</category>
    </item>
    <item>
      <title>2- AWS Serverless: Testing (typescript)</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:31:00 +0000</pubDate>
      <link>https://dev.to/hash01/serverless-typescript-testing-aws-48ab</link>
      <guid>https://dev.to/hash01/serverless-typescript-testing-aws-48ab</guid>
      <description>&lt;p&gt;Shifting from traditional application testing to serverless TypeScript engineering is all about shifting your perspective: &lt;strong&gt;you stop testing a running server, and you start testing how your function responds to events and SDK states.&lt;/strong&gt; Here is a simple, practical example for each testing, using modern AWS SDK v3 syntax.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Unit Testing: Jest + Mock Payloads &amp;amp; SDK Client Mocking
&lt;/h2&gt;

&lt;p&gt;In unit tests, you don't call real AWS services. You pass a simulated API Gateway event to your handler, and you mock the AWS SDK so it returns predictable data instead of hitting live infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code Under Test (&lt;code&gt;src/handler.ts&lt;/code&gt;)
&lt;/h3&gt;



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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddbClient&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;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ddbClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="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="nx"&gt;APIGatewayProxyEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayProxyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;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;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;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;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;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;Missing ID&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="c1"&gt;// Fetch from DynamoDB&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;docClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;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;USERS_TABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;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;User 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;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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="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;
  
  
  The Jest Unit Test (&lt;code&gt;tests/unit.test.ts&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Instead of the legacy &lt;code&gt;aws-sdk-mock&lt;/code&gt;, the modern standard for AWS SDK v3 is &lt;code&gt;aws-sdk-client-mock&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&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;../src/handler&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;APIGatewayProxyEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mockClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-sdk-client-mock&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GetCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/lib-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddbMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mockClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lambda Handler Unit Test&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeEach&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;ddbMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&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;USERS_TABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TestTable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return 200 and user data when user exists&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// 1. Mock the AWS SDK Behavior&lt;/span&gt;
    &lt;span class="nx"&gt;ddbMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GetCommand&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;resolves&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Shape the Mocked APIGatewayProxyEvent Payload&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&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;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Execute handler directly&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="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="nf"&gt;expect&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;response&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="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Integration Testing: LocalStack
&lt;/h2&gt;

&lt;p&gt;Integration tests verify that your code accurately interacts with auxiliary services, without relying on real AWS. LocalStack spins up containerized versions of AWS services inside your CI/CD pipeline or local environment.&lt;/p&gt;

&lt;p&gt;To test against LocalStack, your application code just needs to point its AWS Clients to the local Docker container URL (usually &lt;code&gt;http://localhost:4566&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Integration Test Configuration (&lt;code&gt;tests/integration.test.ts&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CreateTableCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/lib-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&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;../src/handler&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;APIGatewayProxyEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Point the client to LocalStack instead of real AWS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;localStackConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddbClient&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;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localStackConfig&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;docClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ddbClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DynamoDB Integration via LocalStack&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// Setup: Create the table in LocalStack before tests run&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ddbClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CreateTableCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LocalUsersTable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;S&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HASH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;BillingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PAY_PER_REQUEST&lt;/span&gt;&lt;span class="dl"&gt;'&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;USERS_TABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LocalUsersTable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should successfully read data actually written to LocalStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// Seed real data into the LocalStack DynamoDB container&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;docClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LocalUsersTable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-456&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-456&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;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Run the handler — it will communicate directly with LocalStack&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="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="nf"&gt;expect&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. End-to-End (E2E) Testing: Newman
&lt;/h2&gt;

&lt;p&gt;In E2E testing, your application is fully deployed to a staging environment. You treat it entirely as a black box—sending actual HTTP requests to the live API Gateway URL. Newman allows you to run Postman collections natively via the CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Testing Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You export a Postman collection containing your test assertions (&lt;code&gt;my-api-tests.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;You execute it via your terminal or CI environment against the live URL.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Execute your Postman test collection against your deployed AWS Staging endpoint&lt;/span&gt;
newman run ./tests/my-api-tests.json &lt;span class="nt"&gt;--env-var&lt;/span&gt; &lt;span class="s2"&gt;"baseUrl=https://xyz123.execute-api.us-east-1.amazonaws.com/staging"&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Local Execution: AWS SAM Local
&lt;/h2&gt;

&lt;p&gt;Before deploying to staging, you want to see how your Lambda executes inside an isolated Docker container mimicking the real AWS Lambda runtime environment. AWS SAM achieves this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step A: Define your event payload (&lt;code&gt;events/mock-request.json&lt;/code&gt;)
&lt;/h3&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;"pathParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-789"&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;
  
  
  Step B: Invoke the function via CLI
&lt;/h3&gt;

&lt;p&gt;Run the SAM build command to compile your TypeScript down to JavaScript, then use &lt;code&gt;sam local invoke&lt;/code&gt; to execute the function with your mock event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Compile TypeScript and build resources&lt;/span&gt;
sam build

&lt;span class="c"&gt;# 2. Locally invoke the function using the mock event&lt;/span&gt;
sam &lt;span class="nb"&gt;local &lt;/span&gt;invoke UserHandlerFunction &lt;span class="nt"&gt;--event&lt;/span&gt; events/mock-request.json

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

&lt;/div&gt;






&lt;p&gt;Hope youe find it helpful&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>aws</category>
      <category>testing</category>
      <category>jest</category>
      <category>typescript</category>
    </item>
    <item>
      <title>1- AWS Serverless: Designing a serverless API: Order Processing API (E-commerce)</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Thu, 11 Jun 2026 15:40:00 +0000</pubDate>
      <link>https://dev.to/hash01/designing-a-serverless-api-on-aws-order-processing-api-e-commerce-3ekm</link>
      <guid>https://dev.to/hash01/designing-a-serverless-api-on-aws-order-processing-api-e-commerce-3ekm</guid>
      <description>&lt;p&gt;Modern enterprise order processing architectures must decouple synchronous client demands from asynchronous backend dependencies. Here I'll detail a highly scalable, fault-tolerant design built on AWS. By utilizing an automated API Gateway entry point, specialized Amazon Cognito authentication, optimized AWS Lambda logic blocks, an engineered RDS Proxy connection layer, and an event-driven SQS/EventBridge core, the design guarantees isolation, cost efficiency, and sub-millisecond structural routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;p&gt;User places an order → payment is processed → inventory updated → confirmation email sent&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → API Gateway (Cognito auth + validation)
       → Order Lambda (business logic + DynamoDB write)
       → SQS (payment queue)
       → Payment Lambda → EventBridge
                        → Inventory Lambda
                        → Notification Lambda (SES)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&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%2Fy79yqhy6dnrho227u2pl.png" alt="Design" width="800" height="510"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1- Entry Point — API Gateway
&lt;/h2&gt;

&lt;p&gt;REST endpoint: &lt;code&gt;POST /orders&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Request validation via API Gateway models (reject malformed payloads instantly, no Lambda invoked)&lt;/p&gt;

&lt;p&gt;Auth via Cognito User Pool Authorizer — validates JWT token on every request &lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Request Hits: A client sends a POST /orders request with a JWT token in the header.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auth Check: API Gateway automatically intercepts the request and validates the JWT against the Cognito User Pool. If expired or spoofed, it returns 410 Gone / 401 Unauthorized right there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Payload Check: Next, it compares the body against your JSON Schema Model. If a required field like customer_id is missing, API Gateway instantly drops it with a 400 Bad Request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Win: Your downstream services (like Lambda) are never invoked for bad/unauthorized requests, saving compute costs and protecting against basic DDoS or bad actor spam.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cognito Latency: While Cognito authorizers are native, they can add a slight latency overhead to your API's P99 metrics during peak traffic. For massive global scale, some enterprises migrate to custom Lambda Authorizers that cache tokens in ElastiCache (Redis).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Model Validation Limits: API Gateway's built-in validator is great for structural checks (e.g., "is this an integer?"), but it cannot do business logic validation (e.g., "is this item SKU actually in our database?"). You still need lightweight validation downstream.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Throttling: Always configure Usage Plans and Rate Limiting at this layer. Without it, a rogue client could overwhelm your backend before your auto-scaling kicks in.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Business Logic — Lambda (TypeScript)
&lt;/h2&gt;

&lt;p&gt;Bundled with esbuild — tiny bundle, fast cold start&lt;/p&gt;

&lt;p&gt;Uses aws-lambda-powertools for structured logging + correlation IDs + tracing&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;Bundling (esbuild): Strips unused node modules, removes comments, tree-shakes dead code, and transpiles TypeScript down to a single, lightweight JavaScript file. Less code means the internal Lambda service downloads and instantiates your code container incredibly quickly.&lt;/p&gt;

&lt;p&gt;The Execution Lifecycle: * Initialization (Cold Start): Lambda boots the container and runs code outside the handler function. By not putting heavy SDK packages here, initialization remains ultra-fast.&lt;/p&gt;

&lt;p&gt;Invocation (Warm Start): The handler executes. Because DynamoDBClient was stored in a global variable during the first run, subsequent warm invocations bypass the heavy initialization and dynamic import() statement entirely.&lt;/p&gt;

&lt;p&gt;Powertools Observability: Rather than using console.log, Powertools outputs structured JSON logs. If a customer has an issue, you can trace that specific correlationId seamlessly across your logs, metrics, and X-Ray traces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Memory vs. CPU Trap: Developers often assign Lambda the minimum memory (128MB) to "save money". The catch: AWS scales CPU and network performance proportionally with memory. Upgrading to 1024MB or 1536MB often speeds up cold starts and execution times so drastically that the execution costs remain identical or cheaper while delivering a superior P99 response time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VPC Cold Starts: If your business logic needs to query an RDS database inside a private VPC, Lambda must attach an Elastic Network Interface (ENI). While AWS optimized this significantly using Hyperplane, it can still add a predictable overhead to cold starts compared to a Lambda running outside a VPC.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Global State Pollution: Global variables (like DynamoDBClient above) persist across warm starts. If you modify a global variable inside a handler (e.g., global error arrays or temporary user arrays), it will bleed into the next customer's request. Always reset request-specific states inside the handler.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conceptual Application Code (TypeScript)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. GLOBAL SCOPE: Warm start re-use (No heavy SDKs imported here)&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;Logger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-lambda-powertools/logger&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;Tracer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-lambda-powertools/tracer&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;logger&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;Logger&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;tracer&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;Tracer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 2. LAZY LOADING: Dynamically imported only when needed inside handler&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="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="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Clear state/set correlation context&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&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;correlationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Correlation-Id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;awsRequestId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendKeys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;correlationId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Business Logic Validation (Stock/Price Check)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isStockAvailable&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;checkStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;isStockAvailable&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;422&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="s2"&gt;Out of stock&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="c1"&gt;// Lazy load the heavy SDK right before database write&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;DynamoDBClient&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="na"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DB&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;DynamoDBClient&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;DB&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Proceed with processing...&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;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;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345&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;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;logger&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="s2"&gt;Order processing failed&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="k"&gt;as&lt;/span&gt; &lt;span class="nb"&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;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="s2"&gt;Internal 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;h2&gt;
  
  
  3. Database — RDS
&lt;/h2&gt;

&lt;p&gt;table design — orders, users, inventory &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RDS proxy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Serverless Connection Problem: Relational databases assign memory to every single open connection. If your API gets a spike in traffic and Lambda scales up to 2,000 concurrent containers, they will try to open 2,000 direct database connections, instantly crashing RDS with an "out of memory" error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter RDS Proxy: Lambda functions point to the RDS Proxy endpoint instead of the database. The proxy keeps a continuous, optimized pool of connections open to RDS.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multiplexing: When a Lambda function finishes executing an order (takes 50ms), RDS Proxy immediately claims that connection back and hands it to a different Lambda instance. Your DB only ever sees a stable, flatlined connection count.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Session Pinning: RDS Proxy's primary job is to multiplex connections. However, if your Lambda executes certain commands—like preparing a dynamic SQL statement, changing session variables, or utilizing temporary tables—RDS Proxy gets confused and performs &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-Session Pinning. This ties that specific Lambda instance to that specific database connection until the Lambda dies, completely destroying the benefits of the proxy pool. Keep queries standard and stateless.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The IAM Secret Storage Lag: RDS Proxy reads the DB password directly from AWS Secrets Manager. If you rotate your database password in an emergency, there can be a tiny window (seconds) of cached credential lag where the proxy might drop connection handshakes.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DynamoDB vs. RDS: If you genuinely want a Partition Key (userId) and Sort Key (orderId), you should drop RDS and switch to Amazon DynamoDB. In an enterprise context, DynamoDB handles high-scale transactional orders infinitely better without needing an RDS Proxy, VPC configurations, or connection pools, though you sacrifice the ability to run complex SQL JOIN statements across your tables.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  High-Level Relational Database Design (DDL)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Users Table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt; &lt;span class="k"&gt;ZONE&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Orders Table (Composite Index handles the lookups)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;total_amount&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt; &lt;span class="k"&gt;ZONE&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Accelerates "Get all orders for a specific User ordered by date"&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_user_orders&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Inventory Table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;item_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Async Flow — SQS + EventBridge
&lt;/h2&gt;

&lt;p&gt;After order saved → Lambda drops message to SQS (payment queue)&lt;br&gt;
Payment Lambda picks it up, processes payment&lt;br&gt;
On success → publishes event to EventBridge&lt;br&gt;
EventBridge fans out to:Inventory Lambda (update stock)&lt;br&gt;
Notification Lambda (send email via SES)&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Hand-off:&lt;/strong&gt; Once the Business Logic Lambda from Part 2 saves the order as &lt;code&gt;PENDING&lt;/code&gt;, it drops a lightweight message (e.g., &lt;code&gt;{ "orderId": "abc-123", "amount": 99.00 }&lt;/code&gt;) directly into &lt;strong&gt;SQS&lt;/strong&gt;. The API Gateway can instantly return a &lt;code&gt;202 Accepted&lt;/code&gt; response to the client. The user isn't left waiting on a spinner while the payment processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Consumer:&lt;/strong&gt; The &lt;strong&gt;Payment Lambda&lt;/strong&gt; continuously polls SQS. It talks to your third-party payment gateway (like Stripe).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Broadcast:&lt;/strong&gt; On successful payment, the Payment Lambda fires a single structured JSON event into &lt;strong&gt;EventBridge&lt;/strong&gt;. It doesn't know—or care—who needs this information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fan-Out:&lt;/strong&gt; EventBridge evaluates the incoming event pattern. Because it matches a &lt;code&gt;Payment.Success&lt;/code&gt; type, it acts as a traffic cop and duplicates the event, executing both the &lt;strong&gt;Inventory Lambda&lt;/strong&gt; and the &lt;strong&gt;Notification Lambda&lt;/strong&gt; concurrently.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Visibility Timeout Trap:&lt;/strong&gt; Your SQS &lt;code&gt;visibility_timeout_seconds&lt;/code&gt; configuration is hyper-critical. When a Lambda instance reads a message, that message is hidden from other instances for X seconds. If your Payment Lambda hits an API lag with Stripe and takes 31 seconds to complete, but your visibility timeout is set to 30 seconds, SQS will make that message visible &lt;em&gt;again&lt;/em&gt;. A second Lambda will pick it up and process the payment a second time. &lt;strong&gt;Rule of thumb: Visibility timeout must always be &amp;gt; 6 times your Lambda function timeout.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency is Non-Negotiable:&lt;/strong&gt; Because SQS guarantees &lt;em&gt;at-least-once&lt;/em&gt; delivery (network hiccups can cause duplicate messages), your consumers &lt;strong&gt;must&lt;/strong&gt; be idempotent. The Payment Lambda must verify with your DB or payment processor if &lt;code&gt;orderId: abc-123&lt;/code&gt; has already been charged before processing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge Latency vs. Throughput:&lt;/strong&gt; EventBridge is built for massive, complex filtering and cross-microservice routing, but it has a slightly higher delivery latency (typically 20–50ms) compared to Amazon SNS (Simple Notification Service). If you require near-instant sub-millisecond fan-out and don't need advanced JSON schema filtering, an &lt;strong&gt;SNS Topic&lt;/strong&gt; might be a faster alternative, though it lacks EventBridge's robust schema registry capabilities.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Building a enterprise-scale order processing engine on AWS requires balancing system decoupled isolation with a smooth user experience. &lt;/p&gt;

&lt;p&gt;I hope you liked the article and you found it helpful.&lt;/p&gt;

&lt;p&gt;Have questions about this high-level design, or want to discuss alternatives like DynamoDB single-table design? Let me know in the comments below!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Reclaim Your Mac Disk Space</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Fri, 22 May 2026 11:01:52 +0000</pubDate>
      <link>https://dev.to/hash01/reclaim-your-mac-disk-space-3k49</link>
      <guid>https://dev.to/hash01/reclaim-your-mac-disk-space-3k49</guid>
      <description>&lt;p&gt;Your Mac is nearly full. That &lt;code&gt;98% capacity&lt;/code&gt; warning is real, and it will start causing build failures, crashes, and slowdowns if you ignore it.&lt;/p&gt;

&lt;p&gt;This guide walks through what's eating your disk and exactly how to clean it — fast.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Diagnose First
&lt;/h2&gt;

&lt;p&gt;Always start with a full picture before deleting anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the &lt;strong&gt;Avail&lt;/strong&gt; column on &lt;code&gt;/dev/diskXXX&lt;/code&gt;. If you see less than &lt;code&gt;1GB&lt;/code&gt;, you're in trouble.&lt;/p&gt;

&lt;p&gt;Then find the biggest offenders in your home folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Caches/&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Clear the Caches (Safe, ~N GB+)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;~/Library/Caches&lt;/code&gt; is safe to delete. Every app rebuilds its cache on next launch but you can pick some specific ones too &lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Tame Application Support
&lt;/h2&gt;

&lt;p&gt;This is where the real weight lives. Unlike Caches, &lt;strong&gt;be selective here&lt;/strong&gt; — some folders contain important app data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For may Docker Desktop or Rancher Desktop stores its entire Linux VM + all container images.&lt;/p&gt;

&lt;p&gt;If you no longer use Docker just remove it entirly&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Clean Docker Images
&lt;/h2&gt;

&lt;p&gt;Old branch images from months ago are dead weight. Check what you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Repository}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Tag}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Size}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.CreatedSince}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;See how much a full prune would recover:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remove everything not currently running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Or, conservative — only dangling (&lt;code&gt;&amp;lt;none&amp;gt;&lt;/code&gt;) images:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Verify You Recovered Space
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;strong&gt;Avail&lt;/strong&gt; column again. You should be back to several GBs of free space.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Developer's Mindset on Disk Hygiene
&lt;/h2&gt;

&lt;p&gt;Disk bloat is gradual. You don't notice until you're at &lt;code&gt;100%&lt;/code&gt; and your &lt;code&gt;docker build&lt;/code&gt; silently fails.&lt;/p&gt;

&lt;p&gt;A few habits prevent this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;docker system prune&lt;/code&gt; after finishing a feature branch&lt;/li&gt;
&lt;li&gt;Clear &lt;code&gt;~/Library/Caches&lt;/code&gt; monthly — it's always safe&lt;/li&gt;
&lt;li&gt;Keep an eye on &lt;code&gt;~/Library/Application Support/rancher-desktop&lt;/code&gt; if you use container runtimes&lt;/li&gt;
&lt;li&gt;Old branch images in Docker are never coming back — prune them freely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found it helpful&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>development</category>
    </item>
    <item>
      <title>A tip from a senior engineer</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Mon, 19 Jan 2026 18:12:06 +0000</pubDate>
      <link>https://dev.to/hash01/a-tip-from-a-senior-engineer-3pe9</link>
      <guid>https://dev.to/hash01/a-tip-from-a-senior-engineer-3pe9</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/hash01" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F538427%2Fb88dc832-018a-41a3-ad11-7dc58f0f15b1.jpeg" alt="hash01"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/hash01/the-trade-off-clean-testing-vs-code-brevity-in-modern-js-12k0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;The Trade-off: Clean Testing vs. Code Brevity in Modern JS&lt;/h2&gt;
      &lt;h3&gt;Hamid Shoja ・ Jan 19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#frontend&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#backend&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>frontend</category>
      <category>node</category>
      <category>backend</category>
    </item>
    <item>
      <title>Uvicorn in Modern Python APIs</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Mon, 19 Jan 2026 18:10:24 +0000</pubDate>
      <link>https://dev.to/hash01/uvicorn-in-modern-python-apis-4m79</link>
      <guid>https://dev.to/hash01/uvicorn-in-modern-python-apis-4m79</guid>
      <description>&lt;p&gt;A quick guide to why Uvicorn is essential for Python developers in 2026, how to use it, and how it stacks up against other languages.&lt;/p&gt;

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

&lt;p&gt;Uvicorn is a high-speed ASGI (Asynchronous Server Gateway Interface) server. While frameworks like FastAPI or Starlette help you write your code, Uvicorn is the "engine" that actually runs the code and handles web requests from users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we use it?
&lt;/h2&gt;

&lt;p&gt;In the past, Python servers (WSGI) could only handle one request at a time per process. Uvicorn uses an asynchronous event loop, allowing it to handle thousands of concurrent connections (like WebSockets or long-polling) without waiting for one to finish before starting the next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start Example
&lt;/h2&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;uvicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code (main.py):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&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;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http.response.start&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;content-type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text/plain&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http.response.body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello, Dev.to!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn main:app &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternatives to Consider
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Hypercorn&lt;/code&gt;: Great if you need HTTP/3 support.&lt;br&gt;
&lt;code&gt;Daphne&lt;/code&gt;: The go-to for Django Channels.&lt;br&gt;
&lt;code&gt;Granian&lt;/code&gt;: A Rust-based runner for those who need even more speed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture Comparison (Python vs. The World)
&lt;/h2&gt;

&lt;p&gt;How does Uvicorn's "Event Loop" compare to other languages in 2026?&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%2Faa9talvw8flc9n05wz1f.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%2Faa9talvw8flc9n05wz1f.png" alt=" " width="626" height="385"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Perfomance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Unicorn&lt;/strong&gt;: ~150k requests/sec; excellent for I/O but limited by the Global Interpreter Lock (GIL).&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Nodejs&lt;/strong&gt;: ~600k requests/sec; highly optimized for I/O-bound web traffic.&lt;br&gt;
&lt;strong&gt;Go (Goroutines)&lt;/strong&gt;: &amp;gt;1M requests/sec; built-in concurrency makes it a "workhorse" for microservices.&lt;br&gt;
&lt;strong&gt;Rust (Tokio/Axum)&lt;/strong&gt;: &amp;gt;1M requests/sec; the fastest model due to lack of garbage collection and zero-cost abstractions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cpu-intensive tasks
&lt;/h2&gt;

&lt;p&gt;In a single-threaded async loop, one heavy CPU calculation can freeze the entire server. We should offload blocking tasks to a thread pool or a separate worker (like Celery/Arq) to keep the Uvicorn loop responsive.&lt;/p&gt;

&lt;p&gt;Example: Use run_in_executor for blocking code.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/heavy-task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;heavy_task&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Don't run blocking code directly; offload it to keep the loop free
&lt;/span&gt;    &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_running_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_in_executor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;some_blocking_heavy_math&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you are building a modern Python web app, &lt;code&gt;Uvicorn&lt;/code&gt; is the standard choice. It balances the ease of Python with the speed required for modern, real-time web applications.&lt;/p&gt;

&lt;p&gt;Follow for more bite-sized dev tutorials! 🚀&lt;br&gt;
HASH&lt;/p&gt;

&lt;p&gt;refs:&lt;br&gt;
&lt;a href="https://medium.com/@yogeshkrishnanseeniraj/scaling-django-async-workers-uvicorn-gunicorn-celery-and-redis-full-benchmarking-guide-fef057069e96#:%7E:text=Uvicorn%20alone%20provides%20excellent%20async,with%20low%20to%20moderate%20traffic" rel="noopener noreferrer"&gt;https://medium.com/@yogeshkrishnanseeniraj/scaling-django-async-workers-uvicorn-gunicorn-celery-and-redis-full-benchmarking-guide-fef057069e96#:~:text=Uvicorn%20alone%20provides%20excellent%20async,with%20low%20to%20moderate%20traffic&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/21485920/single-threaded-event-loop-vs-multi-threaded-non-blocking-worker-in-node-js#:%7E:text=As%20a%20minor%20addendum%20I,57.8k10%2093%20131" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/21485920/single-threaded-event-loop-vs-multi-threaded-non-blocking-worker-in-node-js#:~:text=As%20a%20minor%20addendum%20I,57.8k10%2093%20131&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=n0KETvEqiCk&amp;amp;t=57" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=n0KETvEqiCk&amp;amp;t=57&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>The Trade-off: Clean Testing vs. Code Brevity in Modern JS</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Mon, 19 Jan 2026 17:57:48 +0000</pubDate>
      <link>https://dev.to/hash01/the-trade-off-clean-testing-vs-code-brevity-in-modern-js-12k0</link>
      <guid>https://dev.to/hash01/the-trade-off-clean-testing-vs-code-brevity-in-modern-js-12k0</guid>
      <description>&lt;p&gt;Hey fellow devs! 👋&lt;/p&gt;

&lt;p&gt;In modern JavaScript and TypeScript development, we are constantly balancing two opposing forces: the desire for &lt;strong&gt;Code Brevity&lt;/strong&gt; (writing concise, minimal code) and the need for &lt;strong&gt;Clean Testing&lt;/strong&gt; (writing code that is easy to isolate and verify).&lt;/p&gt;

&lt;p&gt;Often, the code that is fastest to write is the hardest to test.&lt;/p&gt;

&lt;p&gt;Conversely, code designed for testability often looks "boilerplate-heavy" at first glance.&lt;/p&gt;

&lt;p&gt;Let's explore this trade-off through real-world examples, moving from common patterns to the mindset required for architecting long-term scalable systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Round 1: The Environment Variable&amp;nbsp;Dilemma
&lt;/h2&gt;

&lt;p&gt;This is a classic debate that often appears in code reviews. How do you access environment variables like API keys or feature flags provided by Vite, Webpack, or Node?&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Brief" Approach (Static Constants)
&lt;/h3&gt;

&lt;p&gt;The fastest way is to read the variable directly and store it in a constant. It's one line of code. It's simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config.ts&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;IS_PRODUCTION&lt;/span&gt; &lt;span class="o"&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;meta&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;PROD&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;API_URL&lt;/span&gt; &lt;span class="o"&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;meta&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;VITE_API_URL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// myFeature.ts&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;IS_PRODUCTION&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;./config&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;IS_PRODUCTION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do scary real things&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Hidden&amp;nbsp;Cost
&lt;/h3&gt;

&lt;p&gt;This code is brief, but it is tightly coupled to the build system's global state.&lt;/p&gt;

&lt;p&gt;When you write a unit test for &lt;code&gt;myFeature.ts&lt;/code&gt;, the &lt;code&gt;IS_PRODUCTION&lt;/code&gt; constant is evaluated immediately when the test file loads. Once that constant is set to &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;, it is extremely difficult to change it within the same test suite run.&lt;/p&gt;

&lt;p&gt;To test both scenarios, you often have to resort to "stubbing globally"&amp;nbsp;, telling your test runner (like Vitest or Jest) to fundamentally alter how the JavaScript runtime behaves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Messy Global Testing&lt;/span&gt;
&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stubEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PROD&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="c1"&gt;// Now EVERY test thinks it's prod until you remember to unstub it.&lt;/span&gt;
&lt;span class="c1"&gt;// If you forget, other tests break mysteriously.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The "Testable" Approach (Getter Functions)
&lt;/h3&gt;

&lt;p&gt;The alternative is to wrap the access in a function. It adds boilerplate. It feels slightly redundant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config.ts&lt;/span&gt;
&lt;span class="c1"&gt;// It's just a function returning a value&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;getIsProduction&lt;/span&gt; &lt;span class="o"&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="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&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;PROD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// myFeature.ts&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;getIsProduction&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;./config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// We call the function now&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getIsProduction&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do scary real things&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Benefit: Creating a&amp;nbsp;"Seam"
&lt;/h3&gt;

&lt;p&gt;From a Senior Engineering perspective, we have just created a Seam.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fwiki.c2.com%2F%3FSoftwareSeam" rel="noopener noreferrer"&gt;Seam&lt;/a&gt; is a concept (popularized by Michael Feathers) referring to a place where you can alter the behavior of your program without editing the source code.&lt;/p&gt;

&lt;p&gt;In our tests, we no longer need to hack the global environment. We just need to spy on a standard JavaScript function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Clean Isolated Testing&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Config&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;./config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;does scary things only in prod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create the seam just for this test block&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getIsProduction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;spy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mockReturnValue&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="c1"&gt;// run expectations...&lt;/span&gt;
  &lt;span class="nx"&gt;spy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mockReturnValue&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="c1"&gt;// run other expectations...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The testable approach trades brevity for isolation and control.&lt;/p&gt;




&lt;h2&gt;
  
  
  Round 2: Dealing with&amp;nbsp;Time
&lt;/h2&gt;

&lt;p&gt;Another common area where brevity hurts testing is handling current time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Brief" Approach (Direct&amp;nbsp;Access)
&lt;/h3&gt;

&lt;p&gt;Imagine a function that determines if a discount code is expired.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// discount.ts&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;isDiscountExpired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expiryDate&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;boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Brevity wins here:&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&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;Date&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;now&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;expiryDate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code is incredibly short. But it is non-deterministic.&lt;/p&gt;

&lt;p&gt;If you write a test today that says "Expires tomorrow should return false", that test will pass today. But if you run that same test suite tomorrow, it will fail.&lt;/p&gt;

&lt;p&gt;To test this, you again have to rely on heavy-handed global tool hacks like "Fake Timers" to freeze the system clock of the test runner.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Testable" Approach (Dependency Injection)
&lt;/h3&gt;

&lt;p&gt;To make this testable, we need to take control away from the function itself and inject the dependency (time).&lt;/p&gt;

&lt;p&gt;We can do this via a defaulted parameter (a lightweight form of Dependency Injection).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// discount.ts&lt;/span&gt;
&lt;span class="c1"&gt;// We allow 'now' to be passed in, but default to current time&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;isDiscountExpired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;expiryDate&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="nb"&gt;Date&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;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Default value makes it easy to use in app code&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;return&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;expiryDate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the test is trivial and deterministic. We don't need to freeze system time; we just pass in a fixed date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Clean Testing&lt;/span&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checks expiration&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="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;fixedNow&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2024-01-01T10:00:00Z&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;tomorrow&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2024-01-02T10:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// We inject our fixed time, guaranteeing the result forever&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isDiscountExpired&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tomorrow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fixedNow&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Senior Engineer's Mindset
&lt;/h2&gt;

&lt;p&gt;When you are a junior or mid-level developer, your primary metric is often "velocity"&amp;nbsp;, how fast can I ship this feature? Brevity helps velocity in the short term.&lt;/p&gt;

&lt;p&gt;As you advance to senior or principal roles, your primary metrics shift to &lt;strong&gt;maintainability, stability, and risk reduction.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shift Left
&lt;/h2&gt;

&lt;p&gt;We want to "shift left" on bugs&amp;nbsp;,  finding them during unit tests on a developer's machine, rather than in QA or production.&lt;/p&gt;

&lt;p&gt;If code is brief but relies on global state (like &lt;code&gt;import.meta.env&lt;/code&gt; or &lt;code&gt;new Date())&lt;/code&gt;, developers will instinctively avoid writing tests for it because writing those tests is difficult and painful.&lt;/p&gt;

&lt;p&gt;By introducing slight amounts of boilerplate&amp;nbsp;, creating getter functions, injecting dependencies, creating seams&amp;nbsp;,  we lower the friction required to write a test.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choose Brevity&lt;/strong&gt; for throwaway prototypes, simple scripts, or incredibly confined UI components that have zero logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose Testability&lt;/strong&gt; for business logic, configuration, helpers, and anything that your application relies on to function correctly over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It looks like more code today, but it buys you peace of mind tomorrow.&lt;/p&gt;

&lt;p&gt;If this helped, give it a heart!&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>frontend</category>
      <category>node</category>
      <category>backend</category>
    </item>
    <item>
      <title>Understanding MCP Through a Simple Example: A Practical Introduction</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Sat, 17 Jan 2026 18:41:59 +0000</pubDate>
      <link>https://dev.to/hash01/understanding-mcp-through-a-simple-example-a-practical-introduction-3bp7</link>
      <guid>https://dev.to/hash01/understanding-mcp-through-a-simple-example-a-practical-introduction-3bp7</guid>
      <description>&lt;p&gt;Hey fellow devs! 👋&lt;/p&gt;

&lt;p&gt;I’ve been working with MCP fairly often and like most of us, I learn best by actually building something. Not something production-ready or revolutionary — just something simple enough to understand what MCP actually does.&lt;/p&gt;

&lt;p&gt;That’s why I built a basic currency converter using MCP. It’s intentionally simple, but that’s the point.&lt;/p&gt;

&lt;p&gt;Let me walk you through what can be learned from this example.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Personal Take First
&lt;/h2&gt;

&lt;p&gt;When new protocols and frameworks come out, I want to answer one question: “What problem does this actually solve?”&lt;/p&gt;

&lt;p&gt;This currency converter teaches exactly how MCP works, and that’s worth sharing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Learning Goal
&lt;/h2&gt;

&lt;p&gt;Before writing any code, I want you to understand one specific thing:&lt;/p&gt;

&lt;p&gt;How does AI actually communicate with external tools through MCP?&lt;/p&gt;

&lt;h2&gt;
  
  
  What MCP Actually Does
&lt;/h2&gt;

&lt;p&gt;Here’s what I learned by building this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without MCP:&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;You: "Is ¥10,000 enough to buy a $60 game?"
AI: "I don't have access to current exchange rates, 
     but you can check [some website]."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;With MCP:&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;You: "Is ¥10,000 enough to buy a $60 game?"
AI: 
1. Realizes it needs an exchange rate
2. Sees there's a get_exchange_rate tool available
3. Calls: get_exchange_rate(from="JPY", to="USD", amount=10000)
4. Gets: 10000 JPY = 65.83 USD
5. Responds: "Yes, ¥10,000 is approximately $65.83 USD, 
              so you'd have $5.83 left after buying the game."

The key insight: MCP is just a standardized way for AI to discover and use your tools.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Example Architecture
&lt;/h2&gt;

&lt;p&gt;My simple implementation looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User sends natural language prompt&lt;/li&gt;
&lt;li&gt;Hono API receives it&lt;/li&gt;
&lt;li&gt;API gets list of provided MCP server tools (&lt;code&gt;get_exchange_rate&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;API Sends prompt + tools to Claude AI &lt;/li&gt;
&lt;li&gt;Claude AI interprets when user wants a tool&lt;/li&gt;
&lt;li&gt;In the interception step API calls MCP tool, getting the actual rate&lt;/li&gt;
&lt;li&gt;API sends another request to Claude AI including actual rate + prompt&lt;/li&gt;
&lt;li&gt;User gets their answer
It’s basic. And that’s exactly why it’s useful for learning.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Look at the MCP tool definition:
&lt;/h2&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="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get_exchange_rate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;description:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Get exchange rate between currencies"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;inputSchema:&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="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;properties:&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="err"&gt;from_currency:&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="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;to_currency:&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="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;amount:&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="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;optional:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tool just returns exchange rates. That’s it.&lt;/p&gt;

&lt;p&gt;The AI takes care of everything else.&lt;/p&gt;

&lt;p&gt;Now Let’s take a closer look at the implementation steps together.&lt;/p&gt;

&lt;p&gt;1- User sends natural language prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Is ¥10,000 enough to buy a $60 game?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2- API receives it&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="nx"&gt;app&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="s1"&gt;/api/chat&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- API gets list of provided MCP server tools (get_exchange_rate)&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;tools&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;mcpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listTools&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt; &lt;span class="nx"&gt;Sends&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;Claude&lt;/span&gt; &lt;span class="nx"&gt;AI&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;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&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;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;
      &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5- Claude AI interprets When user wants an action that provided in tools get_exchange_rate&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;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool_use&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;toolUse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&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="nx"&gt;block&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool_use&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6- In the interception step API calls MCP tool, getting the actual rate&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;toolResult&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;mcpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;toolUse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;toolUse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;7- API sends another request to Claude AI including actual rate + prompt&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="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;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&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;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messages&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;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// original prompt&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool_result&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;tool_use_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;toolUse&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="na"&gt;content&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;toolResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// rate info&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;p&gt;8- User gets their answer&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;finalText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;finalText&lt;/span&gt; 

&lt;span class="c1"&gt;// Yes, ¥10,000 is approximately $65.83 USD, &lt;/span&gt;
&lt;span class="c1"&gt;//              so you'd have $5.83 left after buying the game&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Simple Example Matters
&lt;/h2&gt;

&lt;p&gt;Here’s what this simple example demonstrates:&lt;/p&gt;

&lt;p&gt;1.** Tool Discovery**&lt;br&gt;
The AI can see what tools are available and understand when to use them based on the description.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parameter Extraction&lt;/strong&gt;
The AI automatically extracts structured parameters from messy natural language:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;“Is ¥10,000 enough for $60?” → from="JPY", to="USD", amount=10000
“How many dollars is 50 euros?” → from="EUR", to="USD", amount=50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Context Awareness&lt;/strong&gt;
The AI can handle complex queries:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;“I have 5 euros in cash and 20 in my bank, total in USD?”
AI does: 5 + 20 = 25, then calls the tool with 25 EUR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Natural Responses&lt;/strong&gt;
The AI formats the tool’s response naturally instead of just dumping JSON.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  See It Working
&lt;/h2&gt;

&lt;p&gt;I recorded a quick demo showing different types of queries being handled:&lt;/p&gt;

&lt;p&gt;Press enter or click to view image in full size&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%2F6die6pigf3m6qlkdj5ou.gif" 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%2F6die6pigf3m6qlkdj5ou.gif" alt=" " width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing fancy, just the basic flow working as expected.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting It Up (For Learning Purposes)
&lt;/h2&gt;

&lt;p&gt;If you want to try this yourself to understand MCP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/hshoja/Currency-Exchange-MCP-Service
npm install
cp env.example .env
# Add your API keys to .env
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You’ll need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://console.anthropic.com/" rel="noopener noreferrer"&gt;Anthropic API key&lt;/a&gt; (for Claude AI)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://freecurrencyapi.com/" rel="noopener noreferrer"&gt;Freecurrencyapi key&lt;/a&gt; (for exchange rates)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then just send POST requests with natural language prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "prompt": "What is 100 USD in EUR?"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Real Takeaway
&lt;/h2&gt;

&lt;p&gt;After building this simple example, We now understand:&lt;/p&gt;

&lt;p&gt;MCP is not about building smarter APIs.&lt;/p&gt;

&lt;p&gt;It’s about building simple, focused tools and letting AI be the smart layer that connects them to human intent.&lt;/p&gt;

&lt;p&gt;Your tool can be dumb. It should be dumb. Just do one thing well, and let AI the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Potential Use Cases (Beyond This Simple Example)
&lt;/h2&gt;

&lt;p&gt;While the currency converter is basic, the pattern scales to more interesting problems, have a look to these examples.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=09tJS0ZEHms" rel="noopener noreferrer"&gt;AI Agent Crypto MCP Trading&lt;/a&gt;&lt;/strong&gt; : an example which shows how create an mcp that opens or closes positions in a crypto platform by natural language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=GuTcle5edjk&amp;amp;t=1399s" rel="noopener noreferrer"&gt;Kali Linux HACKING MCP&lt;/a&gt;&lt;/strong&gt; : running kali commands for hacking by natural language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=eur8dUO9mvE" rel="noopener noreferrer"&gt;Natural language to SQL&lt;/a&gt;&lt;/strong&gt;: connect and query the database by natural language&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=FLpS7OfD5-s&amp;amp;t=390s" rel="noopener noreferrer"&gt;Building an agentic appointment scheduling ap&lt;/a&gt;p&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complexity isn’t in MCP itself — it’s in the tools you build and connect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check Out The Code
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/hshoja/Currency-Exchange-MCP-Service" rel="noopener noreferrer"&gt;full repository&lt;/a&gt; includes:&lt;/p&gt;

&lt;p&gt;Feel free to clone it, modify it, or use it as a reference while learning MCP yourself.&lt;/p&gt;

&lt;p&gt;Are you learning MCP too? Have you built simple examples to understand new tech? I’d love to hear about it — drop a comment!&lt;/p&gt;

&lt;p&gt;P.S. — If this helped you understand MCP a bit better, give it a like! And remember: simple examples are valid examples.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>webdev</category>
      <category>node</category>
      <category>ai</category>
    </item>
    <item>
      <title>How simultaneously swapping unique IDs in Postgres</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Mon, 08 Dec 2025 18:29:05 +0000</pubDate>
      <link>https://dev.to/hash01/how-simultaneously-swapping-unique-ids-in-postgres-1ki5</link>
      <guid>https://dev.to/hash01/how-simultaneously-swapping-unique-ids-in-postgres-1ki5</guid>
      <description>&lt;p&gt;Hi friends&lt;/p&gt;

&lt;p&gt;Here's a quick tip for when you're working with Postgres, this came in clutch for me when I had to use &lt;code&gt;DEFERRABLE INITIALLY IMMEDIATE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I'm sharing this with a practical example where two rows in a table must exchange unique values simultaneously within the same transaction. This is common when swapping unique IDs or handling cyclic dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Swapping Unique Teacher IDs
&lt;/h3&gt;

&lt;p&gt;Consider a classes table where each class has a unique &lt;code&gt;teacher_id&lt;/code&gt;, but you need to swap the teachers between two classes. The UNIQUE constraint would normally prevent this in a single &lt;code&gt;UPDATE&lt;/code&gt; statement because, mid-operation, there would temporarily be duplicate &lt;code&gt;teacher_id&lt;/code&gt; values.&lt;/p&gt;

&lt;p&gt;So we can Define the table with a unique constraint that is &lt;code&gt;DEFERRABLE INITIALLY IMMEDIATE&lt;/code&gt;:&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;teacher_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;DEFERRABLE&lt;/span&gt; &lt;span class="k"&gt;INITIALLY&lt;/span&gt; &lt;span class="k"&gt;IMMEDIATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Insert initial data&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Biology'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Chemistry'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the database checks the uniqueness after every statement.&lt;br&gt;
Perform the swap within a transaction by explicitly deferring the constraint check:&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;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Temporarily set the specific constraint to DEFERRED mode for this transaction&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINTS&lt;/span&gt; &lt;span class="n"&gt;classes_teacher_id_key&lt;/span&gt; &lt;span class="k"&gt;DEFERRED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- The update statements can now run without immediate failure&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;teacher_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&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="c1"&gt;-- Temporarily creates duplicate 102&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;teacher_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- Resolves the duplicate&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- At the COMMIT point, the database checks all deferred constraints.&lt;/span&gt;
&lt;span class="c1"&gt;-- Since the final state is valid (101 and 102 are unique again), the transaction succeeds.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you tried these updates without the BEGIN; ... SET CONSTRAINTS DEFERRED; ... COMMIT; block, the first UPDATE statement would immediately fail due to the INITIALLY IMMEDIATE setting, which acts as a per-statement check.&lt;/p&gt;

&lt;p&gt;this example demonstrate how to use &lt;code&gt;DEFERRABLE INITIALLY IMMEDIATE&lt;/code&gt; constraints to swap unique values within a transaction:&lt;/p&gt;

&lt;p&gt;Hope that helped! &lt;/p&gt;

&lt;p&gt;Hash&lt;/p&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
    </item>
    <item>
      <title>React 19.2's useEffectEvent vs our Custom useEventCallback</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Sun, 05 Oct 2025 13:02:20 +0000</pubDate>
      <link>https://dev.to/hash01/react-192s-useeffectevent-vs-our-custom-useeventcallback-i45</link>
      <guid>https://dev.to/hash01/react-192s-useeffectevent-vs-our-custom-useeventcallback-i45</guid>
      <description>&lt;p&gt;Hey fellow React devs! 👋&lt;/p&gt;

&lt;p&gt;So React 19.2 dropped and brought us &lt;code&gt;useEffectEvent&lt;/code&gt; - a new hook that solves a problem many of us have been tackling with custom solutions. If you're like me and have been using a custom &lt;code&gt;useEventCallback&lt;/code&gt; hook (or something similar), you're probably wondering: "Should I ditch my trusty custom hook for this new official one?"&lt;/p&gt;

&lt;p&gt;BTW: &lt;a href="https://hamid-shoja.medium.com/react-19-2s-useeffectevent-vs-our-custom-useeventcallback-e96b9ce3811e" rel="noopener noreferrer"&gt;if you interested to read it from medium.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me break it down for you based on some real-world experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Both Hooks Solve
&lt;/h2&gt;

&lt;p&gt;First, let's talk about why these hooks exist. You know that annoying situation where you want to access the latest state/props/reference in a &lt;code&gt;useEffect&lt;/code&gt; without adding them to the dependency array? Yeah, that one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&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;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ShoppingCartContext&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;numberOfItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;onNavigate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;visitedUrl&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;logVisit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;visitedUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;numberOfItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},[&lt;/span&gt;&lt;span class="nx"&gt;numberOfItems&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;onNavigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onNavigate&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// if not passing onNavigate getting lint error&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the Effect should re-run after a render when &lt;code&gt;url&lt;/code&gt; changes (to log the new page visit), but it should &lt;strong&gt;not&lt;/strong&gt; re-run when &lt;code&gt;numberOfItems&lt;/code&gt; changes that creates a new ref for &lt;code&gt;onNavigate&lt;/code&gt; causing new log for every &lt;code&gt;numberOfItems&lt;/code&gt; up and down!&lt;/p&gt;

&lt;p&gt;So we need a way to get a persistent ref for &lt;code&gt;onNavigate&lt;/code&gt; function including the latest &lt;code&gt;numberOfItems&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now if you use new hook &lt;code&gt;useEffectEvent&lt;/code&gt; instead of &lt;code&gt;useCallback&lt;/code&gt; this problem gets solved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&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;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ShoppingCartContext&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;numberOfItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;onNavigate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEffectEvent&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;visitedUrl&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;logVisit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;visitedUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;numberOfItems&lt;/span&gt;&lt;span class="p"&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;onNavigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;url&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This problem already gets solved by another hook called &lt;code&gt;useEventCallback&lt;/code&gt; by creating stable function reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet the Custom Champion: useEventCallback
&lt;/h2&gt;

&lt;p&gt;This is what many of us have been building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useEventCallback&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;extends &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&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;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;T&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;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="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="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's reliable, battle-tested, and works everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  CAN We Now Swap Them?
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. These hooks are &lt;strong&gt;not&lt;/strong&gt; drop-in replacements for each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  useEventCallback instead of useEffectEvent:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Yes you can&lt;/strong&gt;, the result is almost the same, example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&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;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ShoppingCartContext&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;numberOfItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;onNavigate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEventCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;visitedUrl&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;logVisit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;visitedUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;numberOfItems&lt;/span&gt;&lt;span class="p"&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;onNavigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;url&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there are some caveats:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;React's internal magic &amp;gt; our clever hacks&lt;/strong&gt;, so maybe we're missing some React optimizations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The official API is designed for React's future&lt;/strong&gt; - You need perfect Concurrent Mode compliance&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  useEffectEvent instead of useEventCallback:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ✅ You CAN use useEffectEvent when you need stable event handlers for Effect deps
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ useEffectEvent works&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEventCallback&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;doSomething&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;handleClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ❌ You CANNOT use useEffectEvent when:
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;1. You need event handlers for JSX&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ useEventCallback works&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEventCallback&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;doSomething&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Click&lt;/span&gt; &lt;span class="nx"&gt;me&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
&lt;span class="c1"&gt;// ❌ useEffectEvent is NOT for this&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. You're passing callbacks to child components&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Safe with useEventCallback&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onChildEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEventCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handleChildEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&lt;/span&gt; &lt;span class="nx"&gt;onEvent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onChildEvent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
&lt;span class="c1"&gt;// ❌ Don't pass useEffectEvent callbacks as props&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. You need async operations&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ useEventCallback handles async fine&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleAsync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEventCallback&lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;fetchData&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;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ useEffectEvent has restrictions with async&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  My Take: Use Both (For Now)
&lt;/h2&gt;

&lt;p&gt;Here's my practical recommendation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep using &lt;code&gt;useEventCallback&lt;/code&gt;&lt;/strong&gt; for general stable function references, event handlers, and callback props&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adopt &lt;code&gt;useEffectEvent&lt;/code&gt;&lt;/strong&gt; specifically for functions called within effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They serve different purposes, even though they solve similar problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Migration Path
&lt;/h2&gt;

&lt;p&gt;If you're on React 19.2+, start migrating effect-specific uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEventCallback&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentUser&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="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;logEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someData&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;someData&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEffectEvent&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentUser&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="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;logEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someData&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;someData&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But keep &lt;code&gt;useEventCallback&lt;/code&gt; for everything else!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Don't feel bad about your custom hook - it's been doing great work! &lt;code&gt;useEffectEvent&lt;/code&gt; isn't here to replace it entirely, but to provide a more specialized, optimized solution for a specific use case.&lt;/p&gt;

&lt;p&gt;What's your experience been with these hooks? Are you planning to migrate, or sticking with your custom solutions? Drop a comment - I'd love to hear your thoughts!&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
