<?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: Jared Short</title>
    <description>The latest articles on DEV Community by Jared Short (@shortjared).</description>
    <link>https://dev.to/shortjared</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%2F171486%2F36cde198-caaf-4fe4-8b99-d5bb178eb2d9.jpg</url>
      <title>DEV Community: Jared Short</title>
      <link>https://dev.to/shortjared</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shortjared"/>
    <language>en</language>
    <item>
      <title>From LEGO Bricks to Data Blocks: Adventures with PromptQL</title>
      <dc:creator>Jared Short</dc:creator>
      <pubDate>Tue, 19 Nov 2024 14:20:38 +0000</pubDate>
      <link>https://dev.to/shortjared/from-lego-bricks-to-data-blocks-adventures-with-promptql-alo</link>
      <guid>https://dev.to/shortjared/from-lego-bricks-to-data-blocks-adventures-with-promptql-alo</guid>
      <description>&lt;p&gt;Did you know that across all the LEGO Star Wars sets, there are 9113 unique pieces used?&lt;/p&gt;

&lt;p&gt;If you were to build every single one of the Star Wars sets, you would have used 63,269 of the "Light Bluish Gray" color (#A0A5A9).&lt;/p&gt;

&lt;p&gt;Occasionally, I stumble across unique data and interesting data sets that I want to explore. Usually I end up throwing them in a SQL database and poking around the schema and do some SQL queries here or there. Sometimes I can learn a lot just grepping my way through the files for keywords and doing some analysis of those results.&lt;/p&gt;

&lt;p&gt;Given I like playing with data, LLMs and RAG (Retrieval-augmented generation) seem inescapable these days. Using the power of an LLM to ask more in-depth questions across a vectorized space has yielded some surprising results, but I struggle to trust it beyond basic discovery and toy use cases. Don't get me wrong, I do use these tools where I find they aid my process, however I am also cautious when adding new tools to any kind of feature or process, especially ones that can "hallucinate" (read: "lie to your face and not be at all sorry").&lt;/p&gt;

&lt;h1&gt;
  
  
  L(EGO)LLM
&lt;/h1&gt;

&lt;p&gt;I recently found the LEGO data from &lt;a href="https://rebrickable.com/downloads/" rel="noopener noreferrer"&gt;Rebrickable Data&lt;/a&gt; is downloadable in bulk, and contains all sorts of data about LEGO sets, pieces, minifigures, and more.&lt;/p&gt;

&lt;p&gt;There is a lot of CSV data here. Millions of records. And given most of it is just numbers and relationships, RAG seemed like the wrong tool. We aren't trying to search or extract semantic meaning from things like titles or comments or descriptions. We don't have much to use a strategy like text embedding to get vectors. In this case, we are trying to draw meaning between lots of relationships defined with joins on ids and aggregations. However, not all is lost. There's been a flurry of activity with Agents and Agentic tools in the LLM space. Basically, giving an LLM the ability to do things like create and run code, and some degree of "autonomy" to figure out issues.&lt;/p&gt;

&lt;p&gt;I've been playing with a new tool called &lt;a href="https://shortclick.link/geq3ew" rel="noopener noreferrer"&gt;PromptQL&lt;/a&gt; from Hasura that leverages what they call "Agentic Data Access", that has an interesting twist on things. (Note: I was given early access and was working with the team to provide feedback and they sponsored some of my time to play with the tool). Building on the standardized data access layer that Hasura already enables, PromptQL is a data access agent that can understand your data schema, and then generates a query plan to access your data and perform computations on the fly. PromptQL isn't just RAG across a vector space. It is querying data straight out of things like Postgres, or MongoDB, or even totally non-SQL sources like GitHub APIs. The ability to make an optimal query plan as opposed to following a strict retrieval pipeline is what makes PromptQL truly powerful.&lt;/p&gt;

&lt;p&gt;Back to our LEGO data. The schema is pretty simple, just a lot of data organized into meaningful relationships.&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%2Fmqx2r6kr21h0lj21h79n.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%2Fmqx2r6kr21h0lj21h79n.png" alt="This database schema organizes LEGO-style inventories by tracking sets, parts, and minifigures through interconnected tables that manage everything from individual pieces and their colors to complete sets and themes, with relationships defined between all components." width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wrote a simple script to &lt;a href="https://gist.github.com/shortjared/9d95586e0de1603407e91dac7b05e8e5" rel="noopener noreferrer"&gt;download everything from the Rebrickable website and dump it into a Postgres database&lt;/a&gt;. Should be fairly simple to grab and run yourself if you want to do your own tinkering.&lt;/p&gt;

&lt;h1&gt;
  
  
  It's all about the Prompt(QL)
&lt;/h1&gt;

&lt;p&gt;Once I had that done, I set up PromptQL to point to the database. The &lt;a href="https://shortclick.link/gfdgdm" rel="noopener noreferrer"&gt;PromptQL docs are pretty good at explaining this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once done, I fired up the PromptQL interface, and if you have used any chat interface (or ChatGPT like app), you'll feel right at home.&lt;/p&gt;

&lt;p&gt;For example, I wonder how many of those Star Wars sets have that Light Bluish Gray piece?&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%2F8lny0lppzj3zyydh3cjo.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%2F8lny0lppzj3zyydh3cjo.png" alt="Screenshot of a database query interface showing Star Wars LEGO set analysis. The interface displays that out of 970 total Star Wars sets, 709 contain Light Bluish Gray pieces. The dark-themed interface includes a query plan, results table, and detailed analysis of the findings." width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So how do I know I can even trust these results? Simple. I don't have to. Every single thing the Agentic actions of PromptQL are doing is backed by code that it shows me.&lt;/p&gt;

&lt;p&gt;The question "How many different colors are there?" replies with the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To answer your question about the number of different colors, I'll need to query the Colors table in the database. Let me fetch that information for you.&lt;br&gt;
Query Plan&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query the Colors table to count the number of distinct colors.&lt;/li&gt;
&lt;li&gt;Retrieve the total count of colors.&lt;/li&gt;
&lt;li&gt;Store the result in an artifact for easy reference.&lt;/li&gt;
&lt;li&gt;Present the information to the user.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;It then writes and executes the following 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="c1"&gt;# Query to count the number of distinct colors
&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
SELECT COUNT(DISTINCT id) as color_count
FROM app.Colors
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="c1"&gt;# Execute the query
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Extract the color count
&lt;/span&gt;&lt;span class="n"&gt;color_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;color_count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Store the result in an artifact
&lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store_artifact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;color_count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Total Number of LEGO Colors&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;table&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Total Colors&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color_count&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Print the result for verification
&lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total number of colors: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;color_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Output:&lt;br&gt;
SQL statement returned 1 rows.&lt;br&gt;
Stored table artifact: identifier = 'color_count', title = 'Total Number of LEGO Colors', number of rows = 1, sample rows = [{'Total Colors': 267}]&lt;br&gt;
Total number of colors: 267&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't have to trust whatever it spins out of some semantic search and RAG. I can look at the actual SQL it put together, and go look at the actual data sources if I need to confirm things. I have a habit of checking the executed code blocks fairly often to sanity check things. This level of transparency is critical, in my opinion, towards debugging Agentic applications.&lt;/p&gt;

&lt;p&gt;One of my favorite things to do, because it has knowledge of your schema, you can ask PromptQL what kinds of questions you should even be asking. For the LEGO data I have, it suggested trying the following things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Historical evolution of LEGO sets, tracking how piece counts and complexity have changed across decades
- Analysis of rare minifigures and unique parts that only appear in a single set
- Comparison between licensed themes (Star Wars, Harry Potter) and original LEGO themes in terms of popularity and complexity
- Distribution patterns of colors across sets, particularly looking at sets with the most diverse color palettes
- Deep dive into the most epic sets ever produced, ranked by piece count and build complexity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I actually tried the first question, PromptQL yielded some interesting bullet points.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Piece count explosion: From just 18.5 pieces per set (1950s) to 363.3 pieces (2020s) - a massive 1,863.8% increase&lt;/li&gt;
&lt;li&gt;Color variety: Sets went from using 2.7 colors (1950s) to 13.9 colors (2020s) - a 414.8% increase&lt;/li&gt;
&lt;li&gt;Production peak: The 2010s saw the highest number of set releases with 6,074 different sets&lt;/li&gt;
&lt;li&gt;Temporary decline: The 1980s showed an unusual dip in both piece count and color complexity compared to the 1970s&lt;/li&gt;
&lt;li&gt;Recent acceleration: The most dramatic increases in both metrics occurred in the 2010s and 2020s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You also aren't just strictly limited to questions answerable directly within the data. For instance, take things like GitHub issues. You can still leverage a typical LLM use case and ask for PromptQL to categorize things by say, the GitHub issue content. Ask for it to sort things into various priorities (high, medium, low) or different types of issue (support, bug, feature). PromptQL will then combine pulling stuff from your sources and then using an LLM to classify them and use those results in your output artifacts or additional steps.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cool, but what can it do? (Real-world applications beyond plastic bricks)
&lt;/h1&gt;

&lt;p&gt;I've actually played with hooking this up to GitHub issues, and some other data sources. While it has its rough edges, I've been able to accomplish all kinds of interesting things. Like asking PromptQL to identify areas to focus on for improving the project using the actual content from the GitHub issues. Or wiring it up to a support ticket system and billing data, and asking PromptQL to identify the customers that are at risk of churning and help me write customized emails to each of them.&lt;/p&gt;

&lt;p&gt;I will mention, sometimes it gets specific SQL features or syntax support wrong for your data source. Other times it may time out on particularly huge or long running queries. However, a very neat feature is PromptQL will catch these issues, and with a feedback loop, it will attempt to self correct itself, which is pretty cool to witness. Other times, gentle steering by you can work around such issues.&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%2Feqo3mqbn1moe973r7qeb.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%2Feqo3mqbn1moe973r7qeb.png" alt="Screenshot showing PromptQL's self-correction process in two panels. The top panel shows an initial query plan with multiple steps that resulted in a timeout error. The bottom panel shows PromptQL's automated response, where it recognizes the performance issue and proposes a more efficient approach by combining multiple queries into a single SQL statement. Both panels use a dark theme interface with blue accents for executed status and orange for error messages." width="738" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being able to understand what and why the Agent is behaving as it does are incredibly useful. Just as helpful is having what likens to a second set of eyes to help you along as you ask questions and explore, not to mention almost an expert level of SQL relevant to your schema.&lt;/p&gt;

&lt;p&gt;Just like LEGO bricks, I've found PromptQL gives me the building blocks to construct meaning from data. Whether you're analyzing plastic bricks or production metrics, having a tool that shows its work and adapts to your schema is invaluable. I'd encourage anyone interested in LLMs, RAG, or Agentic flows to &lt;a href="https://shortclick.link/geq3ew" rel="noopener noreferrer"&gt;give it a try&lt;/a&gt;. And hey, even if you're just someone wanting to explore a dataset or thinking about giving your users a more natural way to ask questions about their data, you might find yourself building something pretty useful with PromptQL!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>database</category>
      <category>datascience</category>
    </item>
    <item>
      <title>3 Guiding Principles for Building New SaaS Products on AWS</title>
      <dc:creator>Jared Short</dc:creator>
      <pubDate>Tue, 05 May 2020 17:04:31 +0000</pubDate>
      <link>https://dev.to/trek10inc/3-guiding-principles-for-building-new-saas-products-on-aws-ie8</link>
      <guid>https://dev.to/trek10inc/3-guiding-principles-for-building-new-saas-products-on-aws-ie8</guid>
      <description>&lt;p&gt;I won't start this article saying there is "one true-way" for building SaaS on cloud providers, specifically AWS. I will confidently say that there are many wrong ways. At Trek10, we find ourselves helping clients that have seen their AWS usage skyrocket and need to organize the chaos of an organic, home-grown crop of AWS Cloud. This article is distilled from years of working with folks at various points in their AWS journey in an effort to help guide you away from the wrong ways, and towards a successful path.  &lt;/p&gt;

&lt;p&gt;You know the story, and you may even have a part in the story yourself. A company is pivoting to the Software-as-a-Service model to modernize its offerings. Someone at the top hears that the cloud "accelerates the pace of innovation" and proclaims, "we must get on board or get left behind; this company won't be the laggards of the adoption curve!" If you are really on the cutting edge, an engineer or two has heard about this new "serverless" thing and is just plain tired of ssh-ing in and patching their fleet of "totally-automated everything away boss" EC2 instances.&lt;/p&gt;

&lt;p&gt;Based on what I have seen and learned over the years, and some discussion with trusted colleagues, I want to talk about how I would (and in fact have) set up new organizations (products, SaaS, whathaveyou) from day one for future success. Here are a few guiding beacons to help you make the right long-term decisions.  &lt;/p&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  #1 Build as if you may sell at any time
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//www.trek10.com/assets/_standardImage/sell.jpg?mtime=20200501114830&amp;amp;focal=none#asset:102593:transform:standardImage" class="article-body-image-wrapper"&gt;&lt;img src="//www.trek10.com/assets/_standardImage/sell.jpg?mtime=20200501114830&amp;amp;focal=none#asset:102593:transform:standardImage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This may seem silly, especially if your organization has been around for a century. However, in new products and old companies alike the approach of building as if you may sell at any time has a similar effect; it forces you to build with best practices and isolation.&lt;/p&gt;

&lt;p&gt;To achieve this goal, I’d lean on a few well known best practices, and perhaps some lesser-known tooling.&lt;/p&gt;

&lt;h4&gt;
  
  
  AWS Accounts are the ultimate unit of isolation
&lt;/h4&gt;

&lt;p&gt;With the introduction of &lt;a href="https://aws.amazon.com/organizations/" rel="noopener noreferrer"&gt;AWS Organizations&lt;/a&gt;, AWS made it clear that the multi-account strategy is the cut path in the deep jungles of account management. Stack on Control Tower and various &lt;a href="https://aws.amazon.com/solutions/aws-landing-zone/" rel="noopener noreferrer"&gt;Landing Zone&lt;/a&gt; offerings and you can rest assured that in this day and age you can easily leverage accounts for isolation in a practiced manner in even your most trivial projects.&lt;/p&gt;

&lt;p&gt;These days, one of my favorite tools to orchestrate things is nothing more than a thin wrapper around native AWS tooling is &lt;a href="https://github.com/OlafConijn/AwsOrganizationFormation" rel="noopener noreferrer"&gt;AWS OrganizationFormation&lt;/a&gt;. OrgFormation works with a few simple tagging and logic schemes to set up accounts and deploy/maintain CloudFormation templates across them.&lt;/p&gt;

&lt;p&gt;AWS Organizations also simplifies your technical auditing needs by centralizing AWS Config and GuardDuty.&lt;/p&gt;

&lt;p&gt;The AWS account is also one of the most effective tools in your kit for blast radius limiting.&lt;/p&gt;

&lt;p&gt;Finally, AWS Organizations centralizes your billing. Any sufficiently large cloud operation ends up &lt;a href="https://www.duckbillgroup.com/about/" rel="noopener noreferrer"&gt;requiring a Cloud Economist&lt;/a&gt; to make any sense of the madness, but with Organizations you can pretend to keep things under control for a while with the centralized billing as well as some simple alarms or dashboards.&lt;/p&gt;

&lt;p&gt;A basic organization setup looks something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="//www.trek10.com/assets/_standardImage/account-arch.jpg?mtime=20200501114824&amp;amp;focal=none#asset:102589:transform:standardImage" class="article-body-image-wrapper"&gt;&lt;img src="//www.trek10.com/assets/_standardImage/account-arch.jpg?mtime=20200501114824&amp;amp;focal=none#asset:102589:transform:standardImage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following this structure, you will note that each of the “products” is actually distributed into its own “organization unit” and each environment is broken up into its own AWS account. Billing, CI/CD and even Security &amp;amp; Auditing are centralized which can help maintain insight across the company.&lt;/p&gt;

&lt;h4&gt;
  
  
  Build for the cloud, in the cloud
&lt;/h4&gt;

&lt;p&gt;This one may seem a bit odd, but let’s take a direct quote from my friend &lt;a href="https://twitter.com/ben11kehoe" rel="noopener noreferrer"&gt;Ben Kehoe&lt;/a&gt;, Cloud Robotics Research Scientist and AWS Community Hero.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Move your development environment towards the cloud, do not try to move the cloud down to your dev environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are an &lt;a href="https://localstack.cloud/" rel="noopener noreferrer"&gt;undeniable&lt;/a&gt; amount of &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-sam-local-beta-build-and-test-serverless-applications-locally/" rel="noopener noreferrer"&gt;tools&lt;/a&gt; and &lt;a href="https://www.serverless.com/framework/docs/providers/aws/cli-reference/invoke-local/" rel="noopener noreferrer"&gt;initiatives&lt;/a&gt; to bring familiar development cycles to local developer environments. Some are better than others. Some achieve more parity than others. As good as some of these are, inevitably you end up in a situation where something is not &lt;em&gt;quite&lt;/em&gt; perfect and you spend days or weeks trying to work around that issue for your team. Even worse, you spend cycles debugging when something isn't quite right in your deployed cloud version.&lt;/p&gt;

&lt;p&gt;These days, I personally do the &lt;a href="https://www.trek10.com/blog/i-buy-a-new-work-machine-everyday" rel="noopener noreferrer"&gt;majority of my development on AWS Cloud9&lt;/a&gt;. The fast, reliable internet on the box and what I'd call "good enough" feature set and language support are truly sufficient about 90% of the time.&lt;/p&gt;

&lt;p&gt;In addition to Cloud9, I would also highly suggest either a shared "developer" AWS Sandbox account, or for more mature organizations a Sandbox per developer. If you are feeling extremely ambitious, maybe even &lt;a href="https://github.com/iann0036/aws-account-controller" rel="noopener noreferrer"&gt;ephemeral AWS accounts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'd ask most developers to start their day in Cloud9, and only eject to their local machines if they really need to. I would expect that they are doing unit tests or simple mocks locally to get their tests into a place where they are rapid and valuable locally, but that their real development and testing is pushing to their sandbox AWS accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simply put, there is no sufficient simulation or substitute for actual cloud resources.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This also encourages and requires good practice around integration and end-to-end testing.  &lt;/p&gt;

&lt;h2&gt;
  
  
  #2 Build as if you may open-source at any time
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//www.trek10.com/assets/_standardImage/oepn-source.jpg?mtime=20200501114827&amp;amp;focal=none#asset:102591:transform:standardImage" class="article-body-image-wrapper"&gt;&lt;img src="//www.trek10.com/assets/_standardImage/oepn-source.jpg?mtime=20200501114827&amp;amp;focal=none#asset:102591:transform:standardImage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one is a bit more nuanced but let’s think about the decisions it forces us to make. We need to be a bit more introspective on our internal practices and scrutinize our codebase as if it is open to the world.&lt;/p&gt;

&lt;p&gt;This means that security by obscurity, while never a good practice, is a definite no-go. We can’t rely on people not knowing we moved our &lt;code&gt;admin&lt;/code&gt; endpoint to &lt;code&gt;/unfindable-except-by-everyone&lt;/code&gt;. It also means no secrets strewed about in code, and no cert files stored in the repository.&lt;/p&gt;

&lt;p&gt;Your project dependencies need to be regularly audited and remain up to date. You need to be able to roll-out new patched versions of your software soon after critical Common Vulnerabilities and Exposures (CVE) are released against those dependencies. &lt;strong&gt;The only way this is safe and feasible is with automated and comprehensive pipelines for deploying your code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is also the implied "embarrassment" angle. Sure, a little bit of sloppy coding is evident even in many open source projects. But knowing (or pretending) that some external force is looking over your shoulder may help you take the extra hour or two to properly pull out modules or add those tests you know should be done.  &lt;/p&gt;

&lt;p&gt;The final bit of advice that falls under this principle is documentation. An open-source project is only as good as its documentation. You could have the most elegant API in the world, but if there isn't enough documentation to communicate that point to new-comers there won't be anyone using your project. Good documentation extends beyond just your code or API. It encompasses your infrastructure practices, your guiding principles as a project, your on-boarding for new developers, and yes, your code and APIs.  &lt;/p&gt;

&lt;h2&gt;
  
  
  #3 Build with a cloud-native mindset
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//www.trek10.com/assets/_standardImage/cloud-native_200501_184826.jpg?mtime=20200501114826&amp;amp;focal=none#asset:102590:transform:standardImage" class="article-body-image-wrapper"&gt;&lt;img src="//www.trek10.com/assets/_standardImage/cloud-native_200501_184826.jpg?mtime=20200501114826&amp;amp;focal=none#asset:102590:transform:standardImage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look ma’ my biases are showing! Clearly, I am an advocate for building with the tools provided by my platform of choice.&lt;/p&gt;

&lt;p&gt;If AWS doesn't have it (or really, if CloudFormation doesn't support it) does it really exist? I get that this is a pretty aggressive, if not flippant, statement. However, any time I stray from a platform-native offering it's a reasonably high likelihood that I will regret it.  &lt;/p&gt;

