<?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: Ivonne Roberts</title>
    <description>The latest articles on DEV Community by Ivonne Roberts (@ivlo11).</description>
    <link>https://dev.to/ivlo11</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%2F452656%2Fb75b6870-c1f9-475b-90a9-fb2bfffc4402.jpg</url>
      <title>DEV Community: Ivonne Roberts</title>
      <link>https://dev.to/ivlo11</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ivlo11"/>
    <language>en</language>
    <item>
      <title>How to choose a database on AWS</title>
      <dc:creator>Ivonne Roberts</dc:creator>
      <pubDate>Mon, 09 Aug 2021 10:35:36 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-choose-a-database-on-aws-1omh</link>
      <guid>https://dev.to/aws-builders/how-to-choose-a-database-on-aws-1omh</guid>
      <description>&lt;p&gt;How do I choose a database on AWS? How do I choose between NoSQL (Amazon DynamoDB) vs SQL (Amazon RDS)? When building a serverless microservice on AWS, I will walk you through how I narrow down what the best database on AWS is for my microservice.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/drkBB21eKdI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Database options on AWS
&lt;/h2&gt;

&lt;p&gt;When it comes to storing data in AWS, at the time of writing, there are over 15 different options. The options for our microservice’s persistent store are the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Aurora&lt;/li&gt;
&lt;li&gt;Amazon RDS

&lt;ul&gt;
&lt;li&gt;Aurora&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;MariaDB&lt;/li&gt;
&lt;li&gt;Oracle&lt;/li&gt;
&lt;li&gt;SQLServer&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Amazon Redshift&lt;/li&gt;
&lt;li&gt;Amazon DynamoDB&lt;/li&gt;
&lt;li&gt;DocumentDB&lt;/li&gt;
&lt;li&gt;Amazon Keyspaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not included here are the services for graph data and memory caching as it is not applicable for our use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why choosing the right database is important
&lt;/h2&gt;

&lt;p&gt;When choosing a database, it is important to make an informed decision to avoid issues down the road caused by cost, performance, or even worse, having to migrating to another database to support our business model. There are a few questions we need to ask ourselves when choosing a database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do we need a NoSQL or SQL option?
&lt;/h2&gt;

&lt;p&gt;Here is the JSON object we need to be able to support. If you want to understand how we arrived at this JSON object, read &lt;a href="https://blog.ivonneroberts.com/2021/06/16/how-to-design-a-restful-api-on-aws/"&gt;How to design a RESTful API on AWS&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"public"&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;"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;1234556&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;"Weird Animal Facts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tune in to learn about how horses make the neighing sound"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"dateTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-06-30T08:00:00+01:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"duration"&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;"length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"HOUR"&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;"locations"&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;"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;"ONLINE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://ivonneroberts.com/meetingURL"&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;"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;"VENUE"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"streetAddress1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1 5th Avenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"streetAddress2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New York"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"zip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10001&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A normalized form of this data could be represented by an &lt;code&gt;Event&lt;/code&gt; table, a &lt;code&gt;Schedule&lt;/code&gt; table and a &lt;code&gt;Location&lt;/code&gt; table. Let’s analyze these entities to be able to determine which type of database on AWS we could use, NoSQL or SQL.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Event&lt;/code&gt; table could have a 1:many relationship with &lt;code&gt;Schedule&lt;/code&gt;. You can imagine a scenario where an event could have a repeating schedule. That &lt;code&gt;Schedule&lt;/code&gt; table could have multiple entries for a given event. To decide NoSQL versus SQL, let’s look at the update query. How many event records would need to be updated, if an instance needs to be changed? We would need to update one record. This makes it a candidate for NoSQL database.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Event&lt;/code&gt; table could also have a 1:many relationship with &lt;code&gt;Location&lt;/code&gt;. An event could be both online, as well as, at a venue. Asking the same NoSQL versus SQL, let’s look at the update query. If a location needs to be updated how many event records would need to be updated? The answer is n events that use this same venue. You would have to query the table to find how many events have that venue and then update each one. This is not a candidate for NoSQL. However, the attributes of a location, have a lot of details or nuances that have nothing to do with an event. In fact, I would argue &lt;code&gt;Location&lt;/code&gt; should be in its own microservice. The Event REST API response should instead only include a location id, and that information stored as part of another microservice.&lt;/p&gt;

&lt;p&gt;With those questions answered, the remaining data in our microservice now could be stored in a NoSQL database.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the data will be queried
&lt;/h2&gt;

&lt;p&gt;For these first two urls, a NoSQL implementation (specifically Amazon DynamoDB) would be a scan on the full table. As the data grows, scans are not cost efficient, nor performant. We can mitigate this problem by introducing a range/sort key in addition to our primary/partition key, for example category. As a client, I am subscribed to the YouTube category. These APIs could use your authentication to determine your eligible categories, query the DB for them and then further filter either by date or other attributes of the event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mydomain.com/events/v1/events/
https://api.mydomain.com/events/v1/events/search?date[gt]=2021-07-01&amp;amp;status=CANCELLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This next API is straight forward. We can query our NoSQL database by the event id (partition key in DyanmoDB).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mydomain.com/events/v1/events/{eventId}/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Costs associated with databases on AWS
&lt;/h2&gt;

