<?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: Raphael Jambalos</title>
    <description>The latest articles on DEV Community by Raphael Jambalos (@raphael_jambalos).</description>
    <link>https://dev.to/raphael_jambalos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F73734%2Fb8226709-4ec9-4864-a43d-13de204465e0.jpeg</url>
      <title>DEV Community: Raphael Jambalos</title>
      <link>https://dev.to/raphael_jambalos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/raphael_jambalos"/>
    <language>en</language>
    <item>
      <title>AWS Network Challenge 2: Deploy File Upload App on EC2, RDS, DocumentDB</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Fri, 15 Nov 2024 02:27:47 +0000</pubDate>
      <link>https://dev.to/raphael_jambalos/aws-network-challenge-2-deploy-a-file-uploading-app-on-ec2-rds-documentdb-16eb</link>
      <guid>https://dev.to/raphael_jambalos/aws-network-challenge-2-deploy-a-file-uploading-app-on-ec2-rds-documentdb-16eb</guid>
      <description>&lt;p&gt;&lt;em&gt;This talk will be/was presented at the AWS Community Day 2024 - Bacolod on November 16, 2024&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I love taking photographs and would like to create a website selling those prints of those photographs to my friends and family. I started out selling it on Facebook and business has since taken off. I'm selling 100 prints per day, and it's hard to keep track of each transaction. So I decided to do my custom website with the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Product: upload the image for preview, and indicate the initial number of prints available for sale.&lt;/li&gt;
&lt;li&gt;View Products: so buyers can see the photos for sale.&lt;/li&gt;
&lt;li&gt;Checkout: customers can only buy one print design at a time. They can indicate how many of those designs they want to buy, their email, and address.&lt;/li&gt;
&lt;li&gt;View Orders: view my orders.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To simplify things, we don't have any login for now. All users can see everyone's orders.&lt;/p&gt;

&lt;p&gt;For this POC, we use the following tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python and Flask: for the development of the BE and FE&lt;/li&gt;
&lt;li&gt;MongoDB: to store information about the uploaded image&lt;/li&gt;
&lt;li&gt;PostgreSQL: to store data about orders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To give you a headstart, I already developed this application. You can access the Gitlab repository &lt;a href="https://github.com/jamby1100/file-upload-flask" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab 1: Simple deployment via LightSail
&lt;/h2&gt;

&lt;p&gt;The simplest way to get this up and running is to deploy everything inside one server using Amazon LightSail. In one server, we run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;eCommerce Application (Python/Flask)&lt;/li&gt;
&lt;li&gt;MongoDB database&lt;/li&gt;
&lt;li&gt;PostgreSQL database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzemsr67ruwpt91c2rd7.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%2Fmzemsr67ruwpt91c2rd7.png" alt="Image description" width="591" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem with the setup above is that it is a single point of failure. If the server goes down, our app goes down along with all of our data with it. Let's fix that. Let's create 3 servers, one for each of the components:&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%2Fetauvvtmjdmd8yktpfq1.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%2Fetauvvtmjdmd8yktpfq1.png" alt="Image description" width="601" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This solves our problem of losing everything when our eCommerce application goes down. But now, we have to think about the server that holds the MongoDB and PostgreSQL databases. We have to ensure that they don't go down. Or at least, if they do go down, we still have our data intact. In this case, we use the native replication of features of MongoDB and PostgreSQL to have a secondary DB instance that will hold the data. Data written on the primary server have to be written here as well before we return success. This way, we can have two copies of our data in case the primary goes down. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi4czazmowcjdspv6ayu.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%2Fxi4czazmowcjdspv6ayu.png" alt="Image description" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab 2: Deploy to Amazon VPC
&lt;/h2&gt;

&lt;p&gt;The LightSail servers we are managing are starting to get a lot. And they are all publicly available. The problem with this is we have to make an effort to reinforce all of the servers since it is publicly available. Any hacker can reach those instances. We have to ensure there are no gaps in the security. We can reinforce each instance. But when our 5 servers become 500, we would have to do that process over and over. And I don't like to do that. There's just so much to miss. &lt;/p&gt;

&lt;p&gt;To solve this, we will create our virtual network in AWS using a service called AWS VPC. The VPC has a private and public subnet. Servers inside the private subnet cannot be reached directly. We must add a server inside the public subnet that can route traffic to the instances inside the private subnet. This is the only server that needs to be hardened since it's the only one facing the public. Let's call this server our Proxy Server. &lt;/p&gt;

&lt;p&gt;We will then move our LightSail server to a parallel AWS service called Amazon EC2 and deploy those servers to the private subnet. When our customer visits our website, they first connect to the Proxy Server, which then redirects their request to the application server. The app server then connects to the MongoDB and PostgreSQL server for the data it needs.&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%2F412axx50bywo5q1c1h9r.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%2F412axx50bywo5q1c1h9r.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab 3: Use Managed Services to make your life easier
&lt;/h2&gt;

&lt;p&gt;An improvement that can be made here is replacing the proxy server with an AWS service called AWS Application Load Balancer. It is deployed in the public subnet and redirects traffic to the application server. The beauty of this is we don't have to configure and maintain a Proxy Server. We can have this functionality with a press of a button.&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%2F2kpt1s0ai0fcmbfkb4pe.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%2F2kpt1s0ai0fcmbfkb4pe.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem with Lab 2 is we have to manually set up the replication. And if you're an application developer, you'd have to take time to learn and set up how replication works for DynamoDB and MongoDB. This may take days or even weeks to do. Pushing deadlines even further down. &lt;/p&gt;

&lt;p&gt;Thankfully, AWS has recognized that their users usually do this replication themselves. They created a simple way for users to deploy PostgreSQL and MongoDB database servers. Using Amazon RDS and Amazon DocumentDB, we can provision a database server. And with a few additional clicks, we can configure it to be highly available, and its data encrypted with Amazon KMS.&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%2Fp6uh4hxfhe23ztmbs3bk.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%2Fp6uh4hxfhe23ztmbs3bk.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab 4: Prepare our Application for Scaling - Remove State from your Application Servers
&lt;/h2&gt;

&lt;p&gt;Weeks passed and everything is good with your application. Your users are buying your prints like crazy and you can focus on being out in the field and taking more photos. To sell more photos, you made a B1T1 promo on the 15th of the month. Your customers all take to your website at the same time. And your application server gets overloaded and goes down. To keep your application up, you increase the size of your EC2 instance by 8x, from t2.micro to t3.large. &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%2Fny5z7nwspovi3seu08k2.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%2Fny5z7nwspovi3seu08k2.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your customers are now happy and your website works. You become busy fulfilling orders for the next 2 weeks. Then, when your AWS bill comes back, you have a small heart attack. You forgot that you left your EC2 instance in t3.large, 8x the cost! For the next sales, you now spend energy to remind yourself to scale down the instance every time there is a sale. But the problem is when there is no sale and the customers flock to your website, you have to react and increase the size again. And of course, there would be a gap between you scaling the server and when your customers first feel it. So there must be a better way.&lt;/p&gt;

&lt;p&gt;Enter auto-scaling groups. What this does is it adds more EC2 instances when it is needed. And removes it when the demand is no longer there. &lt;/p&gt;

&lt;p&gt;But before we get to that, we need to think about whether our application is ready for that. Currently, our application stores its data on separate servers—order data is stored in DocumentDB, and order data is stored in Amazon RDS. For these data, if we have two application servers, it would be okay because each can just contact the separate database servers for the data. &lt;/p&gt;

&lt;p&gt;However, the images are still stored inside the first application server. If we create a second application server, the images originally uploaded on the 1st server will not be available to our users when their request happens to be served by the 2nd server. Hence, we would need to separate the storage of our images. In this goal, we would use Amazon EFS. We would mount Amazon EFS on both application servers. When a user uploads a file, it is uploaded to the directory that is connected to the EFS drive. It's like a shared drive because as soon as the upload is complete, the image becomes immediately available to the other application server.&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%2F4ykjeip1b75tfomwsun7.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%2F4ykjeip1b75tfomwsun7.png" alt="Image description" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab 5: Auto Scaling your EC2 instances (so you don't have to)
&lt;/h2&gt;

&lt;p&gt;Our application servers are now completely stateless. It means that these servers don't hold data inside them. The data are delegated to EFS for the images, RDS for the orders data, and DocumentDB for the image metadata. Having our app servers to be stateless is critical for us to be able to add more app servers to serve our customers. This ensures that the customer's data will be available to them regardless of which app servers serve their traffic. &lt;/p&gt;

&lt;p&gt;Now, let's go back to the original problem. We don't want to be the ones manually adding and removing application servers. For this, we use Amazon Auto Scaling Groups. First, we create an image of our application server. This is called an AMI. We can use this image to recreate the exact state of our application server - the code, the libraries inside, and the installed OS packages. This creates an identical twin of our first application server. Second, we configure the condition to which the auto-scaling group will add more application servers. The simplest is having the ASG take a look at the average CPU utilization of all the application servers currently in existence. Once it exceeds, say 80% average CPU utilization, the ASG uses the AMI to create more copies of the application server to serve the traffic. &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%2Fimb6gtzexz1zq19exl9l.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%2Fimb6gtzexz1zq19exl9l.png" alt="Image description" width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab 6: Deploy your changes quickly with CI/CD pipelines
&lt;/h2&gt;

&lt;p&gt;With this setup, you'd quickly run into the problem of updating your application server. Every time there is an update, you would have to update the image being used by your auto-scaling group. Then, you would terminate all existing app servers so the ASG can provision new app servers with the updated code. This step is very tedious to do and introduces downtime to your application. &lt;/p&gt;

&lt;p&gt;In this final lab, we would create a CI/CD pipeline that deploys to the current set of EC2 instances in the auto-scaling group. We also update the settings of the auto-scaling group to execute commands to pull the latest code for every new application server it provisions.&lt;/p&gt;

&lt;p&gt;We will create the CI/CD pipeline with the AWS CodePipeline service. Our code will be stored on a public GitHub repository, and we will configure the pipeline to trigger every time there is an update on the main branch of our repository. The second stage of our pipeline will be using CodeDeploy to deploy our code to the Auto Scaling Group. &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%2Fzydt09tns7mu97mjr0rm.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%2Fzydt09tns7mu97mjr0rm.png" alt="Image description" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The simplest setting in CodeDeploy would be to deploy the code change simultaneously on all servers using a technique called All-At-Once. The problem with this technique is that application servers usually go down when new code is being loaded. So when we do an all-at-once, there may be a few seconds to a minute of downtime as all the app servers are receiving and loading the updates. &lt;/p&gt;

&lt;p&gt;An update we can do is to use Half-At-A-Time or One-At-A-Time deployment configurations in CodeDeploy. In this configuration, CodeDeploy chooses half of the app servers and deploys the update to it, leaving the rest of the app servers available to serve your customer's traffic. &lt;/p&gt;

&lt;p&gt;Another problem you'd probably encounter is what if the deployment had a problem. As much as we try to guarantee poor code doesn't reach our production environment, there will still be instances when it would. In the current setting, it deploys to the first half. But then we must configure health checks on CodeDeploy to determine if the deployment was a success. We define success by having our app servers pass a series of checks called a health check. An example health check might be to ensure our website returns success when calling the homepage and the products page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hints
&lt;/h2&gt;

&lt;p&gt;You will provision the EC2, RDS, and DocumentDB instances. Then, you will need to ensure the Flask application inside the EC2 can talk to RDS and DocumentDB. You'll most likely encounter problems with connectivity. Here are our tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure you deployed all those components in the same region and the same VPC&lt;/li&gt;
&lt;li&gt;Check the security groups of each of these components. Check to ensure inbound rules allow communication on the specific ports needed by each component (RDS - 5432, DocumentDB - 27017, EC2 - 5000)&lt;/li&gt;
&lt;li&gt;What helps me learn is to screenshot every step I did when I'm using the AWS service for the first time. So I can get back to them later on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  That's all folks!
&lt;/h2&gt;

</description>
      <category>webdev</category>
      <category>aws</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Full Stack Serverless Challenge #1: AWS Amplify Gen 2</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Sat, 29 Jun 2024 04:19:24 +0000</pubDate>
      <link>https://dev.to/aws-builders/full-stack-serverless-challenge-1-aws-amplify-gen-2-2l31</link>
      <guid>https://dev.to/aws-builders/full-stack-serverless-challenge-1-aws-amplify-gen-2-2l31</guid>
      <description>&lt;p&gt;In this challenge, we'll be learning more about Amplify Gen 2. It is an AWS service that allows front-end developers to deploy full-stack applications. It has a code library that attaches itself to supported frontend frameworks like VueJS, ReactJS, Flutter, React Native, etc. Once attached to the framework, developers can easily create serverless backends, protect them with user authentication, and add file uploads and downloads.&lt;/p&gt;

&lt;p&gt;We start by following the basic getting started guide. From there, we'll get our hands dirty with the core functionalities of Amplify Gen 2.&lt;/p&gt;

&lt;p&gt;This is not a walkthrough post. It forces you to explore on your own by giving a challenge to follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Full Stack Serverless Challenge Series
&lt;/h2&gt;