&lt;p&gt;This also means leaning heavily into all the service offerings and orchestration tooling that is afforded to you by your platform. Don't be afraid to set some boundaries for your teams, but don't dogmatically enforce them.  &lt;/p&gt;

&lt;p&gt;As &lt;a href="https://twitter.com/alexbdebrie" rel="noopener noreferrer"&gt;Alex DeBrie&lt;/a&gt; put it for me...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Provide standards but allow experimentation. AWS is a broad ecosystem, and there are some holes. Your company will choose some services and patterns that you prefer and others that you don't. Help your engineers understand what the preferred and supported patterns are. Make it clear that they can go off those paths, but they're going to be more on their own. This is akin to &lt;a href="https://charity.wtf/2018/12/02/software-sprawl-the-golden-path-and-scaling-teams-with-agency/" rel="noopener noreferrer"&gt;Charity's 'Golden Path' approach&lt;/a&gt;, but it's not really AWS specific.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To expand, there are certainly cases where staying within the platform isn't the most optimal solution. There are cases where the market is a few steps ahead of your platform provider. For instance, AWS doesn't have anything quite as tuned to fast frontend search experiences like &lt;a href="http://algolia.com/" rel="noopener noreferrer"&gt;Algolia&lt;/a&gt;. But the point remains that going outside of the platform should be an exception and something you do only when truly needed. While going out to the market isn't my first option, but it definitely fits with how I think about serverless still in 2020.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1100887501047132160-919" src="https://platform.twitter.com/embed/Tweet.html?id=1100887501047132160"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1100887501047132160-919');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1100887501047132160&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Now, going out to the market and buying something doesn't always work out great. But my regret is lower than if I tried to build something in its place. To illustrate, I'll lean on my good friend &lt;a href="https://twitter.com/forrestbrazeal" rel="noopener noreferrer"&gt;Forrest Brazeal&lt;/a&gt; for the next thousand words.&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%2F03l7o8b4ukklh9chgh7l.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03l7o8b4ukklh9chgh7l.jpg" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Infrastructure as Code is one of the few "no compromise" issues
&lt;/h4&gt;