&lt;p&gt;The next question we need to ask is what are the cost consideration. When choosing a database there is both the cost of the actual database/license, as well as, the people cost to maintain and fine tune that database. For some of the databases on AWS you pay for what you use/store, most of the heavy lifting is abstracted from you. Other databases require EC2 instances, additional licenses in addition to the service. Also, depending on the database, you might need a database administrator to update, manage and fine tune that database. This can be cost prohibitive for a startup.&lt;/p&gt;

&lt;p&gt;Depending on where you are in your journey it is acceptable to start with the minimum requirements, and then at a later date take the downtime and migrate to another database that better meets your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finalized JSON structure
&lt;/h2&gt;

&lt;p&gt;Now that we have reviewed the considerations for choosing our database, and we have finalized on a NoSQL option. Let’s update our JSON payload to reflect the changes we called out. First we have added a category attribute, this will be our range/sort key. Second, we have added a schedule attribute that is a list of all our instances of this event. Lastly we have updated the location object to now simply refer to an ID that references a resource in a separate location microservice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"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;1234556&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEVWIDGETS_YOUTUBE"&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;"Weird Animal Facts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tune in to learn about how horses make the neighing sound"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"schedules"&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;"dateTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-06-30T08:00:00+01:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"duration"&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;"length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"HOUR"&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;"locations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;789&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;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;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So after much deliberation we have finalized our JSON payload and have decided to use NoSQL. On AWS our options are Amazon DynamoDB, Amazon DocumentDB, and Amazon Keyspaces. For this microservice I am going to use Amazon DynamoDB as it is a fully managed service. It is a fairly robust database that abstracts a lot of the heavy lifting associated with managing a database. Most importantly it is serverless which means I don’t need to concern myself with EC2 pricing, patching and troubleshooting. And, to name a few more key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully managed&lt;/li&gt;
&lt;li&gt;multi-region&lt;/li&gt;
&lt;li&gt;multi-active&lt;/li&gt;
&lt;li&gt;built-in security&lt;/li&gt;
&lt;li&gt;backup and restore&lt;/li&gt;
&lt;li&gt;in-memory caching&lt;/li&gt;
&lt;li&gt;configurable autoscaling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We now have walked through how to choose a database on AWS Cloud. Next week, I will continue to build on this design and implement some of these APIs on AWS. Check back weekly for new content. Feel free to comment below any questions you may have or reach out to me on twitter at &lt;a class="mentioned-user" href="https://dev.to/ivlo11"&gt;@ivlo11&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy Coding!
&lt;/h2&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>nosql</category>
      <category>microservices</category>
    </item>
    <item>
      <title>How to design a RESTful API on AWS</title>
      <dc:creator>Ivonne Roberts</dc:creator>
      <pubDate>Mon, 26 Jul 2021 15:07:08 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-design-a-restful-api-on-aws-2nd9</link>
      <guid>https://dev.to/aws-builders/how-to-design-a-restful-api-on-aws-2nd9</guid>
      <description>&lt;p&gt;Have you ever asked yourself what is a REST API? How do I design a RESTful API on AWS Cloud? How do I write a RESTful java microservice? As a software engineer building on AWS, I’ll walk you through designing a REST API.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/6upZCfWuB1Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a REST API?
&lt;/h2&gt;

&lt;p&gt;A REST API is a way for clients to get/store information with an application through HTTP. This is typically what websites and mobile apps use to communicate with backends services. For example: What’s today’s weather in Paris? A website makes a HTTP call to a weather service with Paris as an input. In response, the weather service returns a message with the details that that website would need to show that information.&lt;/p&gt;

&lt;p&gt;The text format that the client and the backend service exchange is usually in JSON structure. However, you could choose to use others like HTML, XML, plain text and so on. The RESTful specification does not define what format you must use unlike something like SOAP which uses XML.&lt;/p&gt;

&lt;p&gt;REST APIs should also be stateless. When calling the same API multiple times, nothing is stored in the backend causing it to change its response. For example, with pagination, calling an API will return a “page” (or subset) of items. Every time i call it, it will return the same subset, regardless of if I had already called that API. However, if I call that API give me the next 20 items starting an index 40, it will then return something different.&lt;/p&gt;

&lt;p&gt;Another characteristic of REST APIs is that they can be cached. When requesting the item, with an id of 123, multiple times, the first request would go to the microservice. The second time I request that item, with a short window, it is unlikely that it has changed. At that point I should instead just receive a cached response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Business Use Case to Design a REST API for
&lt;/h2&gt;