&lt;p&gt;In this series, I'll try out many full-stack serverless offerings in the market. I'll be doing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Amplify Gen 2 (we're here)&lt;/li&gt;
&lt;li&gt;Ion + SST&lt;/li&gt;
&lt;li&gt;and more...!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  [A] Basic Amplify Gen 2
&lt;/h2&gt;

&lt;p&gt;For this section, we would follow the &lt;a href="https://docs.amplify.aws/vue/start/quickstart/" rel="noopener noreferrer"&gt;getting started tutorial&lt;/a&gt;. It is quite straightforward and is doable within 30 minutes. By the end, you should be able to use the Getting Started repository to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create todo list&lt;/li&gt;
&lt;li&gt;Delete todo list&lt;/li&gt;
&lt;li&gt;Have a login via Cognito&lt;/li&gt;
&lt;li&gt;Each unique user has their own set of todo list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your output should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fq9ys37w9p9res9xcekm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fq9ys37w9p9res9xcekm2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Answer the following questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The todo list is stored where? What backend powers the APIs?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;npx ampx sandbox&lt;/code&gt; do? Is it a local environment? Does it deploy to the cloud?&lt;/li&gt;
&lt;li&gt;How do you deploy an Amplify project?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  [B] Amplify Data + Amplify Functions + Amplify Storage Basics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Allow users to add comments for each todo task. This involves duplicating how the todo list schema was created and making an exact duplicate for comment.&lt;/li&gt;
&lt;li&gt;For each todo task, add capacity to add file attachments. These attachments should use Amplify Files. Once uploaded, the s3 URL and the taskID are sent to a Lambda function deployed using Amplify Functions. It should access a DynamoDB table using the aws-sdk library, and create an entry that associates that image_path with the  todo task id. The DynamoDB, APIGW, and Lambda functions are all deployed using CDK.&lt;/li&gt;
&lt;li&gt;Create an API that allows you to see all the attachments associated with each todo task. Integrate it with your frontend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is my version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fv4tx34446hlq420raeu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fv4tx34446hlq420raeu2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Answer the following questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you add JS packages for Amplify functions?&lt;/li&gt;
&lt;li&gt;Where do we get permission to upload files? &lt;/li&gt;
&lt;li&gt;When we preview the files, are there any security features used?&lt;/li&gt;
&lt;li&gt;How is the experience working with basic CDK? Can you use higher-level constructs to accelerate this?&lt;/li&gt;
&lt;li&gt;Cloudformation has a problem that each stack has a limit of 500 resources. How is that mitigated in CDK?&lt;/li&gt;
&lt;li&gt;How do you represent relationships when using a database like DynamoDB?&lt;/li&gt;
&lt;li&gt;Explore the concept of Single Table Design for dynamodb. How is it different from the approach here?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  [C] Above and Beyond!
&lt;/h2&gt;

&lt;p&gt;Here are above and beyond hands-on that you can try with AWS Amplify. I haven't tried them myself, but scanning through the docs, I thought these are interesting features that I might go back to later. And if you have more time, I encourage you to do it as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.amplify.aws/react/build-a-backend/auth/manage-users/with-admin-actions/" rel="noopener noreferrer"&gt;Add authorizations&lt;/a&gt;. The comments functionality can only be used by admins&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.amplify.aws/react/build-a-backend/auth/manage-users/manage-devices/" rel="noopener noreferrer"&gt;Manage devices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.amplify.aws/react/build-a-backend/auth/manage-users/manage-passwords/" rel="noopener noreferrer"&gt;Reset password and update password&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;For data, use an existing PostgreSQL database&lt;/li&gt;
&lt;li&gt;Add triggers for the Cognito User Pool. For each user, they are added to a DynamoDB table. Study the &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html" rel="noopener noreferrer"&gt;cognito hooks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Study &lt;a href="https://docs.amplify.aws/vue/build-a-backend/add-aws-services/analytics/" rel="noopener noreferrer"&gt;Amplify Analytics&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Appendix A: Hints
&lt;/h2&gt;

&lt;p&gt;There are instances where I got stuck. You might find this section useful as you go through the exercise.&lt;/p&gt;

&lt;h4&gt;
  
  
  A1: Adding package
&lt;/h4&gt;

&lt;p&gt;This allows you to include packages in your backend functions&lt;/p&gt;


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

&lt;p&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;amplify&lt;br&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @aws-sdk/client-dynamodb&lt;/p&gt;

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

&lt;/div&gt;
&lt;h4&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  A2: Adding environment variables&lt;br&gt;
&lt;/h4&gt;

&lt;p&gt;This allows you to pass on environment variables to your lambda functions:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;Function&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-cdk-lib/aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineBackend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;});&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c1"&gt;// create a new API stack&lt;/span&gt;&lt;br&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiStack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api-stack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiStack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AttachmentTable12345&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;br&gt;
  &lt;span class="na"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&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;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;});&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lambdaFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span class="nx"&gt;lambdaFunction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEnvironment&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_TABLE_NAME_MASTER&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;AttachmentTable12345&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h4&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  A3: Use ref() to make the elements binded&lt;br&gt;
&lt;/h4&gt;

&lt;p&gt;The variant of VueJS used does not respond to typical v-binding. I found that using &lt;code&gt;ref()&lt;/code&gt; works best.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;commentData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;&lt;br&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;showCommentsV2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;entering showCommentsV2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;currentId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listCommentByTodo_parent_id&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;todo_parent_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todoId&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&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;br&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BEFORE DATA IS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commentData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;commentData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AFTER DATA IS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commentData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h4&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  A4: Ultimate Hint - &lt;a href="https://github.com/jamby1100/amplify-vue-template" rel="noopener noreferrer"&gt;The Github Repo&lt;/a&gt;&lt;br&gt;
&lt;/h4&gt;

&lt;p&gt;I encourage you to use this as last resort. This contains the answer for Challenge A and B already. I recommend that you start with the repository given in the &lt;a href="https://docs.amplify.aws/vue/start/quickstart/" rel="noopener noreferrer"&gt;AWS Getting Started Guide&lt;/a&gt;, and use your wits solve Challenge B and C.&lt;/p&gt;

&lt;h3&gt;
  
  
  That's all!
&lt;/h3&gt;

&lt;p&gt;Let me know if you made this far, or if you have any questions! Would love to hear from you&lt;/p&gt;

</description>
      <category>aws</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>fullstack</category>
    </item>
    <item>
      <title>AWS Network Challenge 1: Deploy Web App to EC2 / Two-Tier VPC Architecture</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Sat, 25 May 2024 03:19:16 +0000</pubDate>
      <link>https://dev.to/ecvph-tech-team/aws-network-challenge-1-deploy-application-in-ec2-on-two-tier-vpc-architecture-135i</link>
      <guid>https://dev.to/ecvph-tech-team/aws-network-challenge-1-deploy-application-in-ec2-on-two-tier-vpc-architecture-135i</guid>
      <description>&lt;h2&gt;
  
  
  About the Challenge
&lt;/h2&gt;

&lt;p&gt;I manage the Service Delivery Team at eCloudValley Philippines, and one of the first things I teach them is to understand how AWS networks work. As cloud developers, we deploy our applications in Lambda and connect them with databases and other components inside our AWS VPC (virtual network in AWS). Because of this, it becomes imperative for them to know more about how VPCs work. They need to know how to navigate network issues they will encounter. &lt;/p&gt;

&lt;p&gt;In this challenge, we would deploy an application to 2 servers inside a virtual network in AWS. The network will have two tiers: private and public. Our result will be:&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%2Fm4pfk4yqqumsfq1uxyss.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%2Fm4pfk4yqqumsfq1uxyss.png" alt=" " width="800" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we begin, here are a few helpful hints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a series of challenges that get progressively harder. It also meant to state what to do and what you should have learned. This is NOT a step-by-step guide on how to do the challenges.&lt;/li&gt;
&lt;li&gt;I have added links to AWS documentation to help you get started with the challenges. &lt;/li&gt;
&lt;li&gt;I have added guide questions to test your knowledge. It's easy to follow documentation to make this all work. But what we are striving for is a true understanding of how these components work together for a secure, reliable, and working network.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  [A] Basic VPC Skeleton
&lt;/h2&gt;

&lt;p&gt;Let's build the backbones of our VPC network. You'll be creating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/how-it-works.html" rel="noopener noreferrer"&gt;VPC&lt;/a&gt; (&lt;a href="-%20https://docs.aws.amazon.com/vpc/latest/userguide/create-vpc.html#create-vpc-only"&gt;how&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;2 Private &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html" rel="noopener noreferrer"&gt;Subnets&lt;/a&gt; (deployed in separate Availability Zones, &lt;a href="(https://docs.aws.amazon.com/vpc/latest/userguide/create-subnets.html)"&gt;how&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2 Public Subnets (deployed in separate Availability Zones)&lt;/li&gt;
&lt;li&gt;1 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html" rel="noopener noreferrer"&gt;Internet Gateway&lt;/a&gt; (connected to the VPC we just created)&lt;/li&gt;
&lt;li&gt;1 NACL for both subnets (with a rule that allows all traffic)&lt;/li&gt;
&lt;li&gt;1 &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Route_Tables.html" rel="noopener noreferrer"&gt;Route Table&lt;/a&gt; (for Private Subnets, &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/WorkWithRouteTables.html" rel="noopener noreferrer"&gt;how&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;1 Route Table (for Public Subnets)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this exercise, you must be able to answer the following guide questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What makes a subnet private? What makes it public?&lt;/li&gt;
&lt;li&gt;What is an IPv4 CIDR block? What is the difference between /16, /20 and /24?&lt;/li&gt;
&lt;li&gt;Why do we deploy subnets in different availability zones?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Architecture Diagram for Exercise A:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqaqucfirkntwdfxy0crk.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%2Fqaqucfirkntwdfxy0crk.png" alt=" " width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  [B] Setting up the instances
&lt;/h2&gt;

&lt;p&gt;With the VPC backbone in place, let's add more network components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html" rel="noopener noreferrer"&gt;NAT Gateway&lt;/a&gt; (deployed in Public Subnet)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/get-set-up-for-amazon-ec2.html" rel="noopener noreferrer"&gt;Set up&lt;/a&gt; for you to deploy an EC2 instance &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html" rel="noopener noreferrer"&gt;EC2 instance&lt;/a&gt; (deployed in Public Subnet) - &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt; - &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-best-practices.html" rel="noopener noreferrer"&gt;Best Practices&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;EC2 instance (deployed in Private Subnet)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html" rel="noopener noreferrer"&gt;Security Group&lt;/a&gt; that allows all traffic from port 22&lt;/li&gt;
&lt;li&gt;Associate the security group to both EC2 instances&lt;/li&gt;
&lt;li&gt;Private Route Table connected to NAT Gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are a few guide questions to answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DEMO: Be able to enter the CLI of the EC2 instance in the public subnet via SSH. Once inside, access the CLI of the EC2 instance in the private subnet.&lt;/li&gt;
&lt;li&gt;Why do we need a NAT Gateway? How does it operate?&lt;/li&gt;
&lt;li&gt;What is the difference between a NAT Gateway and an Internet Gateway?&lt;/li&gt;
&lt;li&gt;What is the difference between a security group and an NACL?&lt;/li&gt;
&lt;li&gt;Can a computer from the internet access the EC2 instance inside the private subnet? Why or why not?&lt;/li&gt;
&lt;li&gt;What is the difference between a NAT gateway and a NAT instance? When would you use a NAT instance? NAT gateway?&lt;/li&gt;
&lt;li&gt;What happens if you don't assign a public IP Address to an EC2 instance?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Architecture Diagram for Exercise B:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycrhacc0bmgh1hktk61w.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%2Fycrhacc0bmgh1hktk61w.png" alt=" " width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we achieved&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We created our private network in the Cloud! We also created two EC2 instances, one in the private subnet and another in the public subnet. &lt;/p&gt;

&lt;h2&gt;
  
  
  [C] Setting up NGINX and load balancing
&lt;/h2&gt;

&lt;p&gt;We're getting close:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 instance (deployed in Private Subnet 1)&lt;/li&gt;
&lt;li&gt;EC2 instance (deployed in Private Subnet 2)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-centos-7" rel="noopener noreferrer"&gt;Install Nginx on both&lt;/a&gt;. Modify &lt;code&gt;/usr/share/nginx/html/index.html&lt;/code&gt; to add "this is server one" for the 1st EC2 and "this is server two" for the second one.&lt;/li&gt;
&lt;li&gt;Create an &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" rel="noopener noreferrer"&gt;application load balancer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-listener.html" rel="noopener noreferrer"&gt;listener&lt;/a&gt; to receive HTTP traffic&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html" rel="noopener noreferrer"&gt;target group&lt;/a&gt; that includes both EC2 instances as instance targets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are a few guide questions to answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DEMO: Once the resources have been created, get the URL from the application load balancer and put it in your browser. It should alternate displaying "this is server one" and "this is server two". This proves that the ALB is alternating sending traffic between the first and second EC2 instances.&lt;/li&gt;
&lt;li&gt;What is the difference between a NAT Gateway and an Application Load Balancer?&lt;/li&gt;
&lt;li&gt;Why put the EC2 instances in separate private subnets?&lt;/li&gt;
&lt;li&gt;Why put the EC2 instances in the private subnet instead of being exposed directly?&lt;/li&gt;
&lt;li&gt;Why add an application load balancer?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Architecture Diagram for Exercise C:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo7e9p9t1novn3sscfrb2.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%2Fo7e9p9t1novn3sscfrb2.png" alt=" " width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result looks like this:&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%2F6bgsjptchyiomp3gxff4.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%2F6bgsjptchyiomp3gxff4.png" alt=" " width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we achieved&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With Activity A-C, you have deployed your first VPC and have successfully deployed a simple app using a two-tier architecture. That’s honestly very close to how we deploy secure applications on the modern web.&lt;/p&gt;

&lt;p&gt;There is one thing though. The application you accessed looked like something like this when typed in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://jamby-alb-1962899873.ap-southeast-2.elb.amazonaws.com/" rel="noopener noreferrer"&gt;http://jamby-alb-1962899873.ap-southeast-2.elb.amazonaws.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two things come to mind when I see this:&lt;/p&gt;

&lt;p&gt;First, the website is deployed on HTTP, not HTTPS, which leaves our end users insecure. As your end user browses your website, the data you send and receive goes across the internet unencrypted. The internet is made of thousands (or maybe millions) of routers connecting everyone in one big interconnected web. From your end user’s Macbook to your EC2 instance, there may be as many as 100 routers in between. if you leave your traffic as HTTP, any one of those 100 routers can see whatever you are sending between one another.&lt;/p&gt;

&lt;p&gt;Second, as an e-commerce, I’d like to have a decent website name that my end users can easily remember. Something like:&lt;/p&gt;

&lt;p&gt;jambyiscool.ecvphdevs.com&lt;/p&gt;

&lt;p&gt;In exercise D, we will do just that.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-application-load-balancer.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-application-load-balancer.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  [D] Securing setup with HTTPS
&lt;/h2&gt;

&lt;p&gt;Let's take it a step further. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buy a domain in GoDaddy and associate it with Route 53 (if you haven't). If you have access to an existing Domain Name, no need to do this step.&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html" rel="noopener noreferrer"&gt;public certificate&lt;/a&gt; with &lt;a href="https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html" rel="noopener noreferrer"&gt;AWS Certificate Manager&lt;/a&gt;. You may have to create DNS records in your domain manager.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://repost.aws/knowledge-center/associate-acm-certificate-alb-nlb" rel="noopener noreferrer"&gt;Associate the certificate with the ALB created earlier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-creating.html" rel="noopener noreferrer"&gt;Create a route 53 record&lt;/a&gt; for "jambyiscool.yourdomain.com" and point it to the load balancer you just created. If your domain manager is not Route 53, feel free to create the record in your domain manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Architecture Diagram for Exercise D:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4pfk4yqqumsfq1uxyss.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%2Fm4pfk4yqqumsfq1uxyss.png" alt=" " width="800" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the end result:&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%2Fvr5c7wlysgn3jxlcsc2k.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%2Fvr5c7wlysgn3jxlcsc2k.png" alt=" " width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Congratulations on completing this exercise! You know have a full view on how VPC works, and have deployed your first application on a best practice version of a private network in AWS.&lt;/p&gt;

&lt;p&gt;On the next challenge, we will be doing VPC peering, EC2 auto scaling, VPC endpoints and CI/CD. Stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Interested about joining ECV Philippines?
&lt;/h2&gt;

&lt;p&gt;Send us your CV via email to &lt;a href="mailto:ph.hr@ecloudvalley.com"&gt;ph.hr@ecloudvalley.com&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmnm1tjkqjb0joll062m.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%2Frmnm1tjkqjb0joll062m.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@dulhiier?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Nastya Dulhiier&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/lighted-city-at-night-aerial-photo-OKOOGO578eo?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>webdev</category>
      <category>network</category>
    </item>
    <item>
      <title>How to Hack (and secure) Serverless Applications</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Thu, 28 Mar 2024 06:49:00 +0000</pubDate>
      <link>https://dev.to/ecvph-tech-team/how-to-hack-and-secure-serverless-applications-2226</link>
      <guid>https://dev.to/ecvph-tech-team/how-to-hack-and-secure-serverless-applications-2226</guid>
      <description>&lt;p&gt;At eCloudValley Philippines, we prefer developing web backends as serverless applications. We just push the code to AWS and they worry about the infrastructure. We don't have to think about provisioning, patching, scaling and securing it. That's all AWS. In terms of security, since even us don't have access to the infrastructure, we don't have to worry about it. We pay AWS to worry about that for us. But what we do have access to is the code. And that's one major attack vector we have to secure. And in ECVPH, we take that very seriously. &lt;/p&gt;