&lt;p&gt;While writing for this piece, I reached out to 5 or so trusted folks to ask their guidance for organizations building on the cloud. Every single person explicitly stated that Infrastructure as Code (IaC) is an essential piece of the puzzle.&lt;/p&gt;

&lt;p&gt;If you are not managing your infrastructure via CloudFormation, Terraform, or any of the other myriad of ways to model and deploy your infrastructure in repeatable ways you will eventually come to regret it. Whether you pay for it via needing to painstakingly rebuild a new environment as your application grows, or if you forget to check a box during a 3 hour manual deployment from staging to production and inexplicably bring down your application for a couple of harrowing hours.  &lt;/p&gt;

&lt;p&gt;If you take nothing else away from this post, please let it be that IaC is a cornerstone of a health product lifecycle on cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't take my word for it
&lt;/h2&gt;

&lt;p&gt;These 3 "guiding principles" are just that. Guidelines. They are not tenets that followed dogmatically guarantee success. They are in fact generalized snippets boiled out of some years of working with clients and on various AWS projects.&lt;/p&gt;

&lt;p&gt;Evaluate every technology decision as a long term decision of partnership. Sure, you are billed by the second/day/month, but really you are electing to take on that technology as a partner in the growth of your SaaS. Some decisions are easier replaced than others, some choices you outgrow. That's natural and expected. In the same vein, don't evaluate your past choices in the current context. You will always know more now than you did then.  &lt;/p&gt;