&lt;p&gt;So let’s put some of what we have learned to practice. We will be designing the APIs for the following business use case. An application needs a way to get the list of upcoming events. The consumers for this API could be a mobile application that displays the events that you are registered for. This API could also be used an a website that advertises upcoming events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the RESTful API
&lt;/h2&gt;

&lt;p&gt;When you name an api you want to use something short and simple. It should accurately convey the service or data it is providing and indication of what you will receive. You don’t want to use long string like &lt;code&gt;events-i-signed-up-for&lt;/code&gt;. Instead you want things like that to show up in an hierarchy of paths like &lt;code&gt;events/search?registered=true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next thing you want is versioning. When creating an API, you define a contract with you consumers. When you need to make a breaking change, they may not be able to update their code base at the precise minute that you deploy. Instead, you would deploy a new version in addition to your existing version. This gives clients time to migration from the previous version to the new one. The key here is to not make breaking changes, but when you have to, use versioning.&lt;/p&gt;

&lt;p&gt;A simple path could be as follows. The name of your microservice, the version number and then the resource that you are requesting. In the below example, the microservice is called events, the version is v1 and the resources are events and locations respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mydomain.com/events/v1/events/
https://api.mydomain.com/events/v1/locations/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These APIs would return a list indicated by the plural resource in the path and brackets in the response&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="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;234&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A path to return a specific event would be structured as follows. The name of your microservice, the version number, the resource that you are requesting and the id of the specific item you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mydomain.com/events/v1/events/{eventId}/
https://api.mydomain.com/events/v1/events/{eventId}/locations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also do complicated searching on resources with something like the below pattern. The path is the name of your microservice, the version number, the resources that you are searching, and that fields that should match the returned resources. For example in the below paths, it searches for events where the date is greater than 2021-07-01 and the status is CANCELLED. The second path searches for events that have a duration of longer than 1 hour&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.mydomain.com/events/v1/events/search?date[gt]=2021-07-01&amp;amp;status=CANCELLED
https://api.mydomain.com/events/v1/events/search?dateAndTime.duration.length[gt]=1&amp;amp;dateAndTime.duration.unit=HOUR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  JSON Structure of our RESTful API
&lt;/h2&gt;

&lt;p&gt;Now that we have an idea of what our paths should look like let us look at the JSON structure that our responses will look like. JSON objects are surrounded by curly braces {}. Then the contents are key value pairs where the keys are surrounded in quotes. The values can be the following data types: a number, a string, a boolean, an object, a list or simply null.&lt;/p&gt;