&lt;p&gt;In this article, we will take a deeper look at how Serverless workloads get hacked, and what you can do about it. &lt;/p&gt;

&lt;p&gt;Here are the 6 common types of attacks on Serverless applications&lt;/p&gt;

&lt;h2&gt;
  
  
  [1] Denial of Service
&lt;/h2&gt;

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

&lt;p&gt;You'd think Serverless applications don't get DDoS attacks. Yes, they can scale fast. But the network components attached to them don't often scale at the same rate as them. We've seen Lambda functions use an RDS database, or is connected to an API that is deployed on a traditional EC2 setup.  Your lambda functions will scale to meet the traffic, but they will rain down traffic into these weaker downstream dependencies.&lt;/p&gt;

&lt;p&gt;That's why it's often a good idea to do event-driven architecture. Instead of directly writing into the database or directly calling the legacy system, the Lambda function queues&lt;/p&gt;

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

&lt;p&gt;It's not only infrastructure that can cause denial of service. Sometimes, there are vulnerabilities in your packages that can cause this. For example, &lt;a href="https://security.snyk.io/vuln/SNYK-PYTHON-ETHABI-6394102"&gt;this Python package for etherium&lt;/a&gt; can cause resource exhaustion when a specific payload is added to it. With just a few requests, the package causes a malfunction that causes it to hog all the CPU and RAM of the device. This exhausts the resources of the server, causing it to go down.&lt;/p&gt;

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

&lt;p&gt;To learn more about how to survive a DDoS attack, check out my &lt;a href="https://dev.to/awscommunity-asean/devs-guide-to-surviving-ddos-attacks-in-your-api-56ke"&gt;blog post&lt;/a&gt; about how we survived a 2M requests per min attack.&lt;/p&gt;

&lt;h2&gt;
  
  
  [2] Denial of Wallet
&lt;/h2&gt;

&lt;p&gt;But suppose that somehow your entire setup is serverless. You're using AWS Lambda for your compute, DynamoDB for your backend, and S3 for all your files. The attacker can just keep attacking you and AWS will be able to keep your website up. But since these services are pay-per-use, you'd just end up racking up higher AWS charges. And so long as they can sustain their attack, they can bankrupt your company to the ground.&lt;/p&gt;

&lt;p&gt;To protect against both DoS and DoW attacks, it is best to put into place some kind of rate limiting. AWS WAF does this by allowing you to limit request per second for each IP address. This makes these attacks harder, but not impossible, especially if they have thousands of IP addresses on their disposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  [3] Remote Code Execution
&lt;/h2&gt;

&lt;p&gt;While the most common attacks are volumetric in nature, some just need a few API calls and they've compromised your system. The most common of which is remote code execution. It is when hackers are able to manipulate the payload so that they can execute custom code in your application.&lt;/p&gt;

&lt;p&gt;Probably the most insecure code in Python is this snippet below. It allows you to evaluate a string and execute it as a Python 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="n"&gt;request_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2 ** 2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine the possibilities. When you are using this code, an attacker can just modify the request body to a more sinister one.&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="n"&gt;request_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;run_sql(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;drop database&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much of the programming world does not use this code anymore. But vulnerabilities found in old packages (that you may still be using) may leave you exposed to this vulnerability. Here's an example from an outdated version of &lt;a href="https://security.snyk.io/vuln/SNYK-PYTHON-ASTROPY-6457316"&gt;Astropy&lt;/a&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  [4] Malicious Packages
&lt;/h2&gt;

&lt;p&gt;The dawn of open-source software has allowed developers to share code much freely. It has accelerated development since we no longer have to write code from the ground up. We can just compose libraries together to make our application. But that openness also comes at a price. &lt;/p&gt;

&lt;p&gt;There are malicious libraries out there that exist to mimic legitimate software. Once added to your project, these libraries open backdoors for hackers to remotely execute code on your server. Like this &lt;a href="https://security.snyk.io/vuln/SNYK-PYTHON-MJPOYTWNGDDH-6483334"&gt;"python package"&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Malicious code don't exclusively come from packages. It can also come from Lambda layers. An inconvenience with Lambda is that when there are libraries with OS-specific dependencies, it is more difficult to upload them to Lambda. You have to compile the library using an OS that is similar to Lambda's OS. An example of this is psycopg2 (an internal library required to make Postgresql work in Python). &lt;/p&gt;

&lt;p&gt;The most convenient way to fix this is just to use a Lambda Layer that somebody else already created. Like this &lt;a href="https://api.klayers.cloud/api/v2/p3.9/layers/latest/ap-southeast-1/html"&gt;Github page of Keith&lt;/a&gt;. While he looks generally reputable, nothing is stopping Keith from inserting malicious code to any one of his public Lambda layers. And once you use the code inside the Lambda layer, he may be able to remotely run code in your lambda. A better fix is to build the lambda layer yourself, even if it takes awhile. In Python, &lt;a href="https://repost.aws/knowledge-center/lambda-python-package-compatible"&gt;Pip added&lt;/a&gt; the &lt;code&gt;--platform&lt;/code&gt; command where you can specific the platform when you install your library.&lt;/p&gt;

&lt;h2&gt;
  
  
  [5] API is too open
&lt;/h2&gt;

&lt;p&gt;The easiest way to hack an API is when it is open to the public. &lt;/p&gt;

&lt;p&gt;What we do is we often pair it with Cognito. The user logs in to the frontend, and Cognito issues a set of tokens that the backend will use to ensure that the user is who he says he is. The token expires every hour and needs to be renewed. During each renewal, we can validate if the session is still valid. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qjwt7v1luh2zazz31l8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qjwt7v1luh2zazz31l8.png" alt="Image description" width="629" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But how about APIs that are only used by other APIs? They shouldn't be open to the public in the first place. They should, at the minimum, be secured by an expiring API token that only the authorized system has access to. An even better approach is to determine these APIs and ensure they can only be accessed privately, inside the network. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw1mhkun7lol8iyxydlp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw1mhkun7lol8iyxydlp.png" alt="Image description" width="736" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  [6] Privilege Escalation
&lt;/h2&gt;

&lt;p&gt;ECS tasks and Lambda functions rely on IAM Roles to access AWS services. Those who leave this role with an admin privilege can leave their account open to hacking.&lt;/p&gt;

&lt;p&gt;If there is a remote code execution vulnerability, hackers can use that to discover the IAM role inside the Lambda / ECS task. Then, they can use this role to create an IAM user with elevated privileges, which they can use to gain access to your AWS accounts. &lt;/p&gt;

&lt;p&gt;But more often than not, this vulnerability is an inside job. For example, we have given the developer limited rights to our AWS account. They can only push code to their CodeCommit repository. But the execution role of the Lambda function is set to Administrator Access. The developer can change the code that is pushed to the CI/CD to include instructions to create an IAM user for him with elevated permissions. &lt;/p&gt;

&lt;p&gt;Another form of privilege escalation is modifying the CloudFormation code that comes with the serverless application. Malicious developers can directly add instructions to create an elevated user there.&lt;/p&gt;

&lt;h2&gt;
  
  
  How about you? What are other ways to "hack" into serverless applications?
&lt;/h2&gt;

&lt;p&gt;Let us know in the comments!&lt;/p&gt;

&lt;p&gt;Also check out my &lt;a href="https://dev.to/aws-builders/top-10-security-best-practices-we-learned-the-hard-way-3f55"&gt;other blog post&lt;/a&gt; on security best practices we learned the hard way.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@maxwbender?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Max Bender&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/person-standing-near-led-sign-XIVDN9cxOVc?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>webdev</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Trigger AWS Lambda with TCP Traffic + Static IP Address</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Wed, 27 Mar 2024 14:46:58 +0000</pubDate>
      <link>https://dev.to/aws-builders/trigger-aws-lambda-with-tcp-traffic-static-ip-address-1bha</link>
      <guid>https://dev.to/aws-builders/trigger-aws-lambda-with-tcp-traffic-static-ip-address-1bha</guid>
      <description>&lt;p&gt;In this post, I will walk you through how to trigger AWS Lambda with TCP traffic. By the end of the post, we would have created a setup similar to the diagram below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fjqw5s4j9ugutzn3i8prq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fjqw5s4j9ugutzn3i8prq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  First question is WHY?
&lt;/h2&gt;

&lt;p&gt;In my case, it is because the client application that I was working with can only send requests via TCP. I needed to supply a stable IP address and a port number. They couldn't really update the client to HTTP because it was a legacy system.&lt;/p&gt;

&lt;p&gt;Given a choice though, it is best to just use HTTP traffic so you can connect with Lambda via API Gateway. Using this approach comes with advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can use API Gateway's routing capabilities to route traffic to small Lambda functions&lt;/li&gt;
&lt;li&gt;The setup is more durable because API Gateway is a managed service. As you'd see later, we would have to create a TCP server to route traffic to Lambda + APIGW. And that can be a single point of failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FACT: Lambda cannot be directly triggered with TCP traffic.
&lt;/h2&gt;

&lt;p&gt;When sending TCP requests, you need the IP address and the port number of the target. Because Lambda functions are only spawned when they are triggered by an event, they are only given an ephemeral IP address and port number. Sending TCP requests to Lambda directly implies you are made aware of the IP address and port number of each invocation of Lambda. And so far, AWS has no feature that makes this possible.&lt;/p&gt;

&lt;p&gt;If you are looking to deploy an application in Lambda to receive and process TCP traffic &lt;em&gt;directly&lt;/em&gt;, this post unfortunately cannot help you. At this time of writing, this is not technically feasible yet. I suggest you just use ECS, EC2 or EKS to do this.&lt;/p&gt;

&lt;p&gt;But if you are looking to find a way for a TCP client to communicate with Lambda by converting TCP traffic to HTTP, then this post is for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Now on the solution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;(1)&lt;/strong&gt; Following the diagram above, we first provision an EC2 instance. To follow with the examples here, I suggest you choose a Linux instance (i.e Ubuntu or Amazon Linux 2).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(2)&lt;/strong&gt; Install Python 3&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(3)&lt;/strong&gt; Create a Python virtual environment and install the requests package.&lt;/p&gt;

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

python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate

pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;(4)&lt;/strong&gt; Create a file called &lt;code&gt;tcp_server.py&lt;/code&gt; and copy paste the contents of this &lt;a href="https://gist.github.com/jamby1100/916b201e2a8739818ab3442a07d74631" rel="noopener noreferrer"&gt;GitHub Snippet&lt;/a&gt; to the file.&lt;/p&gt;

&lt;p&gt;(5) Set the environment variables and run the TCP server.&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;export &lt;/span&gt;&lt;span class="nv"&gt;TCP_IP_ADDRESS_BIND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0 
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TCP_PORT_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8108
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TCP_SERVER_TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;APIGW_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR APIGW ROUTE&amp;gt;"&lt;/span&gt;

&lt;span class="nb"&gt;chmod&lt;/span&gt; +x tcp_server.py
python tcp_server.py


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

&lt;/div&gt;

&lt;p&gt;You should see this in your terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fc743rw78e4q0buc9z6y2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fc743rw78e4q0buc9z6y2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(6) To test your setup, create another Python script by copying the contents of &lt;a href="https://gist.github.com/jamby1100/faf10bbd148e500f9fb20c4f7274cd24" rel="noopener noreferrer"&gt;this second Github Gist&lt;/a&gt; to a file called &lt;code&gt;tcp_client.py&lt;/code&gt;. This gist sends a TCP request to the TCP web server we have started.&lt;/p&gt;

&lt;p&gt;(7) Next, execute these commands to ping the TCP server. &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;export &lt;/span&gt;&lt;span class="nv"&gt;TCP_SERVER_IP_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0 
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TCP_PORT_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8108

python tcp_client.py


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

&lt;/div&gt;

&lt;p&gt;In the screencap below, we see that we have received a response from our TCP web server. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3dxhud0anp6xousol39n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3dxhud0anp6xousol39n.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;This solution is a POC that demonstrates that we can receive TCP traffic and route it to Lambda. The next step is to run this script inside an EC2 instance and expose the port and IP address of the EC2 instance.&lt;/p&gt;

&lt;p&gt;For low-traffic conditions, this setup is okay. But with increased traffic, we have to start thinking about how this would all scale automatically when there is an increased load. For this, we recommend that the TCP server is hosted in a Docker container, orchestrated by either ECS, EKS, or a custom Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;The full repo is found &lt;a href="https://github.com/jamby1100/lambda-tcp-web-server" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@tvick?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Taylor Vick&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/cable-network-M5tzZtFCOfs?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Top 10 Security Best Practices we learned the hard way</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Sun, 03 Mar 2024 10:59:46 +0000</pubDate>
      <link>https://dev.to/aws-builders/top-10-security-best-practices-we-learned-the-hard-way-3f55</link>
      <guid>https://dev.to/aws-builders/top-10-security-best-practices-we-learned-the-hard-way-3f55</guid>
      <description>&lt;p&gt;Our users entrusted us with their data and it's our duty to keep this data secure as they use our application. Unfortunately, security best practices aren't the first things that developers learn when they start out. It's usually learned through experience, when the incident already happened.&lt;/p&gt;

&lt;p&gt;In this article, I'll share our team's experiences and research on how to make your applications secure. It is by no means an exhaustive list, but these are the most common things to miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's dive in with an example eCommerce Store
&lt;/h2&gt;

&lt;p&gt;For this article, let's imagine Fred is an application developer for GoodProducts Manila (GP Manila). It's an eCommerce company that has a website &lt;code&gt;gpmanila.com&lt;/code&gt;. One of his ex-workers Nate is trying to do some damage. Here are a few ways he might do it:&lt;/p&gt;

&lt;h2&gt;
  
  
  [1] Users can access the data of others by URL hijacking
&lt;/h2&gt;

&lt;p&gt;This is by far the simplest security exploit to do, (and thankfully), the simplest to remediate. &lt;/p&gt;

&lt;p&gt;Let's dive into an example:&lt;/p&gt;

&lt;p&gt;Users of GP Manila can register for an account and log in. They can view products and place orders for these products. There is also an orders page where she can track her current and past orders, the URL looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://gpmanila.com/orders?user_id=12345&lt;/code&gt; or &lt;code&gt;https://gpmanila.com/users/12345/orders&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As you may infer, this URL allows users to place the user_id of another user and possibly be able to access their order data. Nothing is stopping Nate from accessing a URL like &lt;code&gt;https://gpmanila.com/orders?user_id=66666&lt;/code&gt; to try and see the data of other users. Even if the user_id is random, I can create an automated script to keep trying different combinations until I get something.&lt;/p&gt;

&lt;p&gt;Another variation of this exploit is when the frontend URL is safe, something like &lt;code&gt;https://gpmanila.com/orders&lt;/code&gt; but the backend URL being called is something like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://api.gpmanila.com/orders?user_id=12345&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This made it a little difficult for non-tech users to see this vulnerability. But our more techy friends will just have to see open the web browser's console and see this waiting for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  [2] Users can access the data of others by Form hijacking
&lt;/h2&gt;

&lt;p&gt;Another similar vulnerability is when our form submissions are too lax. For example, our "Checkout Order" API endpoint for GP Manila contains the following request body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;https://gpmanila.com/orders/:checkout_order&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"silver bag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8 Somewhere Street, Manila City, Philippines"&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;While this method is more secure than #1, it is merely security by obscurity. By hiding the user_id inside the API request data, it is harder to find. But it is still there.&lt;/p&gt;

&lt;p&gt;Nate the hacker can just change the user_id of the API request so that his order will be charged to another user but will be delivered to his address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolve [1] and [2] by using JWT tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this case, we are using an OAuth service called Amazon Cognito which handles the authentication of our app separately from our backend.&lt;/p&gt;

&lt;p&gt;When a user logs in, he should receive an ID token and a Refresh token from Cognito. The frontend then adds the ID token to the &lt;code&gt;Authorization&lt;/code&gt; header of every API request being sent to the backend.&lt;/p&gt;

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

&lt;p&gt;The ID token expires every hour. Once it expires, the backend throws an "IdTokenExpired" error to the FE. The FE takes this as a signal to use the refresh token to get another ID token from Cognito. This ID token is valid for another hour. And is renewed for every hour since. &lt;/p&gt;

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

&lt;p&gt;Both the ID token and the Refresh token are in JWT token format. Before Cognito sends these tokens to the frontend on login, it signs the JWT token with a private key. The process of signing makes sure that if the JWT body gets tampered with, the verification process will fail.&lt;/p&gt;

&lt;p&gt;Once the frontend sends the ID token back on every API request, the backend uses the public key to verify that the JWT token body has not been tampered with. &lt;/p&gt;

&lt;p&gt;So even when Nate tries to change the user_id in the ID token, the backend's verification of the signature will fail.&lt;/p&gt;

&lt;p&gt;Learn more about JWT tokens &lt;a href="https://jwt.io/introduction"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  [3] Form submissions allow XSS attacks
&lt;/h2&gt;

&lt;p&gt;At GoodProducts Manila, we have 1000 products. And we let our users review these products. Since its contents are not moderated, the reviews are posted right away.&lt;/p&gt;

&lt;p&gt;Nate the hacker can exploit this by adding JavaScript runnable content on one of our most famous products.&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is a popup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically gets saved in the database and becomes immediately visible to our other users. Since the review is JavaScript code, your frontend may recognize this as executable JavaScript. Hence, when other users view this review, the JS code executes. &lt;/p&gt;

&lt;p&gt;Now, my sample JS code looks safe. It simply displays a popup for every user who sees this review. But imagine the more sinister things that Nate can do when he can execute JS code on other users' browsers. He can access the user's session cookies and send them to a remote server he controls. That way, he can log in as that user and create transactions on their behalf.&lt;/p&gt;

&lt;p&gt;This exploit is called Cross-Site Scripting (XSS).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolve this by adding AWS WAF or any web firewall&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adding web firewalls like AWS WAF to your frontend and backend deployment can help remediate this. Before the review even gets created to your BE, the firewall takes a quick look at the request body and compares it with the filter rules you have set up. Most web firewalls have anti-XSS capability covered by default, helping you filter our requests with JavaScript code inside.&lt;/p&gt;

&lt;h2&gt;
  
  
  [4] Uploaded files have a virus
&lt;/h2&gt;

&lt;p&gt;We're also looking for good developers for Good Products Manila. So we created a "Contact Us" form where you can upload your resume along with other contact information. &lt;/p&gt;

&lt;p&gt;Nate the hacker can upload his resume in PDF format and add a virus along with it. When your HR opens the file on their end, the virus is also executed and goes on to infect his laptop. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolve this by adding anti-virus&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One way to remediate this is to add anti-virus to our application. In our case, we upload the resume to the S3 bucket and just save an S3 object URL to the database. &lt;/p&gt;

&lt;p&gt;We can build our own lambda function that scans every object that gets uploaded to the S3 bucket... Or we can buy one from the AWS Marketplace. Here are the top two options we have tried.&lt;/p&gt;

&lt;p&gt;For both options, once you have subscribed through the marketplace, you will be redirected to AWS CloudFormation to provision the service to your AWS account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.cloudstoragesec.com/console-overview"&gt;Cloud Storage Security&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloud Storage Security is the top option in terms of anti-virus. It provides access to an account in their web application. You can also add other users/groups to this account, and manage what type of access they have.&lt;/p&gt;

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

&lt;p&gt;It also can protect EBS and EFS volumes. And it integrates nicely with AWS Security Hub, AWS CloudTrail Lake, and AWS Transfer Family.&lt;/p&gt;

&lt;p&gt;However, the downside of this product is the cost. It charges 99USD per month for the first 100GB scanned, and 0.80USD per GB after that. All of this is on top of the cost of the AWS resources provisioned, which is usually at 40 USD (for a basic EC2 instance).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/marketplace/pp/prodview-sykoblbsdgw2o#pdp-overview"&gt;bucketAV powered by ClamAV - Antivirus for Amazon S3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BucketAV is cheaper but it does the basic protection job just as well. But it skimps on more advanced features that Cloud Storage offers.&lt;/p&gt;

&lt;p&gt;It charges 0.05 USD per hour on instance sizes up to m5.large, but you have to shell out for the costs of running the container infrastructure on ECS. &lt;/p&gt;

&lt;p&gt;Its UI is built on top of a CloudWatch dashboard.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  [5] Error messages are too specific
&lt;/h2&gt;

&lt;p&gt;In the spirit of having UI that's helpful to the customer, we tell them where they went wrong. Example error notices are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User with this email does not exist&lt;/li&gt;
&lt;li&gt;The password entered for this email is wrong&lt;/li&gt;
&lt;li&gt;The password used has already been expired&lt;/li&gt;
&lt;li&gt;The user is inactive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But these errors give Nate a lot more information when doing a brute-force attack. He can brute force 1000 emails and be able to know which emails have user accounts on our side. For each user, he can also brute force the password and know the password is wrong. He can keep trying until he gets it right. That's why the best error notification we can give is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User credentials are invalid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It provides little context for Nate, just that he didn't get the password right.&lt;/p&gt;

&lt;h2&gt;
  
  
  [6] Password policies are too simple
&lt;/h2&gt;

&lt;p&gt;We often have to protect users against themselves. Simple passwords can be cracked instantly. By forcing them to a minimum character count, and adding symbols/letters/numbers, we are increasing the complexity of the password, making it harder to crack:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  [7] No API or FE rate-limiting
&lt;/h2&gt;

&lt;p&gt;The easiest way to brute force a password is by accessing the APIs. One way to prevent it is by adding basic rate-limiting to your APIs and frontend assets. Essentially, we limit each IP address to having a maximum of 500 calls per 5 minutes (for example). When the quota is exceeded, the FE or BE returns an error.&lt;/p&gt;

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

&lt;p&gt;Rate-limiting is also the easiest way to protect against DDoS attacks. In &lt;a href="https://dev.to/awscommunity-asean/devs-guide-to-surviving-ddos-attacks-in-your-api-56ke"&gt;this blog post&lt;/a&gt;, we tell our experience of how we survived a DDoS attack and why rate-limiting was central to our strategy.&lt;/p&gt;

&lt;p&gt;By experience, we have to fine-tune the number. If the limit is too small, we will end up rate-limiting normal users, and end up penalizing heavy users of our site. If the limit is too big, it offers no real protection against brute force attacks. We also figured out that frontends needs a higher rate limit. Because we also serve fonts, images, and other static assets. This eats up on our limit, and we can reach it quicker compared to the backend. &lt;/p&gt;

&lt;h2&gt;
  
  
  [8] API logs contain the entire request body
&lt;/h2&gt;

&lt;p&gt;There is no doubt that having some sort of API request/response logging is very helpful for developers to debug issues in production. But if devs aren't careful, they may be creating more headaches for themselves later on. &lt;/p&gt;

&lt;p&gt;If we apply this logging blindly, we'll end up saving passwords, credentials, and other PII data on API logs. These logs tend to be less secure than the actual database. These logs usually sit as files inside our server, waiting for a potential hacker to penetrate the server.&lt;/p&gt;

&lt;p&gt;Resolve this issue by truncating valuable data from the logs as you write them&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;instead&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&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;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"raphael.jamby@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"password: "&lt;/span&gt;&lt;span class="err"&gt;imhandsome&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="s2"&gt;"}