&lt;p&gt;When it comes to those decisions, give your team authority and power to build and innovate. Don't shy away from the strengths of the cloud to augment your teams, and don't shy away from trusting your teams.&lt;/p&gt;

&lt;p&gt;As &lt;a href="https://twitter.com/rchrdbyd" rel="noopener noreferrer"&gt;Richard Boyd&lt;/a&gt; so eloquently put it for me...  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Push authority down (don't push the CEO down a flight of stairs). A Software team that is responsible for the operational requirements of their application will drive the ops burden down. Typically this is done at an organizational level because Dev and Ops are separate teams/org. By forcing dev teams to own the ops of their applications, they have the responsibility (and the authority) to make changes in it that make the application more stable. &lt;em&gt;Note, this is true in many places but it much more pronounced in the cloud where most software runs as a service&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All said I'd build a new SaaS product without hesitation on AWS, and others should strongly consider it, but do so with a plan of action for your tech decisions and your teams.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>saas</category>
    </item>
    <item>
      <title>3 Big Things I Wish Someone had Told Me When I Started Using AWS</title>
      <dc:creator>Jared Short</dc:creator>
      <pubDate>Fri, 24 Apr 2020 17:17:56 +0000</pubDate>
      <link>https://dev.to/trek10inc/3-big-things-i-wish-someone-had-told-me-when-i-started-using-aws-2d0n</link>
      <guid>https://dev.to/trek10inc/3-big-things-i-wish-someone-had-told-me-when-i-started-using-aws-2d0n</guid>
      <description>&lt;p&gt;I've been working with AWS for the past 6 years or so. I've run into many highs and lows during the work. With emotions ranging from feeling like a mythic god, bending the cloud to my will, to debating seeking a job doing something with my hands far away from a computer.&lt;/p&gt;

&lt;p&gt;I introspected over the past years and asked a few co-workers and friends things that they wish they had known before they got started using AWS. I dug for more than "I wish I knew technology X better," because that is circumstantial and not all that helpful in many cases. Instead, view this compilation of observations by folks that have been in the cloud and things we think would have been nice to know.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS is a massive ecosystem. You can't understand it all, let alone be an expert in most of it.
&lt;/h1&gt;

&lt;p&gt;In the early days when we were building Trek10 as a company (an AWS consulting shop), we dabbled in helping folks with Azure and other clouds. We quickly realized these projects drained resources and mental capacity, and simply put, distracted us from our ultimate goals of being recognized serverless and AWS experts. We decided to hyper-focus on our goals and reject all projects that didn't get us closer to that work.&lt;/p&gt;

&lt;p&gt;On a personal level, this holds true for individuals as well. I make an effort to read at least one AWS service's documentation from cover to cover once a week. Rough calculations suggest I have spent 500+ hours just reading AWS service documentation (There are &lt;a href="https://github.com/awsdocs" rel="noopener noreferrer"&gt;221 doc repos&lt;/a&gt; on the official GitHub).  I regularly reach out to AWS folks and colleagues for help understanding interactions between services, implications of a design choice, or even just an overview or refresher of a feature.&lt;/p&gt;

&lt;p&gt;So don't take it personally on day 1 when nothing makes sense. This stuff is hard. My general suggestion is to have an end goal of what you want to build and break it into pieces you think are solvable by various services. Pick one service, read the docs cover to cover, and create that part to get your hit of endorphins. Then, wire that thing to a second service in the same manner.&lt;/p&gt;

&lt;p&gt;Consider your knowledge base of AWS a living source. As features are added and best practices evolved, even what you knew a year ago may soon be obsolete. It's not bad, just a natural consequence of the cloud platform. Saying "I don't know, but I can find out" isn't a shameful thing, even to a client or boss.&lt;/p&gt;

&lt;h1&gt;
  
  
  Understanding a service's use-cases is very different than understanding the service.
&lt;/h1&gt;

&lt;p&gt;I can tell you what most of the services on AWS do at a decent level of detail. Given a use case, I could probably rough out a couple of options for architectures pretty quickly. What I can't tell you is, based on exact needs, what problems you may run into with all of the service options that may fit. To do that, I would need to research service limits and other tidbits that are scattered around AWS documentation (notice a theme here yet?).&lt;/p&gt;

&lt;p&gt;AWS Services have limits, both service limits and other functional restrictions. Usually they are quite reasonable, but other times, they will make your life genuinely miserable. Especially if your system needs to scale or evolve if you didn't consider things ahead of time.&lt;/p&gt;

&lt;p&gt;For example, a real-world use case. Let's say you want to process a queue of transactions. We do some Googling and find that AWS has the Simple Queue Service (SQS). We look up limits and see we there can be 120,000 inflight messages by default and practically unlimited stored and incoming messages. Great, queue solved. To process the queue we can hook things up to AWS Lambda. Lambda will handle processing messages as they come in, so no messages mean no cost, and we can scale up to high volume transactions per second without much consideration. &lt;/p&gt;

&lt;p&gt;Our system is humming along, but then we realize we are getting some problems in our backend during higher load times. Some numbers aren't adding up quite right. Ah, a race condition! Lambda and SQS do not guarantee order. We frantically search and find we can guarantee order with SQS FIFO (first in, first out). We are saved! Except... &lt;code&gt;FIFO queues don't support Lambda function triggers.&lt;/code&gt; That's a heck of a limitation to run into this late in the project.&lt;/p&gt;

&lt;p&gt;We are going to have to rearchitect or reconsider our whole set of service choices to address this problem (perhaps with Amazon Kinesis which can guarantee order and still works great with Lambda)! &lt;/p&gt;

&lt;p&gt;You are going to get caught on sharp edges when you are exploring and learning. Your best defense is to review and re-review the limitations of the services before you choose them and ask in-depth questions about future states. Do we need to guarantee the order of anything? What size of payloads might we need? How many transactions per second if we are wildly successful? Do we need searchability of the data?&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline bonus thing: AWS Improves around you
&lt;/h3&gt;

&lt;p&gt;The AWS savvy among you may have been shouting at your screen "AWS Lambda Support FIFO Queues!" and you would be absolutely right. &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/11/aws-lambda-supports-amazon-sqs-fifo-event-source/" rel="noopener noreferrer"&gt;It does &lt;em&gt;now&lt;/em&gt;&lt;/a&gt;. This wasn't the case a year ago. This brings me to the point that a proper solution today isn't the best solution tomorrow.&lt;/p&gt;

&lt;p&gt;One of the unique properties of AWS (or the cloud in general), is that the services may improve around you. DynamoDB and S3 have addressed numerous pitfalls like hotkeys, scaling, etc. You'll see performance increases as AWS iterates on the underlying architectures. I've had solutions out in AWS Land that just got more performant or started costing less just based on AWS changes without me having to do anything.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cost is really hard; leveraging the cloud is just as much about the business and people as it is the technology.
&lt;/h1&gt;

&lt;p&gt;There are a lot of arguments for and against the cloud on cost models. Nearly every case I have read or heard toys around on raw pricing comparisons. On one end we have, "An EC2 instance on AWS costs $$$ per year, hardware in my data center amortized costs $$ per year. You are silly if you move to the cloud." With the other end saying, "Lambda functions cost $ per year, and I can serve a billion transactions without needing to order new hardware ever."&lt;/p&gt;

&lt;p&gt;Most arguments don't dive into what it takes to build and maintain a data center, or staff folks in the data center or colo. What isn't talked about is the cost to retrain staff to leverage the cloud: the opportunity cost of pursuing cloud and retraining, or of building a data center and staffing developers to maintain internal systems just to keep the data center working.&lt;/p&gt;

&lt;p&gt;While some of these conversations are starting to happen, I think the thing I wish I had realized was that cloud cost is less about optimizing raw infrastructure cost and more about optimizing opportunity, maintenance, and people costs. This frame of mind would have made a lot of conversations much smoother early in my career.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://route53db.com" rel="noopener noreferrer"&gt;Corey Quinn&lt;/a&gt;, AWS Billing maverick explains it better than I can.&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1101616132681752576-864" src="https://platform.twitter.com/embed/Tweet.html?id=1101616132681752576"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1101616132681752576-864');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1101616132681752576&amp;amp;theme=dark"
  }