&lt;p&gt;When choosing which data types to use you want to keep flexibility in mind. Considering that this is a contract with your clients, you want to choose data types that will allow you to extend. For example status is a property that would not need multiple fields. However, the location could be multiple places, so a list would allow us to represent that instead of having a flat structure like location1, location2, etc. Another example is date and time. It could have multiple attribute like time, date, and duration. This might be better represented with another JSON object.&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="p"&gt;{&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACTIVE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234556&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Weird Animal Facts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tune in to learn about how horses make the neighing sound&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dateAndTime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2021-06-30&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;08:00:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timeZone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GMT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;duration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HOUR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;locations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ONLINE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://ivonneroberts.com/meetingURL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VENUE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;streetAddress1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 5th Avenue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;streetAddress2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;city&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New York&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;state&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10001&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currency&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PER_PERSON&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We now have walked through how I design a RESTful API, the path and JSON structure, on AWS Cloud. I continue to build on this design in &lt;a href="https://blog.ivonneroberts.com/2021/06/16/how-to-design-a-restful-api-on-aws/"&gt;How to Choose a Database on AWS&lt;/a&gt;. Check back weekly for new content. Feel free to comment below any questions you may have or reach out to me on twitter at &lt;a href="https://twitter.com/ivlo11"&gt;@ivlo11&lt;/a&gt;&lt;/p&gt;

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

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>microservices</category>
      <category>rest</category>
    </item>
    <item>
      <title>Add Security Headers to your Serverless Static Site</title>
      <dc:creator>Ivonne Roberts</dc:creator>
      <pubDate>Wed, 07 Jul 2021 12:50:32 +0000</pubDate>
      <link>https://dev.to/aws-builders/add-security-headers-to-your-serverless-static-site-4i54</link>
      <guid>https://dev.to/aws-builders/add-security-headers-to-your-serverless-static-site-4i54</guid>
      <description>&lt;p&gt;Being able to create a static website hosted on AWS S3 and fronted by Amazon CloudFront has become the serverless standard these days. Both of these AWS services facilitate a lot of the heavy lifting for you by giving you performant web hosting, with features like caching at edge locations closer to your end users, and security features like mitigating DDoS attacks.&lt;/p&gt;

&lt;p&gt;One thing that required a little more effort then I’d prefer was the ability to add security headers to responses. That used to have to be done through Lambda@Edge which, while still serverless, ideally it should be reserved for more computationally involved origin resolution or response manipulation. Today you can now do that with Amazon CloudFront Functions.&lt;/p&gt;

&lt;p&gt;Amazon CloudFront Functions provide short lived simple JavaScript functions that can manipulate your response/request. Some use cases could be url rewrites/redirects, access authorization and what I will show you here, header manipulation. See Danilo Poccia’s blog post for more details around &lt;a href="https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/"&gt;Amazon CloudFront Functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Keep reading to see how we add the following security headers to our requests using AWS Cloudformation and Amazon CloudFront Function&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strict-Transport-Security&lt;/li&gt;
&lt;li&gt;Content-Security-Policy&lt;/li&gt;
&lt;li&gt;X-Content-Type-Options&lt;/li&gt;
&lt;li&gt;X-Frame-Options&lt;/li&gt;
&lt;li&gt;X-XSS-Protection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create your Serverless function
&lt;/h2&gt;

&lt;p&gt;In CloudFormation there are a few things you need to define. 1) The name of your function, in my case &lt;code&gt;add-security-headers&lt;/code&gt;. 2) that you want this to be published automatically. 3) Your function configuration which includes what run time it will use, which at the time of writing this only includes &lt;code&gt;cloudfront-js-1.0&lt;/code&gt;. And lastly 4) your actual code. See the complete resource definition 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="na"&gt;addSecurityHeadersFunction&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::CloudFront::Function&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;add-security-headers&lt;/span&gt;
    &lt;span class="na"&gt;AutoPublish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;FunctionConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Adds security headers to the response&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;cloudfront-js-1.0&lt;/span&gt;
    &lt;span class="na"&gt;FunctionCode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;function handler(event) {&lt;/span&gt;
          &lt;span class="s"&gt;var response = event.response;&lt;/span&gt;
          &lt;span class="s"&gt;var headers = response.headers;&lt;/span&gt;

          &lt;span class="s"&gt;// Set HTTP security headers&lt;/span&gt;
          &lt;span class="s"&gt;// Since JavaScript doesn't allow for hyphens in variable names, we use the dict["key"] notation&lt;/span&gt;
          &lt;span class="s"&gt;headers['strict-transport-security'] = { value: 'max-age=63072000; includeSubdomains; preload'};&lt;/span&gt;
          &lt;span class="s"&gt;headers['content-security-policy'] = { value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"};&lt;/span&gt;
          &lt;span class="s"&gt;headers['x-content-type-options'] = { value: 'nosniff'};&lt;/span&gt;
          &lt;span class="s"&gt;headers['x-frame-options'] = {value: 'DENY'};&lt;/span&gt;
          &lt;span class="s"&gt;headers['x-xss-protection'] = {value: '1; mode=block'};&lt;/span&gt;

          &lt;span class="s"&gt;// Return the response to viewers&lt;/span&gt;
          &lt;span class="s"&gt;return response;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Link your CloudFront Distribution
&lt;/h2&gt;

&lt;p&gt;Now you can update your existing CloudFront Distribution by associating the function to your CacheBehavior as below and deploy your cloudformation. In my example below I’m importing the ARN from a separate template that includes all my CloudFront Functions.&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;FunctionAssociations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;EventType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;viewer-response&lt;/span&gt;
    &lt;span class="na"&gt;FunctionARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fn::Sub'&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${CloudFrontStackName}-AddSecurityHeadersFunction&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test your implementation
&lt;/h2&gt;

&lt;p&gt;Once completed head over to your terminal window and use curl to test out our headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ivonne@my-machine ~ % curl -i https://static.ivonneroberts.com
 HTTP/1.1 200 OK
 Content-Type: text/html
 Content-Length: 87
 Connection: keep-alive
 Date: Fri, 28 May 2021 23:59:19 GMT
 Last-Modified: Fri, 28 May 2021 23:06:46 GMT
 Etag: "7875df45e56965139099615f6c5c907b"
 Accept-Ranges: bytes
 Server: AmazonS3
 Via: 1.1 3dc5af024af63cc0e8b9cf31fd852ecf.cloudfront.net (CloudFront)
 Content-Security-Policy: default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'
 Strict-Transport-Security: max-age=63072000; includeSubdomains; preload
 X-Xss-Protection: 1; mode=block
 X-Frame-Options: DENY
 X-Content-Type-Options: nosniff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see our 5 security headers are now displayed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Content-Security-Policy: default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'
 Strict-Transport-Security: max-age=63072000; includeSubdomains; preload
 X-Xss-Protection: 1; mode=block
 X-Frame-Options: DENY
 X-Content-Type-Options: nosniff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Adding a Amazon CloudFront function is pretty simple and you can see the updates almost immediately. If you would like to see the full CloudFormation templates head on over my github (link below). Feel free to modify it to fit your use case. As always if you have any questions reach out!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt; [&lt;a href="https://github.com/ivlo11/serverless-patterns/tree/main/static-site-with-cloudfront-function"&gt;https://github.com/ivlo11/serverless-patterns/tree/main/static-site-with-cloudfront-function&lt;/a&gt;]&lt;br&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; [&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html"&gt;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html&lt;/a&gt;]&lt;br&gt;
&lt;strong&gt;CloudFormation Documentation:&lt;/strong&gt; [&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-function.html"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-function.html&lt;/a&gt;]&lt;br&gt;
&lt;strong&gt;CloudFront Function Sample Code:&lt;/strong&gt; [&lt;a href="https://github.com/aws-samples/amazon-cloudfront-functions"&gt;https://github.com/aws-samples/amazon-cloudfront-functions&lt;/a&gt;]&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy Coding!
&lt;/h2&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>s3</category>
      <category>cloudfront</category>
    </item>
    <item>
      <title>AWS SQS Events On AWS Lambda</title>
      <dc:creator>Ivonne Roberts</dc:creator>
      <pubDate>Wed, 26 Aug 2020 19:18:35 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-sqs-events-on-aws-lambda-906</link>
      <guid>https://dev.to/aws-builders/aws-sqs-events-on-aws-lambda-906</guid>
      <description>&lt;p&gt;In 2018, AWS has announced support for SQS triggered events on AWS Lambda. For companies embracing a serverless architecture this opened up new possibilities for event-driven architecture, streamlining batch infrastructure and much more.&lt;/p&gt;

&lt;p&gt;Before the feature launched, if you were a serverless shop that needed to process SQS messages, the only option was to use CloudWatch to trigger a Lambda function, that polled for messages and then either fanned out workers or chewed threw batches of SQS messages. While this worked, it was prone to error. A Lambda function can only live for 5 minutes, at which point it could either spin up another Lambda function and pass the torch or simply wait for CloudWatch to trigger another Lambda function. Depending on how long the Lambda function would run, you either, lost time waiting for the next CloudWatch trigger or had a high number of messages with multiple receives as the Lambda function timed out.&lt;/p&gt;

&lt;p&gt;At Edelman Financial Engines, we put a stake in the ground to embrace two things: event-driven architecture and serverless architecture. These two designs have allowed us to create more scalable applications as well as focus on the things that are important to us and have a direct impact on our clients. With the recent Lambda and SQS announcement, we now have a new option to process our queues in a serverless fashion: simply configure an event source mapping on a Lambda function for a queue.&lt;/p&gt;

&lt;h1&gt;
  
  
  Event-Driven Architecture
&lt;/h1&gt;

&lt;p&gt;When a client or a planner does something on our site, a cascade of decisions and processes are run. An event-driven architecture enables us to do that work in a reasonable amount of time. Take, for example, a client logging in. When a client presses that login button, we have to do a series of things; get the client’s latest holdings from the provider, validate the client’s information, generate a snapshot of the client’s progress, run Monte Carlo simulations and the list goes on.&lt;/p&gt;

&lt;p&gt;With event-driven architecture, as soon as the login happens, an event is sent to an event-bus where consumers are standing by waiting to process certain events. In our use case, an analytics consumer could process the login event and trigger a client engagement metric for reporting. Soon after, another event is published saying the client’s latest holdings are updated. Multiple consumers of that event can then pre-calculate the clients current progress and run Monte Carlo simulations simultaneously and cache the results. Now, as the client navigates to our site landing page, their results show up almost instantaneously instead of the 10+ seconds it would normally take if we did this all sequentially.&lt;/p&gt;

&lt;p&gt;Having services like SNS and SQS greatly facilitates this architecture model. We can have consumers (AWS Lambda, SQS queue, HTTP endpoint, etc) set as endpoints to SNS topics that do asynchronous heavy lifting on the inputs. For SQS, queues can be processed by a consumer (formerly only EC2 instances, but now, Lambda functions as well) chewing through the messages in a batch fashion.&lt;/p&gt;

&lt;h1&gt;
  
  
  Serverless Architecture
&lt;/h1&gt;

&lt;p&gt;At Financial Engines, we have embraced a DevOps culture where decisions are shifted to the functional development team level providing greater choice of tools and technology to match various problems at hand. However, what we don’t want is development teams to be bogged down with tasks like OS patching, auto-scaling servers, and so on. To that end, we have set a standard for teams to use managed services when possible.&lt;/p&gt;

&lt;p&gt;Teams can spin up complete microservices using AWS Lambda, DynamoDB, S3, etc., without provisioning or managing any server and OS patching, and still get the power they need. In addition to ease of use, we have had significant cost savings from switching to serverless computing. (Checkout this Financial Engines AWS Case Study for more details on that.)&lt;/p&gt;

&lt;h1&gt;
  
  
  Using SQS &amp;amp; AWS Lambda to Pre-Calculate Progress
&lt;/h1&gt;

&lt;p&gt;For an example of this new feature/architecture, I am going to dive deep into one of the scenarios mentioned above. When a client’s holdings have been updated, whether the client triggered the update or a job ran to update all client holdings, an event is published with a few details on that client and their holdings. We then have consumers of that event pre-calculate the client’s current progress and cache the results.&lt;/p&gt;

&lt;p&gt;There are a couple considerations we needed to make.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the client triggered the update (e.g. the client logged in), then it is likely the progress score will be consumed soon and the pre-calculation needs to happen as soon as possible.&lt;/li&gt;
&lt;li&gt;If a job ran to update all client holdings, then the possibility of that progress score being consumed soon is fairly low and therefore we can take our time doing the pre-calculation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To accomplish the requirements we created the following architecture.&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%2Fi%2Fbuui0n2tdexa2kza8bvv.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%2Fi%2Fbuui0n2tdexa2kza8bvv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our event bus publishes events to SNS topics. From there, consumers decide how to process the messages. For our client-triggered updates, we have an AWS Lambda function set as an endpoint to the holdings updated topic. We also configured message attribute filtering so that this Lambda function only receives client-triggered events. For our job-triggered events, we used an SQS queue to consume filtered events. We then used the newly-created SQS Event on AWS lambda to process that queue.&lt;/p&gt;

&lt;p&gt;To follow, I’ll walk you through how that SQS-to-AWS Lambda connection was configured.&lt;/p&gt;

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

&lt;p&gt;The first thing you need is a lambda that accepts SQS Events&lt;/p&gt;

&lt;p&gt;For our Lambda function we need the following dependencies via gradle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;compile 'com.amazonaws:aws-java-sdk-lambda:1.11.321'
compile 'com.amazonaws:aws-java-sdk-sqs:1.11.321'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the very basic level, the handler code in Java 8 is the configured something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.fngn.samples;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import com.fngn.samples.api.dto.EventDto;
import com.fngn.samples.api.dto.ResponseDto;
import com.fngn.samples.application.service.EventConsumer;
public class QueueConsumerLambda implements RequestHandler&amp;lt;SQSEvent, Void&amp;gt; {
  @Override
  public Void handleRequest(SQSEvent event, Context context) {
    try {
      for (SQSEvent.SQSMessage message : event.getRecords()) {
        EventDto eventDto = null;
        ResponseDto responseDto = null;
String input = message.getBody();
        eventDto = unmarshalEventBody(input);
        responseDto = eventConsumer.processEvent(eventDto);
        handleFailures(responseDto, message);
      }
    } catch (Exception ex) {
      logger.error("Exception handling batch seed request.", ex);
      throw ex;
    }
    return null;
  }
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we bring this together with the following CloudFormation definition for our Lambda function and our execution role (for bonus points, we have added dead letter queue configuration):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;queueConsumerLambda:
  Type: 'AWS::Lambda::Function'
  Properties:
    Handler: 'com.fngn.samples.QueueConsumerLambda::handleRequest'
    Role: !Sub "arn:aws:iam::*:role/${queueConsumerExecutionRole}"
    Description: !Sub "Cloud formation lambda for ${projectName}"
    FunctionName: !Sub "${projectName}-queue-consumer"
    MemorySize: 512
    Timeout: 30
    Code:
      S3Bucket: "com.fngn.samples"
      S3Key: !Sub "${projectName}/${lambdaCode}"
    Runtime: java8
    TracingConfig:
      Mode: Active
    Environment:
      Variables:
        REGION: !Ref "AWS::Region"
        APP_ID: !Sub ${projectName}-event-consumer
        ENV_REALM: !Sub ${accountType}
  DependsOn:
    - queueConsumerExecutionRole
queueConsumerExecutionRole:
  Type: 'AWS::IAM::Role'
  Properties:
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
      - Action: 'sts:AssumeRole'
        Principal:
          Service: lambda.amazonaws.com
        Effect: Allow
        Sid: ''
    Policies:
    - PolicyName: !Sub "${projectName}-queue-consumer-policy"
      PolicyDocument:
        Version: '2008-10-17'
        Statement:
        ...
        - Action:
          - sqs:ChangeMessageVisibility
          - sqs:DeleteMessage
          - sqs:GetQueueAttributes
          - sqs:ReceiveMessage
          - sqs:SendMessage
          Effect: Allow
          Resource:
          - !GetAtt sqsQueue.Arn
          - !GetAtt sqsQueueDLQ.Arn
          Sid: 'SQSPermissions'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We can now create an SQS queue via CloudFormation that subscribes to our SNS topic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sqsQueue:
  Type: 'AWS::SQS::Queue'
  Properties:
    QueueName: !Sub '${projectName}-ConsumerQueue'
    RedrivePolicy:
      deadLetterTargetArn: !GetAtt sqsQueueConsumerDLQ.Arn
      maxReceiveCount: 5
    VisibilityTimeout: 600

policyQueueConsumer:
  Type: 'AWS::SQS::QueuePolicy'
  Properties:
    PolicyDocument:
      Id: !Sub '${projectName}-ConsumerQueuePolicy'
      Version: '2012-10-17'
      Statement:
      - Sid: 'AllowConsumerSnsToSqsPolicy'
        Effect: 'Allow'
        Principal:
          AWS: '*'
        Action:
          - 'sqs:SendMessage'
        Resource: !GetAtt sqsQueue.Arn
        Condition:
          ArnEquals:
            aws:SourceArn: !Sub 'arn:aws:sns:*:*:holding-events']]
    Queues:
      - !Ref sqsQueue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Now that those two are in place we can configure the trigger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sqsEventTrigger:
  Type: "AWS::Lambda::EventSourceMapping"
  Properties:
    BatchSize: 5
    Enabled: true
    EventSourceArn: !GetAtt sqsQueue.Arn
    FunctionName: !Sub "${projectName}-queue-consumer"
  DependsOn:
    - queueConsumerLambda
    - queueConsumerExecutionRole

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

&lt;/div&gt;



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

&lt;p&gt;Send messages to your SQS queue or, in our case, SNS topic and sit back and watch the Lambda function chew through messages as they come in. Honestly, it is as simple as that.&lt;/p&gt;

&lt;p&gt;With this configuration, here’s what’s happening. AWS Lambda takes over polling and invoking concurrent Lambda functions to chew through the queue with chunks of messages up to the configured batch size (In our case we configured the batch size to be 10 messages). The Lambda function processes each message in the SQSEvent. If any of those messages fail, the Lambda function fails and the messages stay on the queue. If all the messages are successful then AWS Lambda deletes the message from the queue. If the queue has a dead letter queue (redrive policy above) configured, then AWS Lambda will try to process those messages until it reaches the max receive count of 5. At which point, if the visibility timeout expires and the message is still on the queue, SQS will delete that message from the queue and move it to the dead letter queue.&lt;/p&gt;

&lt;h1&gt;
  
  
  Monitoring
&lt;/h1&gt;

&lt;p&gt;You can use the dashboards in the AWS Lambda and SQS service consoles as well as configure your own CloudWatch dashboards to monitor the progress. We also employed the use of AWS X-Ray which really helped us early on to identify issues with downstream resources in our microservice.&lt;/p&gt;

&lt;p&gt;In the AWS Lambda dashboard, you can see quickly see how the service is invoking your Lambda function pretty consistently until the queue is empty. In the below case, we limited our execution to 200 “workers.” So, you can also see throttles as AWS Lambda enforced the limit on concurrent executors.&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%2Fi%2Fi3sqxuejngfp8o1ccosb.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%2Fi%2Fi3sqxuejngfp8o1ccosb.png" alt="AWS Lambda Monitoring Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the SQS dashboard, you can see the rate of messages coming into the queue, rate of receives, and other interesting tidbits. As you can see in the below chart, our job was generating events faster that our 200 concurrent executors were processing them, by noticing the age of messages go up and then drop precipitously as the Lambda functions finish processing all the messages.&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%2Fi%2Fueydbc134ic4qwxgnymp.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%2Fi%2Fueydbc134ic4qwxgnymp.png" alt="SQS Monitoring Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here in X-Ray, you can see all the components that make up this microservice. The Lambda function that processes the SNS messages, the Lambda function that process the SQS queue, and the Lambda function that serves up the cached response.&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%2Fi%2Fujlm06069629xkrxlqum.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%2Fi%2Fujlm06069629xkrxlqum.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Advance Configuration
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Concurrency and EventSourceMapping Config
&lt;/h2&gt;

&lt;p&gt;With our use case, we took configuration a bit further. For example, if a job ran to update all client holdings, then the rate of events per minute spikes fairly high in a short period of time. Downstream resources (e.g. database) used to calculate the progress score might not be able to support that load.&lt;/p&gt;

&lt;p&gt;In this case, what we did was create a schedule with subscription details given the day of the week or time of day. We then configured a CloudWatch event that triggers a Lambda function hourly. Based on the schedule’s subscription details, that Lambda function updates either the SQS consumer Lambda function config or its SQS event mapping.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "MON-FRI_09:00-04:00_11:00-04:00": {
    "queueEnabled":false
  },
  "MON-FRI_11:00-04:00_22:00-04:00": {
    "queueEnabled":true,
    "workers":15,
    "batchSize":10
  },
  "MON-FRI_22:00-04:00_09:00-04:00": {
    "queueEnabled":true,
    "workers":100,
    "batchSize":10
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This level of control proved to be quite powerful. During the day, at peak times of our load, it was prudent to not risk stressing downstream resources while they were simultaneously serving client traffic. We could either completely disable processing (i.e. disable the EventSourceMapping) or configure the Lambda function execution concurrency to a low number (ex: 15). However, in the evenings, we can ramp up the number of concurrent executions to 100. On the weekends, we could pump that up even higher to, say, 200 concurrent executions.&lt;/p&gt;

&lt;p&gt;It took a cross-functional team effort and several runs to determine our concurrency limit. With resources both in the cloud and on-premises, we needed to closely monitor them as we kept ramping up the numbers. At the end of the day, our on-premises relational database ended up limiting the microservice to not exceed 200 concurrent executions, roughly equaling 5000 messages per minute and 1.4m database calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filtering SNS messages
&lt;/h2&gt;

&lt;p&gt;As mentioned previously, our use case had 2 flavors of the holdings updated event. There were events triggered by users and events triggered by jobs. Those SNS jobs have their event metadata also configured in the SNS message attributes field.&lt;/p&gt;

&lt;p&gt;In this case, you can use the SDK to configure filtering the messages for a given endpoint. For our SNS-to-AWS Lambda, flow we could have it only receive events that had appType=WEB. For our SNS-to-SQS flow we configured it to only receive events that had appType=JOB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Subscription subscription = getSubscriptionForArn(environment.getenv("BATCH_SNS_ENDPOINT"));
if (batchSubscription != null) {
  String jsonPolicy = "{\"applicationType\": [\"JOB\"]};
  setFilterPolicy(subscription.getSubscriptionArn(), jsonPolicy);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We took it a step further and also filtered the messages on the financial institutions or employers that were enabled. This helped us significantly reduce cost on message processing, as well as simplified our code, as the Lambda function no longer had to do its own filtering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String getBatchSubscriptionFilterPolicy() throws IOException {
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  JsonGenerator generator = (new JsonFactory()).createGenerator(baos, JsonEncoding.UTF8);

  generator.writeStartObject();
  generator.writeFieldName("applicationType");
  generator.writeStartArray();
  generator.writeString("JOB");
  generator.writeEndArray();

  Set&amp;lt;String&amp;gt; financialInstitutions = getFinancialInstitutions();
  if (financialInstitutions.isEmpty() == false) {
    generator.writeFieldName("financialInstitutionId");
    generator.writeStartArray();
    for (String financialInstitution : financialInstitutions) {
      generator.writeString(financialInstitution);
    }
    generator.writeEndArray();
  }

  Set&amp;lt;String&amp;gt; employers = getEmployers();
  if (employers.isEmpty() == false) {
    generator.writeFieldName("employerId");
    generator.writeStartArray();
    for (String employer : employers) {
      generator.writeString(employer);
    }
    generator.writeEndArray();
  }

  generator.writeEndObject();
  generator.close();

  return baos.toString();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Notes/Tips/Features Requests
&lt;/h1&gt;

&lt;p&gt;While this is a fairly strong feature, there are a few things we would like to see in the future.&lt;/p&gt;

&lt;p&gt;For example, when processing the dead letter queue with this feature, you actually have to manually delete failed messages, or they very quickly become “poison pills.” If you don’t manually delete the messages, then you need to create a dead letter queue for your dead letter queue so that you can set the max receive count to 2 for example, and just ignore what gets into that final queue. It’s a little more heavy lifting than I had expected, when I thought I could just re-use the same Lambda function processing the main queue. It would be nice if AWS would allow you to set a redrive policy without specifying another dead letter queue.&lt;/p&gt;

&lt;p&gt;The other issue was the rate of throttling in the Lambda function. At times, we saw several hundred invocations a minute getting throttled. While the documentation says that concurrent execution is honored, we assumed that the rate of Lambda functions the AWS Lambda would invoke was also going to be limited to the number of concurrent executors. I imagine that, long-term, this can get pricey and somewhat wasteful if you limit concurrent executions. I’d like to see AWS address this or try to minimize the number of throttled lambdas.&lt;/p&gt;

&lt;p&gt;Lastly, while not really an issue that AWS could address, we struggled with configuring downstream resources’ auto scaling. Since, as mentioned before, jobs can start at any time of day and cause a sudden significant spike of publish events in a short period of time. Downstream services like DynamoDB’s read/write auto scaling always lagged to respond for the first few minutes. This would create a spike in throttles and therefore failed messages, another reason why configuring a dead letter queue is important. (Note that the dashboard shows a per minute average so that doesn’t always equate to actual throttles.)&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%2Fi%2Fb9cwqb2lsrisktatfz1h.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%2Fi%2Fb9cwqb2lsrisktatfz1h.png" alt="DynamoDB Write Capacity Auto-Scaling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I hope that through this article you can see how powerful this new feature is and how it can greatly reduce code/management of infrastructure to support queue processing.&lt;/p&gt;

&lt;p&gt;Feel free to ask any questions and reach out with any comments you may have. Also, come back and check out any new content we publish. We are always playing around with some of the latest and greatest features with the goal of figuring out all the nuances before teams start using the features.&lt;/p&gt;

&lt;h1&gt;
  
  
  Happy Coding!
&lt;/h1&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>eventdriven</category>
      <category>sqs</category>
    </item>
  </channel>
</rss>