# do instead:
{ "&lt;/span&gt;&lt;span class="err"&gt;email&lt;/span&gt;&lt;span class="s2"&gt;": "&lt;/span&gt;&lt;span class="err"&gt;XXXX&lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="err"&gt;password:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XXXX"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  [9] Credentials are pushed on the frontend
&lt;/h2&gt;

&lt;p&gt;One final issue is adding hardcoded credentials on the frontend. Sometimes, the frontend needs to access AWS resources directly: upload files to S3, access Cognito APIs for login, etc. &lt;/p&gt;

&lt;p&gt;The fastest way to do this is to use the AWS JavaScript SDK and add the AKID and secret key onto the frontend. This is a big mistake, especially if the frontend is a single-page application. There are a lot of web crawlers out there that scour the internet for these keys. And since SPAs are uploaded and delivered to the client in their entirety, the keys will be visible. In a matter of hours after deployment, you'll feel the effects of being hacked. And if the keys you uploaded are admin access, The Armageddon is coming your way.&lt;/p&gt;

&lt;p&gt;The best way to resolve this is not to use AWS AKID and Secret, but instead use AWS Amplify with Cognito. Amplify is a library that helps you call Cognito when your users log in and register. When your user logs in, Cognito and Amplify grant temporary access to specified AWS resources. &lt;/p&gt;

&lt;h2&gt;
  
  
  [10] Not adding API request sanitation
&lt;/h2&gt;

&lt;p&gt;When you are developing your application, you know exactly what each of your API endpoints expects for it to work: attribute name, the data type, the range of valid values, etc. You should use tools like &lt;a href="https://docs.pydantic.dev/latest/"&gt;Pydantic&lt;/a&gt; or &lt;a href="https://docs.python-cerberus.org/"&gt;Cerberus&lt;/a&gt;. These tools check request data against the rules you these expectations to make sure your app gets only what it expects, otherwise, it throws an error. &lt;/p&gt;

&lt;p&gt;When you don't do request sanitation, you allow users to enter values in the API request that may potentially break the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  [BONUS] Your security is only effective if all of your team knows it too
&lt;/h2&gt;

&lt;p&gt;This final tip is not a technical one. We can have all of these best practices but if at the end of the day, your team doesn't understand why they are doing it, they probably won't end up implementing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to share other security best practices? Comment them below!
&lt;/h2&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@flyd2069?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;FlyD&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/red-and-black-love-lock-zAhAUSdRLJ8?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>webdev</category>
      <category>coding</category>
    </item>
    <item>
      <title>Amazon Cognito: The Ugly Parts (and our workarounds)</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Wed, 10 Jan 2024 15:02:05 +0000</pubDate>
      <link>https://dev.to/raphael_jambalos/amazon-cognito-the-ugly-parts-and-our-workarounds-2k5p</link>
      <guid>https://dev.to/raphael_jambalos/amazon-cognito-the-ugly-parts-and-our-workarounds-2k5p</guid>
      <description>&lt;p&gt;Don't get us wrong. Amazon Cognito is sufficient as an authentication service. We've been using it for more than two years now. But as our needs get more complex, we find ourselves boxed in by Cognito's limitations (the ugly parts). In this post, we will tackle these limitations one by one. And how we addressed them.&lt;/p&gt;

&lt;h2&gt;
  
  
  For those new to Cognito...
&lt;/h2&gt;

&lt;p&gt;Amazon Cognito is an AWS service that allows you to add authentication and authorization to your application. It is designed to scale to support millions of users.&lt;/p&gt;

&lt;p&gt;To get started, you create a user pool. It stores your app's users and provides you APIs for login, register, etc. &lt;/p&gt;

&lt;p&gt;Before Cognito, you'd have to find a Python package or Laravel package that adds user authentication to your application, storing your user data on your database. With Cognito, you store user data like email, and passwords in the user pool. &lt;/p&gt;

&lt;p&gt;During user login, you configure your FE to talk to Cognito (using an FE library called Amplify) and it gives you 3 tokens that your application can use as the user browses your website. These tokens prove the user's identity and declare what functionalities they can access.&lt;/p&gt;

&lt;p&gt;With that, let's proceed with the ugly parts...&lt;/p&gt;

&lt;h2&gt;
  
  
  1. You cannot make changes to attributes after User Pool creation.
&lt;/h2&gt;

&lt;p&gt;When you create a user pool, you set both standard and custom attributes. Standard attributes are preset attributes like name, birthday, and email (Cognito has a list, you just tick whichever you need). Custom attributes are the attributes with names and types you specify (i.e. income level, education level, etc).&lt;/p&gt;

&lt;p&gt;But these attributes can only be set when you create the Cognito User Pool. After the user pool has been created, it can no longer be changed. No additional attributes, no removal of attributes after creation. This limitation forces us to &lt;a href="https://www.reddit.com/r/aws/comments/op4rjq/aws_cognito_custom_attributes/" rel="noopener noreferrer"&gt;pair our Cognito user pool with a separate database like a DynamoDB table&lt;/a&gt;, so that our application can accommodate new attributes later on. We stick with essential data in Cognito and use DynamoDB to store all other user information. &lt;/p&gt;

&lt;p&gt;When the user updates their account (i.e. name, gender, section, income), our FE would have to call an API endpoint that updates both Cognito and DynamoDB. It would have been nice to just have to call Cognito and not develop our own set of APIs.&lt;/p&gt;

&lt;p&gt;Unfortunately, if you need to change attributes in a Cognito user pool, you will have to create a new user pool and migrate your users there (an exhausting affair, as you'll see &lt;a href="https://repost.aws/knowledge-center/cognito-change-user-pool-attributes" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  2. No support for search by custom attributes.
&lt;/h2&gt;

&lt;p&gt;Cognito cannot search through custom attributes &lt;a href="https://stackoverflow.com/questions/53293080/search-amazon-cognito-users-by-custom-attribute" rel="noopener noreferrer"&gt;because they aren't indexed attributes&lt;/a&gt;. It can only query standard attributes &lt;/p&gt;

&lt;p&gt;This forces us to put even more functionality into a custom API and query DynamoDB instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Customizing the hosted UI is very limited. But Custom UI does not have the same feature set as hosted UI
&lt;/h2&gt;

&lt;p&gt;For Hosted UI, Cognito's OAuth is robust. When the user signs in, it gives you a code that you can exchange for token_set. Your backend does this exchange so you can keep track of your tokens.&lt;/p&gt;

&lt;p&gt;For Custom UI, we use Amplify. We cannot use the code method anymore. We use &lt;code&gt;Auth.signIn()&lt;/code&gt;. It verifies the username and password, and it sends the token set to the frontend. This is troublesome because our FE now needs to call our custom BE to store the session and the tokens associated with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. You can't keep track of the sessions
&lt;/h2&gt;

&lt;p&gt;When your user login, Cognito generates &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html" rel="noopener noreferrer"&gt;3 JWT tokens&lt;/a&gt;: id_token, refresh_token and access_token. Let's call them the token set. &lt;/p&gt;

&lt;p&gt;Cognito keeps track of the token set internally but does not give you APIs to do the same. So, if you need a feature like "list all active sessions", you would need to store the sessions on your database. This means that after every login with Cognito, your FE would have to call your BE APIs to send the token set and create the session on your database. All this trouble just so you can show these sessions later.&lt;/p&gt;

&lt;p&gt;Cognito has only 3 APIs for token management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate token set (after login)&lt;/li&gt;
&lt;li&gt;Revoke one token&lt;/li&gt;
&lt;li&gt;Revoke all tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we are keeping track of the sessions on our database, we have to wrap those Cognito APIs into our own API Endpoint. If the user wants to delete a session, we delete it on our database and revoke the token in Cognito.&lt;/p&gt;

&lt;p&gt;We also have to create an extra API endpoint for the Show All Sessions API, which is currently not supported by Cognito.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Cognito APIs have rate limitations (and some couldn't be increased)
&lt;/h2&gt;

&lt;p&gt;On one of our projects, we had to migrate 3M users from an on-premise database. We also had to migrate their passwords (if it complies with Cognito's password policy). Cognito has a CSV Import functionality but it does not allow importing passwords, so we had to find another route. &lt;/p&gt;

&lt;p&gt;We created a lambda function that reads a CSV from S3. It reads the CSV row-by-row and sends each row to SQS. SQS queues each row, and another lambda function consumes those tasks. For each user, the consumer lambda function does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the user in Cognito [CreateCognitoUser]&lt;/li&gt;
&lt;li&gt;Create the user in DynamoDB, with the user_sub from Cognito as the primary key&lt;/li&gt;
&lt;li&gt;Using Cognito Admin SDK, change the user's password with the one in the file [AdminUpdateUser]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fclnite51wpfx3ymvb0ex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fclnite51wpfx3ymvb0ex.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we have 3M users, this operation took around 26 hours. And this is where we encountered several issues. &lt;/p&gt;

&lt;p&gt;[1] The CreateCognitoUser API has a limit of 100 requests per minute. We also asked AWS to expand their request limit to 200, which they did.&lt;/p&gt;

&lt;p&gt;[2] The AdminUpdateUser API has a limit of 30 requests per minute. And this cannot be requested for a higher limit. And this is where we got stumped. We repeatedly got throttled here so we had to add code in our Lambda that retries if we got throttled. But it had the unfortunate consequence of also limiting our rate of user creation to 30 requests per second. This caused our deployment to last for 26+ hours.&lt;/p&gt;

&lt;p&gt;[3] Since our workflow ran for 26+ hours, our Lambda scaled up to accommodate the demand. It eventually consumed all available concurrency in the prod account. This means all the other Lambda functions couldn't run anymore because ours hogged all the concurrency in the account! We had to request AWS to expand our account-level concurrency to 10,000 and allocate just 2,000 concurrency to Lambda functions involved in the migration. The 8,000 concurrency is for other lambda functions to use. &lt;/p&gt;

&lt;h2&gt;
  
  
  7. Dirty data
&lt;/h2&gt;

&lt;p&gt;Our 26hr migration run had some problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user is created multiple times in Cognito even if they appear only once in the CSV. We had to manually delete these users&lt;/li&gt;
&lt;li&gt;The user is created in Cognito, but not in DynamoDB. This may be because the Lambda function stopped at the part where the user is created. We deleted these users as well.&lt;/li&gt;
&lt;li&gt;The user is created in DynamoDB but not in Cognito.&lt;/li&gt;
&lt;li&gt;Some users were created with the state FORCE_CHANGE_PASSWORD, which our application wasn't designed to handle.&lt;/li&gt;
&lt;li&gt;Cognito treats emails as case-sensitive. So a user with the email "&lt;a href="mailto:Jamby@aws.com"&gt;Jamby@aws.com&lt;/a&gt;" and "&lt;a href="mailto:jamby@aws.com"&gt;jamby@aws.com&lt;/a&gt;" are different users.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  8. Our bill spiked during the migration
&lt;/h2&gt;

&lt;p&gt;Of the 3M users our customer had, perhaps only 300K are active users at any given point in time. Cognito prices per monthly active user. So we expected to be billed for only 300K users. However, if you created the users programmatically, they are considered active upon creation. Hence, for the first month, we had to pay for 3M active users! Not to mention the additional costs of running thousands of Lambda functions for 26 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. It's very expensive to "dry run" a big migration
&lt;/h2&gt;

&lt;p&gt;With 3M users, we have a lot of variety in our data. It would have been ideal if we could run the whole dataset before it goes to production. But doing so would cost over 9000 USD! So we can only test with a subset of the data. This meant we didn't encounter all sets of possible errors during the dry run. And so we had to face a lot of the errors after the production migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's all folks!
&lt;/h2&gt;

&lt;p&gt;I hope this article showed you what you're getting into with Cognito, and the potential pain points you'd encounter down the line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Did I miss anything? Comment it below!
&lt;/h3&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@onurbinay?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Onur Binay&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-person-holding-a-phone-Uw_8vSroCSc?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Challenge #4: Create CI/CD for Serverless Apps</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Thu, 17 Nov 2022 09:07:49 +0000</pubDate>
      <link>https://dev.to/awscommunity-asean/challenge-4-create-cicd-for-serverless-apps-dk4</link>
      <guid>https://dev.to/awscommunity-asean/challenge-4-create-cicd-for-serverless-apps-dk4</guid>
      <description>&lt;p&gt;&lt;strong&gt;This was presented as a lightning talk last Nov 17, 2022 in the 4th AWSUG F2F Meetup at the GCash Office, BGC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this post, we will create a basic CI/CD pipeline for the serverless application we built in the first challenge. For a quick recap, our serverless application has 3 API endpoints: for creating a loyalty card, seeing all loyalty cards, and displaying details about one loyalty card. The loyalty card data are stored in DynamoDB:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8m6n0j91n2y5v3mzip22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8m6n0j91n2y5v3mzip22.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a CI/CD pipeline, we can deploy changes to our application in a consistent and automated way. &lt;/p&gt;

&lt;p&gt;To achieve that, we will create a CI/CD pipeline with AWS CodePipeline. Inside CodePipeline, we have 2 stages. One for Source and another for Build. By the end of this walkthrough, our changes will be deployed to production once it is merged to master:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SOURCE STAGE&lt;/strong&gt;: CodePipeline simply watches for any changes from the master branch in GitHub. Once a change has been pushed, our pipeline gets triggered and the code is copied for further processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BUILD STAGE&lt;/strong&gt;: CodePipeline triggers CodeBuild and supplies it with a copy of the source code. CodeBuild is an automated build service. It relies on a file called "buildspec.yml" inside the repository. The file contains instructions on how to prepare and deploy our Serverless App. When CodeBuild is triggered, it executes these instructions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fphre4dbakk0b6lxiv2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fphre4dbakk0b6lxiv2qh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Now, let's set up our CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;[1]&lt;/strong&gt; Head over to CodePipeline by searching for it in the top search bar&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fgrottd2oq2y5oxg08kwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fgrottd2oq2y5oxg08kwp.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, hit the "Create pipeline" button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Flnb26lvuu4606n40i1sb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Flnb26lvuu4606n40i1sb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[2]&lt;/strong&gt; Type the name of the pipeline and leave everything else as default. Press Next&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fxngmczrareksgnq44gih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fxngmczrareksgnq44gih.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[3]&lt;/strong&gt;  In the Source stage, select the source provider that applies to you. For me, my code is stored in GitHub so I select the "Github (Version 2)" option.&lt;/p&gt;

&lt;p&gt;Since my repository is outside AWS, I need to authorize CodePipeline to access it. Press "Connect to GitHub". You may have to do something similar if you select BitBucket. Aside from the two, CodePipeline supports CodeCommit as a source repository. However, Gitlab is not natively supported.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fyo58r7wnabszyjlmxjvk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fyo58r7wnabszyjlmxjvk.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A pop-up screen will appear. Create a name for your connection. Then, press "Connect to GitHub"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Frjogkp2grfofambsml4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Frjogkp2grfofambsml4k.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are signed in to the GitHub account, you will see this pop-up screen in Github that requires your permission. Grant it by clicking "Authorize AWS Connector for GitHub"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftd6jgu0jzrvuwmky7vq3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftd6jgu0jzrvuwmky7vq3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[4]&lt;/strong&gt; You will be redirected back to this screen. If you have done this before, you will see a pre-installed GitHub App. If there is none, hit "Install a new app". You will also have to click "Install a new app" if your GitHub repository is owned by another organization and your GitHub user was just invited to that organization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fljyfvbls95lnnxquib8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fljyfvbls95lnnxquib8f.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you have chosen the correct GitHub App, click "Connect"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[5]&lt;/strong&gt; With that, the pop-up window closes and you are redirected back to the "Add source stage" screen. Choose your repository and branch name. Leave the rest as default and then, click "Next"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Frrb63ja5addiq90si4up.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Frrb63ja5addiq90si4up.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[6]&lt;/strong&gt; In the build provider, choose "AWS CodeBuild". If you haven't created your CodeBuild Project yet, hit "Create Project"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fltwr6162z19fryxtw1ye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fltwr6162z19fryxtw1ye.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another pop-up screen appears for us to create our CodeBuild project. In this screen, add your project name:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fa84tpkt6tyj8gybkw5gv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fa84tpkt6tyj8gybkw5gv.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll a bit down for the environment section.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environment image: Managed image&lt;/li&gt;
&lt;li&gt;Operation System: Ubuntu&lt;/li&gt;
&lt;li&gt;Runtime: Standard&lt;/li&gt;
&lt;li&gt;Image: aws/codebuild/standard:5.0 (or whatever the latest version is)&lt;/li&gt;
&lt;li&gt;Image version: Always use the latest image for this runtime version&lt;/li&gt;
&lt;li&gt;Environment type: Linux&lt;/li&gt;
&lt;li&gt;Privilged: Enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fcc8tkctpnwoh4c5h1uxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fcc8tkctpnwoh4c5h1uxx.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the previous step, we connected GitHub to our CI/CD pipeline. In this step, we created a CodeBuild project that runs a series of commands to build our application. These commands are listed under the buildspec.yml file. Here is our project's buildspec.yml file&lt;/p&gt;

&lt;p&gt;In our project's buildspec.yml, we only used 2 phases: install and build. Phases allow us to group our build commands. For our install phase, we installed serverless framework and pip. Then, for the build phase, we ran &lt;code&gt;serverless deploy&lt;/code&gt; to deploy our application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;

&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runtime-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;python&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.8&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ls -a ~&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install -g serverless&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl -O https://bootstrap.pypa.io/get-pip.py&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ls -a ~&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3 get-pip.py --user&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip --version&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;serverless deploy --region $AWS_REGION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leave the rest as default and click "Continue to CodePipeline". This creates the CodeBuild project. You will then be redirected back to the CodePipeline screen, with a pre-filled form. Double-check the values. Once satisfied, click next:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3rrjub4xh5mc1jtz30h6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3rrjub4xh5mc1jtz30h6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[7]&lt;/strong&gt; You will be redirected to the deploy stage. We don't need this for now. With the &lt;code&gt;serverless deploy&lt;/code&gt; command in CodeBuild, we are deploying the application already. Click "Skip Deploy Stage". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdtoafrid4l5mbbxkb6pe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdtoafrid4l5mbbxkb6pe.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, we will be on the Review screen. Review the values and press "Create pipeline". Right after pipeline creation, it executes immediately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fgwrqh5ns1ku6y688303f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fgwrqh5ns1ku6y688303f.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Source stage executes immediately and fetches code from GitHub. If this stage fails the first time, just click retry. Sometimes the first run malfunctions.&lt;/p&gt;

&lt;p&gt;Once the source stage is done, the Build stage triggers. In this stage, CodeBuild executes the instructions coded inside your buildspec.yml. If you are curious as to what is happening while CodeBuild is executing, click the "details" and you will see a log of the deployment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fy7uvzz9ofnx8cty0g2oa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fy7uvzz9ofnx8cty0g2oa.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will take a few minutes before the build completes. Wait for a few minutes and come back to it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Let's troubleshoot the deployment
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;[8]&lt;/strong&gt; From the result of our first run, it looks like the execution failed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fn6cdp1t5n1e0gfqz872e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fn6cdp1t5n1e0gfqz872e.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To figure out what's wrong, scroll down and find the last few lines of the execution. You shall see the error there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftl02etyte21awdllpmwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftl02etyte21awdllpmwu.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks like our Build execution failed when we tried to deploy our serverless app using the &lt;code&gt;serverless deploy&lt;/code&gt; command. The error is the role that CodeBuild uses is "not authorized to perform" operations in CloudFormation. Since Serverless Frameworks apps are just CloudFormation deployments under the hood, we need to equip our CodeBuild with an IAM role that has permission to access CloudFormation and other needed resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[9]&lt;/strong&gt; Let's update the permission of our CodeBuild role. First, let's go to the Build Project that powers our execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fu8tzto71qa0yd85yabl7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fu8tzto71qa0yd85yabl7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, under Edit, click Environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F20uzekzj00ju3wdfprgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F20uzekzj00ju3wdfprgy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will see this screen. Copy the ARN of the Service role:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fn5t0wd5sp22mfyryejrh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fn5t0wd5sp22mfyryejrh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, go to the AWS IAM console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fprzepa2qdz2vl0zmu4ya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fprzepa2qdz2vl0zmu4ya.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click "Roles" on the left-hand side menu and search for the role&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3e6yq5zeq9xa9oo6hqgb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3e6yq5zeq9xa9oo6hqgb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under Permissions, click Attach policies. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fauaprkgg88qgtpaxceel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fauaprkgg88qgtpaxceel.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Search for the following AWS-managed policies and attach them to the role. In production, be more deliberate and granular about the permissions we grant to this role. For our exercise, this will do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AmazonS3FullAccess&lt;/li&gt;
&lt;li&gt;AWSCloudFormationFullAccess&lt;/li&gt;
&lt;li&gt;AmazonDynamoDBFullAccess&lt;/li&gt;
&lt;li&gt;AmazonAPIGatewayAdministrator&lt;/li&gt;
&lt;li&gt;CloudWatchLogsFullAccess&lt;/li&gt;
&lt;li&gt;IAMFullAccess&lt;/li&gt;
&lt;li&gt;AWSLambda_FullAccess&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Famo84nuzpn0ymacpo3x7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Famo84nuzpn0ymacpo3x7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we're good. Let's run the CodePipeline again. Find the pipeline you created earlier and click "Release Change"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F599mxt16b6xwtj7p0p1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F599mxt16b6xwtj7p0p1m.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On this run, our application is now deployed!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fh1pdj6f24laupgx7ki58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fh1pdj6f24laupgx7ki58.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's see it in action on our Postman collection:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fo1qx9ub46btsihooghms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fo1qx9ub46btsihooghms.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's try to modify the response body just a bit by adding "status": "success". Then, we commit the change to master and our CI/CD should deploy our change:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fx7t8c2vf1fqd5rk4gv33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fx7t8c2vf1fqd5rk4gv33.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our CI/CD pipeline should automatically run. After a few minutes, our change should now reflect in our API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F32c5bc8djenlhky57p93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F32c5bc8djenlhky57p93.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  That's all folks!
&lt;/h2&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@jjying?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;JJ Ying&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/pipeline?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cicd</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Field Guide to Surviving DDoS Attacks in your application</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Wed, 09 Nov 2022 02:50:05 +0000</pubDate>
      <link>https://dev.to/awscommunity-asean/devs-guide-to-surviving-ddos-attacks-in-your-api-56ke</link>
      <guid>https://dev.to/awscommunity-asean/devs-guide-to-surviving-ddos-attacks-in-your-api-56ke</guid>
      <description>&lt;p&gt;One of our customer-facing websites got attacked with a huge DDoS attack recently, to the magnitude of 80M-100M requests per hour. For context, our website usually just receives 30,000 requests per minute. That's a 3000x increase in traffic. Luckily, we were able to respond promptly and we stopped the DDoS attack within 3 days. But those 3 days were the most stressful times of my year (so far). &lt;/p&gt;

&lt;p&gt;I created this field guide for developers so I can save all of you the firefighting and research I had to do on the fly during those 3 days. While this is by no means a comprehensive guide, it will give you the tools necessary to respond to a DDoS attack.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's a DDoS attack?
&lt;/h2&gt;

&lt;p&gt;At its core, DDoS attacks are denial-of-service attacks. They aim to overwhelm your servers with so much fake traffic that your legitimate end-users won't be able to access your application. This results in downtime, and with your website down, your customer won't be able to buy anything, and your revenue grounds to a halt.&lt;/p&gt;

&lt;p&gt;There are 3 types of DDoS attacks: Application Layer, Protocol Attacks, and Volumetric attacks. We will focus on application layer attacks for this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Architecture
&lt;/h2&gt;

&lt;p&gt;Let's start with this typical network architecture. We have an eCommerce website called &lt;code&gt;jambyswags.com&lt;/code&gt;. Both its PHP backend and NuxtJS frontend applications are hosted in EC2. Once the customer access the website, the request is routed to a load balancer that distributes traffic between 2 EC2 instances. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fx39xqgyurgzxshg8zea0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fx39xqgyurgzxshg8zea0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm if you are being attacked
&lt;/h2&gt;

&lt;p&gt;A DDoS attack usually starts with your website becoming unavailable to all of your users. First, check the CPU utilization of the EC2 instances of your backend and frontend applications. If your EC2's CPU utilization is overloaded, then this is a sign it's a DDoS attack. &lt;/p&gt;

&lt;p&gt;The next step is to go to your ALB's CloudWatch Metrics to check your application's request count. In our case, our website typically takes in 500 requests per minute. Then, we suddenly experienced 1M - 1.5M requests per min. That's a 3000x increase from our baseline, a big sign of a DDoS attack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F9stayezrie1hsnnya5w5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F9stayezrie1hsnnya5w5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final step is to check if the traffic is legitimate. You don't want to stop your legitimate users from going bananas shopping on your website, especially during a sale. One way to do this is to enable VPC Flow Logs for the ENI of the load balancer. From here, you can see the IP addresses connecting to your application. If it's all from the same set of IP addresses, that's credible proof that you are experiencing DDoS Attack. &lt;/p&gt;

&lt;p&gt;Another indicator is if you're not having a promo or major ad push for that day but are experiencing a big jump in traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  First Response: AWS WAF
&lt;/h2&gt;

&lt;p&gt;Now that you are sure you have a DDoS attack on your hands, it's time to bring the big guns. As a first response, it is essential to add AWS WAF behind your load balancers. With AWS WAF, we create a web ACL that contains rule groups. Rule groups can be managed by AWS or customized by you. It can also be a regular rule group, which checks requests based on their contents, or a rate-based rule group, which sets a cap on the requests per minute coming in from each IP address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F1dd4z1qqf3tduii6y9lc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F1dd4z1qqf3tduii6y9lc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before the request goes to your ALB, the web ACL scans the requests based on the rule groups you add to it. For web applications, we recommend adding the following rule groups: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS-AWSManagedRulesAdminProtectionRuleSet&lt;/code&gt; - checks your requests for paths that may be trying to get access to your admin pages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS-AWSManagedRulesAmazonIpReputationList&lt;/code&gt; and &lt;code&gt;AWS-AWSManagedRulesAnonymousIpList&lt;/code&gt; - checks your requests if it comes from suspicious IP addresses&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS-AWSManagedRulesCommonRuleSet&lt;/code&gt; - checks common exploits found in the OWASP 10&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AWS-AWSManagedRulesKnownBadInputsRuleSet&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS-AWSManagedRulesSQLiRuleSet&lt;/code&gt; - checks against SQL injection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These rules check against suspicious IP addresses and potentially malicious request bodies. Meanwhile, the Bot Control is a rule that should be last in your web ACL. It has an additional cost of 1USD per million but protects your application from malicious bots trying to access your application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS-AWSManagedRulesBotControlRuleSet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another thing you can do is add a rate-based rule. In this example, you can block IP addresses if the rate at which they access the site exceeds 500 requests per minute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fvqb608sohvn3pjxq4ysb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fvqb608sohvn3pjxq4ysb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The caveat with AWS WAF is it can potentially break your application. For instance, the CommonRuleSet has a rule against request bodies exceeding a certain size. If your API was uploading 7MB files, it's probably going to hit this rule and your end users won't be able to upload files. The key here is to test it out first in staging to identify the rules that break your application. Then, you can adjust those rules from "BLOCKED" to "COUNT"&lt;/p&gt;

&lt;h2&gt;
  
  
  First Response: Route 53 Geolocation
&lt;/h2&gt;

&lt;p&gt;A quick and dirty way to limit DDoS attacks is to limit who can resolve your website's domain name (i.e jambyswags.com) to the IP address of your load balancer. In our case, our customer's website engages audiences only in Singapore and the Philippines. And the DDoS attack featured servers from Asia, Europe, Canada, S.America, and Africa. With this, it makes sense to only make our website accessible only to people from Asia. This effectively stopped all attacks from non-Asian countries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5rhfwhgcd1ehsc3lx84o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5rhfwhgcd1ehsc3lx84o.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While Asia is still a big audience, the attacker now becomes limited in what servers he/she can use. It won't stop the attack, but it can help reduce its volume.&lt;/p&gt;

&lt;p&gt;To make it even more effective, try shifting your API to a different domain name to force your attackers to use the DNS resolver. For example, move your backend from "jambyswags.com" to "prod.backend.jambyswags.com"&lt;/p&gt;

&lt;h2&gt;
  
  
  First Response: File Abuse Teams
&lt;/h2&gt;

&lt;p&gt;With the two actions above, your applications are more protected against DDoS attacks. However, you may see your AWS WAF costs spike. As of writing, AWS WAF charges 0.60USD per million requests. In our case, we were attacked with 90 million requests per hour over 55 hours. That's 4.95 billion requests, or 2,970USD. That's still a huge price to pay for protection. &lt;/p&gt;

&lt;p&gt;One way to permanently stop these attacks is to those IP addresses to the Abuse Team of AWS, GCP, and Azure. This process usually takes a few days, but the sooner you get the ticket filed, the sooner it can process. To get those abuser's set of IP addresses, you can browse the Sampled Request section of your AWS WAF.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6l8yj9w2wo4nejp0ihhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6l8yj9w2wo4nejp0ihhp.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will also have to provide application logs that these IP addresses are disrupting your website&lt;/p&gt;

&lt;h2&gt;
  
  
  Second Response: Reduce Surface Area
&lt;/h2&gt;

&lt;p&gt;Your application is protected, for now. While the intruder can't brute force your website anymore because of the rate limits of AWS WAF, they can still try to penetrate your application. One way to prevent this is by reducing the surface area exposed to attackers.&lt;/p&gt;

&lt;p&gt;First, move your application servers to the private subnet. With this, they can no longer be accessed directly, only through the load balancer. If you need to SSH to them, use AWS Sessions Manager via the AWS Console.&lt;/p&gt;

&lt;p&gt;Second, audit the security group of each resource inside your VPC. Make sure only the ALB exposes a port to the world (0.0.0.0/0). The rest of the services exposes only the port they need to expose to only the resource that needs to access them. For instance, the Backend EC2 should have port 80 open only for the ALB that forwards requests to it.&lt;/p&gt;

&lt;p&gt;Third, create a Database Private Subnet. It is a private subnet that has much less network access than the private subnet where your application is hosted in. Its NACL rules are also tighter in that it opens a few ports inbound and outbound.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ficbu28av3kbx1q7ywp72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ficbu28av3kbx1q7ywp72.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Second Response: Use private communications for API to API comms
&lt;/h2&gt;

&lt;p&gt;Sometimes one of your backend APIs will have to connect to another backend API in your network. Typically, your first backend API will send a request that traverses the open internet, back to your second backend's ALB, and to the second backend API. Aside from additional network costs you might incur, this link may be tagged as a DDoS attack by WAF, especially if this link is high volume.&lt;/p&gt;

&lt;p&gt;To solve this, create an internal ALB that resides in your application private subnet. With this, your first backend API sends a request to the internal ALB within the same network and the traffic doesn't have to traverse e the open internet. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ffg82ivv914qzm6hmbgs3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffg82ivv914qzm6hmbgs3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Third Response: Migrate FE to a Single Page Application
&lt;/h2&gt;

&lt;p&gt;In our case, our FE application was a NuxtJS application deployed to EC2 with the command &lt;code&gt;nuxt serve&lt;/code&gt; running. With minimal adjustments, we were able to able to generate a single-page application using the &lt;code&gt;npm generate&lt;/code&gt; command. We uploaded the generated dist folder to S3, hosted it as a static website, and connected it with CloudFront. &lt;/p&gt;

&lt;p&gt;Depending on how your FE application is written, this may not be as easy to do or even possible. Some FE applications are baked into the Backend API and require a full rewrite to become an SPA. Some FE applications are purposedly hosted as a Server Side Rendered (SSR) application and are hard to migrate to an S3-CloudFront setup.&lt;/p&gt;

&lt;p&gt;CloudFront offers additional DDoS protection as the first point of contact for your application is the edge location of CloudFront, and CloudFront has DDoS protection built in. It is also much cheaper to serve requests via CloudFront since it is cached nearer to the user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F4woptenok32m91yaw3au.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F4woptenok32m91yaw3au.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Third Response: No-Cache CloudFront for the BE
&lt;/h2&gt;

&lt;p&gt;With this technique, you will add a no-cache CloudFront distribution to your application. This way, your API's first point of contact will be an AWS edge location, and CloudFront can use its anti-DDoS features. Visit &lt;a href="https://aws.amazon.com/blogs/security/how-to-protect-dynamic-web-applications-against-ddos-attacks-by-using-amazon-cloudfront-and-amazon-route-53/" rel="noopener noreferrer"&gt;this AWS blog&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fnih7xlaxrdnqujlks684.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fnih7xlaxrdnqujlks684.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fourth Response: Consider AWS Shield Advanced
&lt;/h2&gt;

&lt;p&gt;If your DDoS attack is sophisticated, the tactics we mentioned above may not be enough. Here are some common work throughs they can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use thousands of IP Addresses from Asia to rain down hell on your application (and evade rate-based rules)&lt;/li&gt;
&lt;li&gt;Determine your application's most expensive API endpoints and target that with a low volume DDoS - For example, if your add-to-cart functionality takes 3s to load and is using SQL statements that take much effort from your DB to fulfill, they can get maybe 100 IP Address target that endpoint with 100 requests each. You'll be down in no time.&lt;/li&gt;
&lt;li&gt;Overwhelm your WAF and CloudFront with millions and tens of millions of requests per minute such that even if your website doesn't go down, you will burn through your AWS budget.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the first and third scenarios, you can count on AWS Shield Advanced. With this service, you will have a dedicated Shield Response Team who will proactively make changes to your AWS environment to protect your assets. You will also be not liable for any bill spike resulting from the provisioning of excess assets during a DDoS attack.&lt;/p&gt;

&lt;p&gt;The downside is that AWS Sheild Advanced is 3000 USD per month, with a 12-month commitment. Hence, this service makes more sense to avail if you are in an enterprise with deep pockets and a lot of assets to protect.  &lt;/p&gt;

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

&lt;p&gt;While I don't wish a DDoS attack happens to your applications, it's best to be prepared for this possibility. This article provided you with 4 layers of responses you can do to keep your website protected. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>devops</category>
      <category>ddos</category>
    </item>
    <item>
      <title>Use EventBridge for your event-driven architecture in 8 steps</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Sun, 25 Sep 2022 16:11:47 +0000</pubDate>
      <link>https://dev.to/awscommunity-asean/use-eventbridge-for-your-event-driven-architecture-in-10-steps-13dm</link>
      <guid>https://dev.to/awscommunity-asean/use-eventbridge-for-your-event-driven-architecture-in-10-steps-13dm</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was also presented as a talk at the recently concluded APAC Community Summit 2022 in Bangkok, Thailand&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Before you begin, I suggest you read my &lt;a href="https://dev.to/awscommunity-asean/how-we-embraced-eventbridge-for-our-event-driven-architecture-1jb3"&gt;introduction to EventBridge&lt;/a&gt;. &lt;/p&gt;

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

&lt;p&gt;In this demo, we will start with &lt;a href="https://github.com/jamby1100/eventbridge-demo-apac-summit/tree/baseline-two" rel="noopener noreferrer"&gt;a basic repository&lt;/a&gt; that has 3 microservices: orders, points, and notification. The order service marks an order as delivered. The points service award points based on a delivered order, while the notification service sends a simple "your order has been delivered" email. Our baseline repository looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdkg4sb1hlg1ui3whm401.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdkg4sb1hlg1ui3whm401.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the end of the post, we would use EventBridge so that the order service publishes an event once an order is marked as delivered. Then, the points and notifications receive that event. This triggers them to award points and send an email.&lt;/p&gt;

&lt;p&gt;Clone the &lt;a href="https://github.com/jamby1100/eventbridge-demo-apac-summit/tree/baseline-two" rel="noopener noreferrer"&gt;repository&lt;/a&gt; and then, run &lt;code&gt;git checkout baseline-two&lt;/code&gt;. There are 3 folders inside, representing each of the services. Set up each one by following the setup guide on the README.md of each service.&lt;/p&gt;

&lt;p&gt;If you want to know more about how I structured the 3 applications, you can check out my blog post that talks about &lt;a href="https://dev.to/aws-builders/why-we-need-another-web-framework-on-top-of-serverless-framework-5ha3"&gt;how we build apps on top of Serverless Framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the next section, let's test out all 5 endpoints by using the Postman collection at the root of the repository. It is ideal to have completed the full setup before proceeding to the next section.&lt;/p&gt;

&lt;h4&gt;
  
  
  Orders Service
&lt;/h4&gt;

&lt;p&gt;The order service has 2 endpoints. The first endpoint creates an order. It outputs an order_number that we use for the next endpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fuh0slksd4n15vh3sgjbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fuh0slksd4n15vh3sgjbw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second endpoint marks that order as delivered. We get the order_id generated in the first endpoint and place it as input here. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fwn8xfbvvjq71uedth3q8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fwn8xfbvvjq71uedth3q8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the response, we see the order successfully marked as delivered.&lt;/p&gt;

&lt;h4&gt;
  
  
  Points Service
&lt;/h4&gt;

&lt;p&gt;The points service also has 2 endpoints. The first endpoint awards points to a given user. If the user does not exist in the database yet, it creates an entry for it in the DB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fjzn3zyn3x3e0pye2v17q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fjzn3zyn3x3e0pye2v17q.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second endpoint displays the existing points of the user so we can verify if the points did increase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F76p7ukfldmdx67mj9xud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F76p7ukfldmdx67mj9xud.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Notification Service
&lt;/h4&gt;

&lt;p&gt;The notification service only has one endpoint. It takes a message and sends an email to an email address we have set up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3i7gezjgjob5i4n5sdhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3i7gezjgjob5i4n5sdhj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An email is then sent to our designated email:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F95o5pim99cj9ufmsxjr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F95o5pim99cj9ufmsxjr8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Objective
&lt;/h2&gt;

&lt;p&gt;When you clone the repository, you can deploy all 3 microservices and call the 5 API endpoints separately. But that's not what we are here for. We plan to connect the 3 microservices such that when we call the "Mark as Delivered" endpoint, it sends an "order_delivered" event to EventBridge.&lt;/p&gt;

&lt;p&gt;By the end of this post, we will just have to call the order service to mark the order as delivered. Then, via EventBridge, the user will be credited his points and receives a short email.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F4b6vd7fgue5a489v741f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F4b6vd7fgue5a489v741f.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1
&lt;/h2&gt;

&lt;p&gt;For the serverless.yml of all three services, add the additional permissions and environment variables as seen below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# modify: order-service/serverless.yml&lt;/span&gt;
&lt;span class="c1"&gt;# modify: notification-service/serverless.yml&lt;/span&gt;
&lt;span class="c1"&gt;# modify: points-service/serverless.yml&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.8&lt;/span&gt;
  &lt;span class="na"&gt;lambdaHashingVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20201221'&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${opt:stage, 'dev'}&lt;/span&gt;
  &lt;span class="na"&gt;iamRoleStatements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow"&lt;/span&gt;
      &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamodb:*"&lt;/span&gt;
      &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow"&lt;/span&gt;
      &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sns:*"&lt;/span&gt;
      &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;

    &lt;span class="c1"&gt;# ADD THIS STATEMENT&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow"&lt;/span&gt;
      &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events:*"&lt;/span&gt;
      &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ORDERS_TABLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:provider.stage}-ecom-orders-table&lt;/span&gt;

    &lt;span class="c1"&gt;# ADD THESE THREE ENV VARS&lt;/span&gt;
    &lt;span class="na"&gt;EVENT_SOURCE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ph.ecommerce.com&lt;/span&gt;
    &lt;span class="na"&gt;EVENT_BUS_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;${self:provider.stage}-ecom-event-bus&lt;/span&gt;
    &lt;span class="na"&gt;EVENT_BUS_ARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:events:${self:custom.region}:${self:custom.accountId}:event-bus/${self:provider.environment.EVENT_BUS_NAME}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2
&lt;/h2&gt;

&lt;p&gt;Specifically for the orders service, add the &lt;code&gt;EcommerceEventBus&lt;/code&gt; in the resources section. This is a CloudFormation code to programmatically create an EventBridge Event Bus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# modify: order-service/serverless.yml&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;EcommerceEventBus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Events::EventBus&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:provider.environment.EVENT_BUS_NAME}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3
&lt;/h2&gt;

&lt;p&gt;Now, we redeploy all 3 services. These should add the environment variables and additional permissions. It should also create the event bus.&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="nv"&gt;ACCOUNT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Account"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;order-service
serverless deploy &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-1 &lt;span class="nt"&gt;--stage&lt;/span&gt; dev &lt;span class="nt"&gt;--accountId&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_ID&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;points-service
serverless deploy &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-1 &lt;span class="nt"&gt;--stage&lt;/span&gt; dev &lt;span class="nt"&gt;--accountId&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_ID&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;notification-service
serverless deploy &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-1 &lt;span class="nt"&gt;--stage&lt;/span&gt; dev &lt;span class="nt"&gt;--accountId&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4
&lt;/h2&gt;

&lt;p&gt;Next, let's focus on the code of the orders service. Let's start by adding the EventbridgeGateway. It uses the boto3 library to send tasks to EventBridge. We placed it in its own gateway to abstract boto3 code from the rest of our application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# NEW FILE: order-service/gateways/eventbridge_gateway.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventbridgeGateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;events&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, let's create an &lt;code&gt;EventbridgeEvent&lt;/code&gt; model file that stores the business logic of building and sending events to EventBridge. It uses the EventbridgeGateway to send the event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# NEW FILE: order-service/models/eventbridge_event.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;gateways.eventbridge_gateway&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EventbridgeGateway&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;helpers.response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventbridgeEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;EVENT_BUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EVENT_BUS_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;SOURCE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EVENT_SOURCE_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_body&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_name&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_body&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;json_details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;construct_json_body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SOURCE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DetailType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json_details&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EventBusName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EVENT_BUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;event_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;EventbridgeGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, on the order model, let's add the &lt;code&gt;send_order_delivered_event()&lt;/code&gt; function that creates an EventBridgeEvent and sends it to the event bus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# UPDATE: order-service/models/order.py
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DynamodbModelBase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;DYNAMODB_TABLE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ORDERS_TABLE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# ADD THESE TWO FUNCTIONS
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serialize_for_eventbridge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order_number&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;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&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;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_order_delivered_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EventbridgeEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order_delivered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize_for_eventbridge&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Finally, let's call the &lt;code&gt;send_order_delivered_event()&lt;/code&gt; in our handler.&lt;/p&gt;

&lt;p&gt;As you can see in the code, an order object is instantiated, marked as delivered, saved to DB, and then, we call the &lt;code&gt;send_order_delivered_event()&lt;/code&gt; to send an event to EventBridge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# UPDATE: order-service/handlers/mark_order_as_delivered.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;

        &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Order&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="n"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_as_delivered&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# ADD THIS LINE
&lt;/span&gt;        &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_order_delivered_event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5
&lt;/h2&gt;

&lt;p&gt;Now, let's shift our attention to the points service. Let's add a new Lambda function whose event source is our Event Bus. Notice how we added a filter where we only want to receive "order_delivered" events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# UPDATE: points-service/serverless.yml&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;awardPointsSix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handlers/award_points.handler&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;eventBridge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;eventBus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:provider.environment.EVENT_BUS_ARN}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${self:provider.environment.EVENT_SOURCE_NAME}&lt;/span&gt;
            &lt;span class="na"&gt;detail-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;order_delivered&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the case you want to change the eventBridge parameters we defined above (or you made a mistake somewhere), you have to change the name of the function. It's my sixth attempt at getting it right, hence my function's name is &lt;code&gt;awardPointsSix&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the handler for our award points endpoint, let's add an if statement so we can accommodate both calls from the API endpoint and EventBridge. Notice the difference in how we extract the payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# UPDATE: points-service/handlers/award_points.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AND THE EVENT IS NOWWWW!!!!&amp;lt;3 &amp;lt;3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="n"&gt;primary_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

            &lt;span class="n"&gt;primary_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pathParameters&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;user_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_if_not_exist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;award_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6
&lt;/h2&gt;

&lt;p&gt;Going to the notification service, let's add another Lambda function that receives an event from EventBridge. Notice how it's similar to the original API-based Lambda function, sendEmailNotification, except for the eventBridge part.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# UPDATE: notification-service/serverless.yml&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sendEmailNotificationSix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handlers/send_email_notification.handler&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;eventBridge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;eventBus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:provider.environment.EVENT_BUS_ARN}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${self:provider.environment.EVENT_SOURCE_NAME}&lt;/span&gt;
            &lt;span class="na"&gt;detail-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;order_delivered&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like with the award points API, let's add this &lt;code&gt;if&lt;/code&gt; statement to accommodate both EventBridge event and API calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# UPDATE: notification-service/handlers/send_email_notification.py
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="n"&gt;primary_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

            &lt;span class="n"&gt;primary_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pathParameters&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;user_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;topic_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SNS_TOPIC_ARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;SnsNotificationGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Order marked as delivered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;topic_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;topic_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ap-southeast-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7
&lt;/h2&gt;

&lt;p&gt;With all that code change, let's deploy once again!&lt;/p&gt;

&lt;p&gt;You can also opt to do local testing first via &lt;code&gt;serverless offline&lt;/code&gt;. Learn more about that in this &lt;a href="https://dev.to/awscommunity-asean/challenge-3-using-offline-tools-to-speed-up-dev-in-serverless-2hp8"&gt;blog post&lt;/a&gt;&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="nv"&gt;ACCOUNT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Account"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;order-service
serverless deploy &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-1 &lt;span class="nt"&gt;--stage&lt;/span&gt; dev &lt;span class="nt"&gt;--accountId&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_ID&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;points-service
serverless deploy &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-1 &lt;span class="nt"&gt;--stage&lt;/span&gt; dev &lt;span class="nt"&gt;--accountId&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_ID&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;notification-service
serverless deploy &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-1 &lt;span class="nt"&gt;--stage&lt;/span&gt; dev &lt;span class="nt"&gt;--accountId&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 8
&lt;/h2&gt;

&lt;p&gt;Now, let's test it out. Check first how many points the user has by calling the "Check User Points" API Endpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fazmz49u2qv2wt54uouzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fazmz49u2qv2wt54uouzm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, create an order and mark it as delivered. When you call the "Check User Points" API endpoint again, the points should have increased on their own:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fqmti7jpagz6b4z4zy3zs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fqmti7jpagz6b4z4zy3zs.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, you should have received an email.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F95o5pim99cj9ufmsxjr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F95o5pim99cj9ufmsxjr8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it!
&lt;/h2&gt;

&lt;p&gt;You have used EventBridge to connect your event-driven serverless architecture. In the next post, we'll tackle the problems we encountered while using EventBridge in production and how you can address them. Stay tuned!&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@kellysikkema?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Kelly Sikkema&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/hands-on?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>eventbride</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>How we embraced Amazon EventBridge for our event-driven architecture</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Thu, 15 Sep 2022 00:33:05 +0000</pubDate>
      <link>https://dev.to/awscommunity-asean/how-we-embraced-eventbridge-for-our-event-driven-architecture-1jb3</link>
      <guid>https://dev.to/awscommunity-asean/how-we-embraced-eventbridge-for-our-event-driven-architecture-1jb3</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was also presented as a talk at the recently concluded APAC Community Summit 2022 in Bangkok, Thailand&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last year, our team embraced microservices architecture. Instead of one big monolithic application, we developed a dozen small microservices. This setup allowed us to push out features faster and keep each microservice simple and maintainable. &lt;/p&gt;

&lt;p&gt;As our application grew, our events started to have actions that spanned different services. It became a distributed nightmare just to mark an order as delivered. We had to make sure actions in the payments, referral, points, and notification services were executed successfully.&lt;/p&gt;

&lt;p&gt;In this post, I'll introduce EventBridge and how it simplified the coordinating of multiple microservices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monolith Apps get slower over time
&lt;/h2&gt;

&lt;p&gt;Let's start by discussing how we would typically do this in a traditional monolithic application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F61hz9ofgrwse3usy7z2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F61hz9ofgrwse3usy7z2o.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A monolith responds to this event by executing the series of actions sequentially. As an example, an "order_delivered" event may have the following actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mark the order as "delivered"&lt;/li&gt;
&lt;li&gt;Collect Payment&lt;/li&gt;
&lt;li&gt;Send the "your order has been delivered" email&lt;/li&gt;
&lt;li&gt;Award the rewards points&lt;/li&gt;
&lt;li&gt;Check the order if it had a referral code if it did award additional points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The monolith does this synchronously and makes the user wait until all the steps are finished. As we add more actions to this event, the waiting time gets longer.&lt;/p&gt;

&lt;p&gt;Because monoliths tightly couples modules together, a simple change in one module becomes more complex than it should be. Unintended consequences in a seemingly unrelated module pop up occasionally. A full regression test is usually run to ensure against this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Microservices are smaller, simpler, and easier to make changes to
&lt;/h2&gt;

&lt;p&gt;For these reasons, we decided to move to an event-driven serverless architecture. Instead of one big application housing all modules, we broke down our application into a dozen microservices, each in charge of a specific group of functionality:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F41xkm25nshwwobipkx15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F41xkm25nshwwobipkx15.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the order_delivered event, its actions are owned by 5 different microservices. In our team, we assign 2-3 microservices to each developer. As system owners, they create features and troubleshoot bugs.&lt;/p&gt;

&lt;p&gt;Since the actions required by the "order_delivered" event is delegated across five microservices, fulfilling this event requires us to make the microservices communicate with one another.&lt;/p&gt;

&lt;p&gt;The simplest way to connect them is via API. But that would end up being even slower than the monolithic approach because of the latency caused by doing five API calls. &lt;/p&gt;

&lt;h2&gt;
  
  
  The shortcomings of using SQS to decouple the architecture
&lt;/h2&gt;

&lt;p&gt;The better way would be for the order service to execute its component actions asynchronously. First, it receives the request. Then, it sends a task to the SQS queue of the payment service. On the receiving end, the payment service will get the task on the SQS queue and run the action to collect the payment. The order service then sends a task to the SQS queues of the points, referral, and notification services. Each service processes the action by getting the task off the queue. Then, it returns a response to the customer. &lt;/p&gt;

&lt;p&gt;In this process, the response was sent only when all 4 actions have been queued in their respective SQS queues - not when all 4 actions have succeeded. That is asynchronous processing in action. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8djh9bgrwirxirqpkl13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8djh9bgrwirxirqpkl13.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SQS queues are many-to-1. It can have many producers but it only sends to one homogenous group of consumers. This easily becomes a problem with our setup because this means we have to build a new SQS queue for each group of consumers. For instance, we added a new action in our event that sends a recommendation of items based on the delivered order. This action is housed in the recommendation service. And as it is a new action, another SQS queue is required.&lt;/p&gt;

&lt;p&gt;As our app gets more complex over time, we add more actions to our events. And more actions mean more SQS queues being added. This adds to the overhead of maintaining our system over time.&lt;/p&gt;

&lt;p&gt;A reader has pointed o&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter EventBridge
&lt;/h2&gt;

&lt;p&gt;EventBridge addresses this pain point while still retaining the decoupling that SQS introduced. Instead of sending 4 different tasks to 4 different SQS queues, it just publishes the "order_delivered" event to an event bus in EventBridge.&lt;/p&gt;

&lt;p&gt;An event bus is like an SQS queue but instead of being many-to-1, it is many-to-many. This means many different event producers can push events to an event bus, and the event bus sends the event to all consumer systems configured to listen to it. &lt;/p&gt;

&lt;p&gt;Aside from that, it allows consumers to listen only to specific events and disregard other events. When we configure consumers to "listen" for events, we do so by creating event rules against the event bus. These rules help the consumer filter what events it receives from the event bus. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdnuc1uc98kzzzbgffw1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdnuc1uc98kzzzbgffw1x.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, the payment service only listens for the "order_delivered" event. When an event for the "order_shipped" or "order_confirmed" happens, it is not sent to that service. &lt;/p&gt;

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

&lt;p&gt;EventBridge allows microservices to communicate with one another in a decoupled manner. We only need one event bus for all our events. Hence, managing the actions of our events across services becomes simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;In the next post, we will create a simple demo to demonstrate the powers of EventBridge. &lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@dnevozhai?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Denys Nevozhai&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/interstate?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>ElasticSearch: Zero to Hero in 12 Commands</title>
      <dc:creator>Raphael Jambalos</dc:creator>
      <pubDate>Sat, 27 Aug 2022 04:23:00 +0000</pubDate>
      <link>https://dev.to/awscommunity-asean/the-guided-elasticsearch-cheatsheet-you-need-to-get-started-with-es-54f5</link>
      <guid>https://dev.to/awscommunity-asean/the-guided-elasticsearch-cheatsheet-you-need-to-get-started-with-es-54f5</guid>
      <description>&lt;p&gt;It's relatively easy to get started with ElasticSearch. But as our use cases get more specific, we found the documentation lacking. This guided cheatsheet will execute 12 commands: from setting up your ES index to making advanced ES queries to support advanced (but common) use cases.&lt;/p&gt;

&lt;p&gt;The 12 commands works when done sequentially. I will explain each of them, but trying them for yourself is still the best.&lt;/p&gt;

&lt;p&gt;This post is part of a broader series on ElasticSearch that will be released in the coming weeks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Guided ElasticSearch Cheatsheet you need to Get Started with ES - &lt;strong&gt;you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Using DynamoDB + ElasticSearch for prod workloads - coming soon&lt;/li&gt;
&lt;li&gt;And how to create DynamoDB Streams to sync data changes from DynamoDB to ES asynchronously - coming soon&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  0 | Prerequisites
&lt;/h2&gt;

&lt;p&gt;Install Elasticsearch with &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html" rel="noopener noreferrer"&gt;this official ES Guide&lt;/a&gt;. And then, &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/starting-elasticsearch.html" rel="noopener noreferrer"&gt;turn on the ES server&lt;/a&gt; on localhost:9200&lt;/p&gt;

&lt;p&gt;For easier testing, installing an API platform like &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; is a must. &lt;/p&gt;

&lt;h2&gt;
  
  
  A | Setup the index
&lt;/h2&gt;

&lt;p&gt;In ElasticSearch, we store our data in indexes (similar to tables in your MySQL database). We populate indexes with documents (similar to rows). We will create and set up your first index in the subsequent commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  [1] Verify the ES cluster is accessible
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;First, make sure your local ES server is online, and you have your Postman open. Create a new GET request headed for localhost:9200. You should see something like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F9hg3xs2bm583fsky4so7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F9hg3xs2bm583fsky4so7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  [2] Create an index
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;PUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Now, let's create our first index. Indexes store our data. It is equivalent to creating a table in relational databases. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8hiu1qytcpujytqoq3gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8hiu1qytcpujytqoq3gu.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  [3] Create the mapping for the index
&lt;/h3&gt;

&lt;p&gt;The index we just created has no mapping. A mapping is similar to a schema in SQL databases. It dictates the form of the documents that our index will ingest. Once defined, the index will refuse to accept documents that cannot fit into this mapping (i.e, we defined stocks as integer below. If we try to insert a row with stocks="none", the operation will not continue).&lt;/p&gt;

&lt;p&gt;One thing you'd notice with ES is that these mappings are permissive by default. If I add a row with a new attribute "perishable" = true, when I push a document to ES, the schema will add that attribute and infer its data type. In this case, it will add a new attribute in the mapping for "perishable" with data type "boolean".&lt;/p&gt;

&lt;p&gt;There are options that you can add when you create your index to only allow attributes defined in mapping of your index, nothing more, nothing less.&lt;/p&gt;

&lt;p&gt;In this command, we create the mapping for our newly created index. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;PUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_mapping&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;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"product_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&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;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"float"&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;"stocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="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;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&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;"sortable_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&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;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&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;&lt;a href="https://media.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%2Fr56bvnn1yk8jfvc2bcuz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fr56bvnn1yk8jfvc2bcuz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of the data types are straightforward, except for Text and Keyword. This &lt;a href="https://codecurated.com/blog/elasticsearch-text-vs-keyword/" rel="noopener noreferrer"&gt;article&lt;/a&gt; explains the difference clearly. &lt;/p&gt;

&lt;p&gt;But TLDR, Text allows you to query words inside the field (i.e querying "Burger" will show the product "Cheese Burger with Fries"). It does this by treating each word in the text as individual tokens that could be searched: "cheese", "burger", "with", "fries".&lt;/p&gt;

&lt;p&gt;On the other hand, Keyword treats the content of the field as one, so if you want to get the cheeseburger with fries, you'd have to query it: "Cheese Burger with Fries". Querying "burger" will return nothing. &lt;/p&gt;

&lt;h3&gt;
  
  
  [4] Show the mapping of the index
&lt;/h3&gt;

&lt;p&gt;Let's verify if we have successfully created the mapping for the index by sending a GET request.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fa160bvf2n5jflbastb5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fa160bvf2n5jflbastb5a.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  B | Data Operations with our ES Index
&lt;/h2&gt;

&lt;p&gt;With our index already set up, let's add data and chip away at the more exciting bits of ES!&lt;/p&gt;

&lt;h3&gt;
  
  
  [5] Create data for the index
&lt;/h3&gt;

&lt;p&gt;For this section, let's send three consecutive post requests with different a request body per request. This adds 3 "rows" inside our Elasticsearch index. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_doc&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;"product_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;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;99.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sortable_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kenny Rogers Chicken Sauce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kenny Rogers Chicken Sauce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chicken sauce poultry cooked party"&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;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_doc&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;"product_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;"456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;200.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sortable_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Best Selling Beer Flavor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Best Selling Beer Flavor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"beer best-seller party"&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;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_doc&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;"product_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;"789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;350.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sortable_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Female Lotion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Female Lotion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lotion female"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fiif7kcwyplwu8qt9srhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fiif7kcwyplwu8qt9srhb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  [6] Display all the data
&lt;/h3&gt;

&lt;p&gt;Now, let's see if the three documents we inserted via command #5 got inside our index. This command shows all the documents inside your index:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_search&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;"query"&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;"match_all"&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;It does!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fbe1sch8jqbyq7nd1y2ql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fbe1sch8jqbyq7nd1y2ql.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  [7] Exact search with product id
&lt;/h3&gt;

&lt;p&gt;Now, let's start with a simple search. Let's search by product id.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_search&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;"query"&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;"term"&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;"product_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;"456"&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;In the command above, we are using a "term query" because we are looking for a product with a "product_id" &lt;em&gt;that exactly matches&lt;/em&gt; the string "456". The term query works because the data type of "product_id" is "keyword". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpi60y9w5zrp2rayc6co5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpi60y9w5zrp2rayc6co5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  [8] Fuzzy search with titles
&lt;/h3&gt;

&lt;p&gt;Now, onto the more exciting bits. &lt;/p&gt;

&lt;p&gt;ES is known for its comprehensive search capability. Let's sample that by creating our first Fuzzy search. Fuzzy searches allow us to search for products by typing just a few words instead of the whole text of the field. Instead of typing the full name of the product name (i.e Incredible Tuna Mayo Jumbo 250), the customer just instead has to search for the part he recalls of the product (i.e Tuna Mayo).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_search&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;"query"&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;"match"&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Beer Flavor"&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;In the default setting, we can get the product "Best Selling Beer Flavor" even with our incomplete query "Beer Flavor". There are other settings that allow us to tolerate misspellings or incomplete words to show results (i.e Bee Flavo)&lt;/p&gt;

&lt;p&gt;Also, notice carefully that we now use a "match query" instead of a "term query" because we want to be able to get results even if we didn't type the full product name. The match query works because the title field is of type "text".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fcc1y2531neod9auia8b5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fcc1y2531neod9auia8b5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  [9] Sorted by prices
&lt;/h3&gt;

&lt;p&gt;Another thing we usually have to do with an e-commerce website is to sort products by specific categories like price or rating:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_search&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;"query"&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;"match_all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sort"&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="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"desc"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"_score"&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;With our query above, we return all the products sorted by most expensive to the cheapest. Notice that the sort parameter is a list, which allows us to add multiple criteria for sorting. We also added "_score", which is an elasticsearch keyword for search relevance. We will explore this concept deeper on later examples.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7j4fidhwlzwzj85gb0x2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7j4fidhwlzwzj85gb0x2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  [10] Search for all "beer" products that are PUBLISHED, and in stock. Sorted by cheapest to most expensive
&lt;/h3&gt;

&lt;p&gt;To make things more interesting, let's add several more beer products. We do this by sending a POST request thrice, with a different request body each time.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_doc&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;"product_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;"111"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;350.55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sortable_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tudor Beer Lights"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tudor Beer Lights"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"beer tudor party"&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;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_doc&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;"product_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;"222"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;700.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sortable_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stella Beer 6pack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stella Beer 6pack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"beer stella party"&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;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;9200&lt;/span&gt;&lt;span class="err"&gt;/mynewindex/_doc&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;"product_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;"333"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;340&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sortable_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kampai Beer 6pack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kampai Beer 6pack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"beer kampai party"&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;With more documents in our index, we can now do the query. This is a complex query that has three conditions that must be fulfilled. We analyze the query below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"query"&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;"bool"&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;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"match"&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Beer"&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="nl"&gt;"term"&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;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="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="nl"&gt;"range"&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;"stocks"&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;"gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"sort"&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="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asc"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"_score"&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;With our recent additions, there are four products with the word beer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;456: Best Selling Beer Flavor&lt;/li&gt;
&lt;li&gt;111: Tudor Beer Lights&lt;/li&gt;
&lt;li&gt;222: Stella Beer 6pack&lt;/li&gt;
&lt;li&gt;333: Kampai Beer 6pack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we filter out items whose inventory is zero (or below), we remove product 456 from the list. Another filter is that the product must be published (published = true). With this filter, product 222 is removed. We are left with the 2 products below. They must be sorted by cheapest to most expensive, as is shown below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;333: Kampai Beer 6pack (price = 340)&lt;/li&gt;
&lt;li&gt;111: Tudor Beer Lights (price = 350.55)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fnll265egfqec8opp708z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fnll265egfqec8opp708z.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, the key "must" was used, with a list as its value. The list contains conditions that must be met together for the query requirements to be met. In this example, its "title must have the word 'beer'" AND "published attribute is equal to true" AND "stocks is greater than zero". &lt;/p&gt;

&lt;h3&gt;
  
  
  [11] Search for all products that have at least 1 of the following tags ['poultry, 'kampai', 'best-seller'], that are PUBLISHED, and in stock. Sorted by cheapest to most expensive
&lt;/h3&gt;

&lt;p&gt;Our previous query just involved three conditions that must be ALL TRUE to hold. That's equivalent to "A and B and C". &lt;/p&gt;

&lt;p&gt;In this query, we still have three conditions that have to be all true, but the 1st condition is marked as true if it has either "poultry", "kampai", or "best-seller".In this example, we introduce the syntax for "OR":&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"query"&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;"bool"&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;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&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;"should"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="nl"&gt;"match"&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;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"poultry"&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="nl"&gt;"match"&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;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kampai"&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="nl"&gt;"match"&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;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"best-seller"&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="nl"&gt;"minimum_should_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"term"&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;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="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="nl"&gt;"range"&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;"stocks"&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;"gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asc"&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="s2"&gt;"_score"&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;In this query, we still have a "must" keyword, but its first contains a "should" keyword. The whole query is equivalent to: (A or B or C) AND D AND E. The "should" implies that as long as one condition is met, the (A or B or C) statement returns true.&lt;/p&gt;

&lt;p&gt;A tweak we can do is adjust the "minimum_should_match" (msm) parameter, so we can require that two or three or N conditions be met for the statement to be true. In our example, if msm=2, it means a product has to have two matching tags to be considered true (i.e a product has to be both poultry and kampai). &lt;/p&gt;

&lt;p&gt;We analyze the query below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The product &lt;strong&gt;should&lt;/strong&gt; have at least 1 of these tags: poultry, kampai, best-seller

&lt;ul&gt;
&lt;li&gt;This matches 3 products: poultry (pid: 123), kampai (pid: 333) and best-seller (pid: 456)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;That is published

&lt;ul&gt;
&lt;li&gt;All 3 PIDs from the previous step are already published. So no changes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Should have stocks

&lt;ul&gt;
&lt;li&gt;Since pid 456 does not have stocks, we are left with pid 123 and pid 333&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Sorted by price

&lt;ul&gt;
&lt;li&gt;pid 333 is 340pesos&lt;/li&gt;
&lt;li&gt;pid 123 is 99.75pesos&lt;/li&gt;
&lt;li&gt;therefore, the order should be pid 123 =&amp;gt; pid 323&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  [12] Search for all products that have at least 1 of the following tags ['poultry, 'kampai', 'best-seller'], and in stock. The price should be between 0 to 300 only. Sorted by cheapest to most expensive
&lt;/h3&gt;

&lt;p&gt;This query is similar to #11 but we added another criteria that the price of the products returned should only be between 0 and 300. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"query"&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;"bool"&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;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&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;"should"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="nl"&gt;"match"&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;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"poultry"&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="nl"&gt;"match"&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;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kampai"&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="nl"&gt;"match"&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;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"best-seller"&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="nl"&gt;"minimum_should_match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"term"&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;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="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="nl"&gt;"range"&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;"stocks"&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;"gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;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;"range"&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;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="nl"&gt;"gt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="nl"&gt;"lt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asc"&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="s2"&gt;"_score"&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 query introduces the "range" keyword, which allows us to filter for items if they match a specific range of values. For the price, we set a condition for the price to be between 0 and 300. For the stock, we only set the price to be greater than zero.&lt;/p&gt;

&lt;p&gt;Let's analyze the query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the results in #11, we have pid 333 (340pesos) and pid 123 (99.75pesos)&lt;/li&gt;
&lt;li&gt;With the 0-300 price filter, our only result will be pid 123 (99.75 pesos)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Getting started with ElasticSearch is easy! But your searching needs can become more complex as your business needs grow. This cheatsheet helps you navigate that complexity. &lt;/p&gt;

&lt;p&gt;An alternative to learning ES syntax at this level is to use a &lt;a href="https://elasticsearch-dsl.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;DSL library for Elasticsearch&lt;/a&gt; that "abstracts" the long-form syntax of Elasticsearch. It is a powerful tool for general-purpose usage of ES. However, as your query needs grow, learning the syntax under that DSL will keep you informed on the options you can add to make your searching richer. &lt;/p&gt;

&lt;h2&gt;
  
  
  How about you? Are there other ElasticSearch syntax you want to learn?
&lt;/h2&gt;

&lt;p&gt;Maybe I can help! Type it in the comments, and I'll try to add it to the article.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@tkristin?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;TK&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/superhero?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elasticsearch</category>
      <category>codenewbie</category>
      <category>aws</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