&lt;iframe class="tweet-embed" id="tweet-1101616133235339264-907" src="https://platform.twitter.com/embed/Tweet.html?id=1101616133235339264"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1101616133235339264-907');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1101616133235339264&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h1&gt;
  
  
  Speed round
&lt;/h1&gt;

&lt;p&gt;There was a lot to think about for this post. Here are some other things to consider.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always, always practice good AWS account hygiene. Separate AWS accounts for environments and projects to get a built-in security blast radius and more effective costing practices.&lt;/li&gt;
&lt;li&gt;AWS services sometimes aren't that practical or useful when they release, but they get progressively better. Check back on them as cases arise.&lt;/li&gt;
&lt;li&gt;It's usually cheaper to try stuff out than do a ton of research, Proof-of-Concept early and often. Do it in a new account and with infrastructure as code so you can throw away the environment when you are done without losing any work.&lt;/li&gt;
&lt;li&gt;There will be regional outages of services. This doesn't mean AWS is less available or stable than your data center. Build with these outages in mind. Review the &lt;a href="https://aws.amazon.com/architecture/well-architected/" rel="noopener noreferrer"&gt;AWS Well-Architected Framework&lt;/a&gt;. It will help you make better decisions from day 1.&lt;/li&gt;
&lt;li&gt;Moving to cloud from traditional data centers is usually easier for developers than previously infrastructure and ops folks. Have a plan to turn your people into cloud natives, not just your services.&lt;/li&gt;
&lt;li&gt;"Lift and Shift" is never as smart or short-term of a strategy as you want to believe.&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%2Fndgj5xvfirs2rq6h0hoo.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%2Fndgj5xvfirs2rq6h0hoo.png" alt="Lift and Shift - FaaS and Furious Comic" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;
From &lt;a href="https://faasandfurious.com/33" rel="noopener noreferrer"&gt;FaaS and Furious&lt;/a&gt; by Forrest Brazeal



&lt;p&gt;I imagine there is a lot more someone could be told, more that I wish I had been told, but I can only hope to save someone a few troubles or moments of worry as they start their cloud journey. I'm always available on Twitter or in the comments to help!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>An Intro to the Power of Serverless with AWS Amplify</title>
      <dc:creator>Jared Short</dc:creator>
      <pubDate>Wed, 29 May 2019 14:41:56 +0000</pubDate>
      <link>https://dev.to/trek10inc/build-a-real-time-serverless-visitor-counter-with-aws-amplify-1c0l</link>
      <guid>https://dev.to/trek10inc/build-a-real-time-serverless-visitor-counter-with-aws-amplify-1c0l</guid>
      <description>&lt;h1&gt;
  
  
  A little story about serverless
&lt;/h1&gt;

&lt;p&gt;Since 2014, my day job has been helping folks build and maintain stuff on Amazon Web Services (AWS). I've helped organizations from the most niche startups to household names on everything from real-time inventory management to machine learning on pizzas and everything in between.&lt;/p&gt;

&lt;p&gt;I've seen containers get popular. I've seen the word Serverless get used so much that I am not even sure what it means anymore. One thing does remain for sure though, the real world Serverless systems I've been part of creating that have handled billions of transactions are some of the most pleasant to maintain and operate of any I've seen in my career.&lt;/p&gt;

&lt;p&gt;So why do I love serverless, why does it matter? Y'all remember the Spectre/Meltdown insanity in early 2018 where chip level vulnerabilities were discovered, and everyone was freaking out and scrambling to fix it? The serverless environments I operated were patched before most orgs even had the conference rooms booked to build the response plan.&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%2Fqe5uzzwjjnfg2hxbse0i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqe5uzzwjjnfg2hxbse0i.gif" alt="Sleeping Spongebob" width="499" height="408"&gt;&lt;/a&gt;&lt;/p&gt;
Here is a real depection of myself and the Trek10 team patching our Clients' serverless environments the night of the Spectre/Meltdown.



&lt;p&gt;Some of my most fun tickets and replies of all time were along the lines of "Hello, we are from Company X's security team, and we need a full plan for maintenance and audits for the environment you maintain for us ASAP to address the recent security issues." Nothing like saying "already handled" with a link to the AWS patch notes completed before the vulnerability was even public.&lt;/p&gt;

&lt;p&gt;At the end of the day, you want to deliver business value. You, too, can leverage the operations of the best and brightest in computing for pennies on the dollar by using serverless practices. Instead of worrying about server patching, networking, ssh keys, and the ilk, you get to focus on delivering your core value.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tools of the Trade
&lt;/h1&gt;

&lt;p&gt;There are a lot of options out there today from the &lt;a href="https://serverless.com/" rel="noopener noreferrer"&gt;Serverless Framework&lt;/a&gt; to &lt;a href="https://github.com/apex/up" rel="noopener noreferrer"&gt;Apex Up&lt;/a&gt; and many other providers and frameworks (many of them focusing on a niche use-case or language).&lt;/p&gt;

&lt;p&gt;Most of my new projects kick off with AWS Amplify CLI these days. AWS Amplify CLI is somewhat of a wrapper on the complexities of the cloud, offering opinionated solutions while still offering customizability where you need.&lt;/p&gt;

&lt;p&gt;Make sure you have Node.js 8.11.x or later and an AWS Account (don't worry about configuring anything yet, we simply need the account), and away we go.&lt;/p&gt;

&lt;p&gt;Our project today is going to be a blast from the past, with a little twist. Remember those small "visitor counter" badges everyone used to have at the bottom of their website, usually right next to an animated gif of their countries flag?&lt;/p&gt;

&lt;p&gt;Let's do that, but better... we are going to make it &lt;strong&gt;real-time&lt;/strong&gt;™!&lt;/p&gt;

&lt;p&gt;Start by installing the Amplify CLI with &lt;code&gt;npm install -g @aws-amplify/cli&lt;/code&gt; and running &lt;code&gt;amplify configure&lt;/code&gt; in your terminal. You'll be walked through various steps in a combination of your terminal and browser window, at the end of which you will have created and configured a new IAM user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install -g @aws-amplify/cli
$ amplify configure
Follow these steps to set up access to your AWS account:

Sign in to your AWS administrator account:
https://console.aws.amazon.com/
Press Enter to continue

Specify the AWS Region
? region:  us-east-1
Specify the username of the new IAM user:
? user name:  captain-counter
Complete the user creation using the AWS console
https://console.aws.amazon.com/iam/home?region=undefined#/users$new?step=final&amp;amp;accessKey&amp;amp;userNames=captain-counter&amp;amp;permissionType=policies&amp;amp;policies=arn:aws:iam::aws:policy%2FAdministratorAccess
Press Enter to continue

Enter the access key of the newly created user:
? accessKeyId:  AKIAWTXIHO**********
? secretAccessKey:  AfGA3kTlGyv6F0GMyzQS********************
This would update/create the AWS Profile in your local machine
? Profile Name:  captain-counter

Successfully set up the new user.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's set up our project directory and initialize our amplify app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir counter &amp;amp;&amp;amp; cd counter
$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project counter
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using none
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  npm run-script build
? Start Command: npm run-script start
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use captain-counter
⠙ Initializing project in the cloud...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These steps (and mostly using default answers) will end up with a base amplify app running in your AWS account. It doesn't do much now, but let's keep coding. Do note; I opted not to use any particular framework. I want to keep this &lt;em&gt;extremely&lt;/em&gt; lightweight since we'll be loading the script in other websites and there is no need for a framework where we are going.&lt;/p&gt;

&lt;h1&gt;
  
  
  Adding the API
&lt;/h1&gt;

&lt;p&gt;Our little project wouldn't be very successful if we didn't have a way to track hits. We are going to leverage a GraphQL API with a service called AWS AppSync. AppSync is a fully managed GraphQL solution that allows you to quickly and easily map to various data sources, I've personally used it for many things, and it's everything it says on the label and then some.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: counter
? Choose an authorization type for the API API key
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? No
? Provide a custom type name Counter
Creating a base schema for you...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, now let's open up &lt;code&gt;amplify/backend/api/counter/schema.graphql&lt;/code&gt; and change the schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;model&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&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;Now for the fun part, let's deploy our API. Behind the scenes, Amplify compiles your schema to various queries and mutations, updates your CloudFormation templates to manage all the resources you need for your API, code generates a small client to access your API, and finally deploys everything via CloudFormation to your AWS account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ amplify push

Current Environment: dev

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Api      | counter       | Create    | awscloudformation |
? Are you sure you want to continue? Yes

GraphQL schema compiled successfully.
Edit your schema at /Users/jshort/Work/counter/amplify/backend/api/counter/schema.graphql or place .graphql files in a directory at /Users/jshort/Work/counter/amplify/backend/api/counter/schema
? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
⠦ Updating resources in the cloud. This may take a few minutes...

....sometime later

GraphQL endpoint: https://ol2t5s4qlvbidcx2mwmigeya4m.appsync-api.us-east-1.amazonaws.com/graphql
GraphQL API KEY: da2-rbk5t2xpuvevlm6qza4onbly7m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the end of the process, you'll get a GraphQL endpoint and API key. You can use these to start playing with and exploring your API immediately. Using something like GraphQL Playground or Insomnia may be the fastest way to play around with things.&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%2Fl3msbia4wej0rsi0lnds.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%2Fl3msbia4wej0rsi0lnds.png" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;
Example configured GraphQL Playground, note the HTTP headers for authorization and all the pre-generated CRUD operations



&lt;p&gt;If you check out your API, you will notice a TON of pre-built functionality for normal CRUD operations (Create, Read, Update, Delete). For our use-case, we don't need any of it, and we are going to substitute our own.&lt;/p&gt;

&lt;p&gt;Change your &lt;code&gt;amplify/backend/api/counter/schema.graphql&lt;/code&gt; to reflect this more locked down schema. We are taking away nearly all of the CRUD operations, renaming some of the operations, and adding a filtered &lt;strong&gt;subscription&lt;/strong&gt; method. If you want to learn more about this, check out the &lt;a href="https://aws-amplify.github.io/docs/cli/graphql" rel="noopener noreferrer"&gt;AWS Amplify docs for GraphQL Transforms&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Subscription&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="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;aws_subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutations&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="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="err"&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;Behind the scenes, Amplify is managing a DynamoDB table for us. A managed NoSQL database that can handle a tremendous load (In my experience one of the best serverless databases). &lt;/p&gt;

&lt;p&gt;Next, We are going to customize our GraphQL resolver to take advantage of &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithItems.html#WorkingWithItems.AtomicCounters" rel="noopener noreferrer"&gt;atomic updates&lt;/a&gt; within Amazon DynamoDB, meaning that every "hit" mutation for a counter, we increment the "hits" column by adding 1.&lt;/p&gt;

&lt;p&gt;Amplify gives us a convenient way to override default resolver implementations with a &lt;code&gt;resolvers&lt;/code&gt; folder in &lt;code&gt;amplify/backend/api/counter/resolvers&lt;/code&gt;. Create a file call &lt;code&gt;Mutation.hit.req.vtl&lt;/code&gt; and pop in the &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-programming-guide.html" rel="noopener noreferrer"&gt;Velocity Template Language&lt;/a&gt; code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$util.qr($context.args.input.put("__typename", "Counter"))

#if( $util.isNullOrBlank($context.args.input.id) )
    $util.error("You MUST pass an `id` parameter")
#else

{
  "version": "2017-02-28",
  "operation": "UpdateItem",
  "key": {
      "id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id)
  },
  "update": {
    "expression": "SET #typename = :typename ADD hits :one",
    "expressionNames": {
        "#typename": "__typename"
    },
    "expressionValues": {
        ":one": $util.dynamodb.toDynamoDBJson(1),
        ":typename": $util.dynamodb.toDynamoDBJson("Counter")
    }
  }
}
#end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another quick &lt;code&gt;amplify push&lt;/code&gt; (go ahead and agree to the prompts) grab yourself a beverage of choice and come back to a shiny new API ready for us to use.&lt;/p&gt;

&lt;p&gt;Go ahead and try it out in your GraphQL Editor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&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="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;hits&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;And you should get a response similar to this.&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;"data"&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;"hit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;118&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;Awesome. Next order of business, make ourselves a little counter.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd src&lt;/code&gt; from your project root. You will see some other files and folder in there, like &lt;code&gt;aws-exports.js&lt;/code&gt; and &lt;code&gt;graphql&lt;/code&gt;. Create a new file called &lt;code&gt;package.json&lt;/code&gt; with the following content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"counter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Error: no test specified&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; exit 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;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"parcel index.html"&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;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&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;"parcel-bundler"&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.12.3"&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;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"aws-amplify"&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.1.28"&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;"browserslist"&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="s2"&gt;"last 2 Chrome versions"&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;Once you've got the file in place and saved run &lt;code&gt;npm install&lt;/code&gt;, this may take a few minutes. As part of this, we are installing the aws-amplify Javascript SDK, as well as &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt;, a zero-config bundler so we can bundle up our module and leverage the SDK as well as making it easy to develop on later.&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%2Fv0ovf3u0c3p8frbty7qg.jpeg" 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%2Fv0ovf3u0c3p8frbty7qg.jpeg" width="800" height="534"&gt;&lt;/a&gt;Don't give up now, we are almost there!&lt;/p&gt;

&lt;p&gt;Alright, the final two bits before the big payoff.&lt;/p&gt;

&lt;p&gt;First, create an &lt;code&gt;index.html&lt;/code&gt; file in &lt;code&gt;src&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Counter Widget&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-counter-id=&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/index.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need this for Parcel to hook in to. Please note, you could be doing most of this in React, Vue, Svelte, and using WebPack or whatever floats your boat. Amplify SDK was leveraged and the rest of the code was written to keep things simple and illustrate the power behind the scenes, I am not trying to proselytize any particular frontend approach.&lt;/p&gt;

&lt;p&gt;Finally, we have arrived at the big payoff. Let's create &lt;code&gt;index.js&lt;/code&gt; in &lt;code&gt;src&lt;/code&gt; as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphqlOperation&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-amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;awsmobile&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-exports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;awsmobile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;mutations&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./graphql/mutations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;subscriptions&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./graphql/subscriptions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/*
Find all the unique counter id on the page.
Send a single hit request for each unique ID.
Subscribe to all updates for each one as well.
*/&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createUpdateCounters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countersToUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-counter-id]`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counterHitIdSet&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;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;countersToUpdate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;counterHitIdSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;counterHitIdSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;hitCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/*
Send a mutation to your GraphQL to let it know we hit it.
This also means we get back the current count, including our own hit.
*/&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hitCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;graphqlOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="nf"&gt;updateText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;subscribeCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countersToUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-counter-id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;countersToUpdate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hits&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="cm"&gt;/*
Subscribe via WebSockets to all future updates for the counters
we have on this page.
*/&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;subscribeCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;graphqlOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// On dom loaded, kick things off&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&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;The comments contain most the hints about what is going on, but Amplify and GraphQL do a significant amount of the heavy lifting for us.&lt;/p&gt;

&lt;p&gt;Go ahead and run &lt;code&gt;npm start&lt;/code&gt; in the terminal and visit the URL it says your local dev server is started on. If everything works as expected, you should be able to see a counter after a brief &lt;code&gt;Loading...&lt;/code&gt; message. &lt;/p&gt;

&lt;p&gt;Open multiple tabs to see the counters increase as more hits accumulate, hit refresh once or twice to see that change propagate across all your open tabs in real-time.&lt;/p&gt;
Check out the little demo below, see how many visits have hit this page and codepen! Open it in a couple tabs and hit "rerun" button to watch real-time updates.



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/shortjared/embed/wbEBWw?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;So, we made it! There are a couple of important takeaways to think about here. What we've done is running on production ready, massively scalable services. This can easily scale to thousands of requests per second (after lifting some default limits). Our actual code written was quite minimal, allowing Amplify to do the heavy lifting, not to mention everything is actually "infrastructure as code" which means we can create whole &lt;a href="https://aws-amplify.github.io/docs/cli/multienv" rel="noopener noreferrer"&gt;new app instances and environments&lt;/a&gt; on demand.&lt;/p&gt;

&lt;p&gt;The mindset of serverless is all about offloading "undifferentiated heavy lifting", leveraging tools and services like AWS Amplify and AppSync get us closer to producing business value and further from managing infrastructure.&lt;/p&gt;

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