<?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: Trek10</title>
    <description>The latest articles on DEV Community by Trek10 (@trek10inc).</description>
    <link>https://dev.to/trek10inc</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%2Forganization%2Fprofile_image%2F780%2F88cc648e-b667-46ed-b34d-eccf83190dc1.png</url>
      <title>DEV Community: Trek10</title>
      <link>https://dev.to/trek10inc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trek10inc"/>
    <language>en</language>
    <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>What I Learned Starting My First Tech Job During a Global Pandemic</title>
      <dc:creator>Matt Skillman</dc:creator>
      <pubDate>Mon, 27 Apr 2020 15:52:54 +0000</pubDate>
      <link>https://dev.to/trek10inc/what-i-learned-starting-my-first-tech-job-during-a-global-pandemic-5hn9</link>
      <guid>https://dev.to/trek10inc/what-i-learned-starting-my-first-tech-job-during-a-global-pandemic-5hn9</guid>
      <description>&lt;p&gt;The pandemic that started in the US in early 2020 has necessitated the shift to remote work for those of us who can make this shift; I started my first tech job during this time period and after a few weeks in the office, I found myself working from home.&lt;/p&gt;

&lt;p&gt;In the past, I had considered working from home to be an impossibility, or, at best, something fundamentally inefficient and undesirable for both employer and employee. In truth, when it comes to the software development industry, remote work is an enabling factor, rather than being a hindrance.&lt;/p&gt;

&lt;p&gt;That all said, I have a few takeaways learned while starting a new job at Trek10 during this global pandemic.&lt;/p&gt;

&lt;h1&gt;
  
  
  Silence is golden, even when working from home and alone
&lt;/h1&gt;

&lt;p&gt;Coding requires at least a few hours of deep concentration daily and a remote work environment has enabled me to more easily meet this requirement. The essence of programming is the construction of very specific, logically coherent statements that must take into account the behavior and expectations of the system they reside within. This is exactly the sort of task that demands an individual’s full attention and focus.&lt;/p&gt;

&lt;p&gt;For me, working at home means that I can guarantee that, when necessary, I can silence all notifications on both my computer and phone and attend to my job with complete concentration. Once I have completed the most complex parts of whatever it is that I am working on, or whenever I need clarifications, I can open Slack and communicate with the appropriate people.&lt;/p&gt;

&lt;p&gt;In my previous workplaces, communicating with team members required a walk from one building to another or a series of phone calls; because of Slack and my current remote work environment, communication is far more efficient and sensible. Slack features channels intended for the distribution of information which concerns large groups of people. In my opinion, this feature trumps its real-world counterparts, which are intercom systems, cork boards with sheets of paper posted on them, or even worse, word-of-mouth propagation of business-critical knowledge. Additionally, ad hoc group discussions no longer require that several people physically relocate themselves just to have a three-minute conversation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Asking for help early and often is fine, it doesn't show weakness or ineptitude
&lt;/h1&gt;

&lt;p&gt;These new digital mediums reduce the need to delay conversations out of concern for the potential of interrupting the other party. If an engineer is presently occupied and wishes not to be interrupted, Slack offers a variety of features that allow her to signal to her teammates that she is busy and not currently available.&lt;/p&gt;

&lt;p&gt;Personally, it is rare for me to use these features which indicate unavailability for discussions, because, relative to the people I am working with, I lack experience and it is especially important that I communicate with them to accelerate my learning. &lt;/p&gt;

&lt;p&gt;I think the biggest challenges that a remote work environment could present to a new hire relate primarily to personality traits or work habits. In my case, I happen to be intrinsically motivated to do my job and attempt to learn from people more experienced than me, so working from home has not presented any issues. The reality of my situation is that I am a new hire and have much to learn, so it is important to be humble and ask questions when necessary. Because it is so easy to communicate through Slack, working remotely has also enabled me in this regard.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tribal knowledge still exists, learning where things are and how things work is still a necessary soft skill
&lt;/h1&gt;

&lt;p&gt;Despite all this, remote work environments are obviously not infallible; individuals do bear some responsibility in becoming comfortable with the workflow. From what I have experienced, it is important to understand where all the needed resources are located within your organization.&lt;/p&gt;

&lt;p&gt;In a purely physical work environment, an employee can visually see where everything is located. The only barriers are locked doors or campuses too large to easily navigate. In a completely digital work environment, the tools necessary to complete the job often take the form of various URLs, nested folders within a Google drive UI, etc.&lt;/p&gt;

&lt;p&gt;I have learned the importance of soft skills such as paying attention to the locations of these digital resources so that anything I need is within quick reach.&lt;/p&gt;

&lt;p&gt;I'm excited to continue learning and growing in my roles in tech, and even post-pandemic I am sure these learnings will serve me well.&lt;/p&gt;

</description>
      <category>career</category>
      <category>remote</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>CI/CD, AWS, and Serverless: 5 tips I learned the hard way</title>
      <dc:creator>Forrest Brazeal</dc:creator>
      <pubDate>Wed, 29 May 2019 18:42:16 +0000</pubDate>
      <link>https://dev.to/trek10inc/ci-cd-aws-and-serverless-5-tips-i-learned-the-hard-way-223p</link>
      <guid>https://dev.to/trek10inc/ci-cd-aws-and-serverless-5-tips-i-learned-the-hard-way-223p</guid>
      <description>&lt;p&gt;So you want to build a &lt;a href="https://aws.amazon.com/serverless/" rel="noopener noreferrer"&gt;serverless app on AWS&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Great! There are lots of cool posts that show you how to get started.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/trek10inc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F780%2F88cc648e-b667-46ed-b34d-eccf83190dc1.png" alt="Trek10" width="400" height="400"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F171486%2F36cde198-caaf-4fe4-8b99-d5bb178eb2d9.jpg" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/trek10inc/build-a-real-time-serverless-visitor-counter-with-aws-amplify-1c0l" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;An Intro to the Power of Serverless with AWS Amplify&lt;/h2&gt;
      &lt;h3&gt;Jared Short for Trek10 ・ May 29 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#graphql&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;But how do you get that app off your laptop and into the cloud? You could zip your code and upload it manually into the AWS console every time you change something, but that would get old pretty fast. &lt;/p&gt;

&lt;p&gt;What you probably want is some kind of CI/CD (continuous integration / continuous delivery) system, where code is automatically tested and released to your environment when you push changes to a source control repository.&lt;/p&gt;

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

&lt;p&gt;I've been building CI/CD pipelines for serverless on AWS for years, and I've picked up a few tricks along the way. Here are five tips I wish somebody had told me when I first got started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test code locally and services in the cloud
&lt;/h2&gt;

&lt;p&gt;There are several ways to run a Lambda function on your local developer machine -- for example, the Serverless Framework's &lt;a href="https://github.com/dherault/serverless-offline" rel="noopener noreferrer"&gt;serverless-offline&lt;/a&gt; plugin or the &lt;a href="https://github.com/awslabs/aws-sam-cli" rel="noopener noreferrer"&gt;AWS SAM CLI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This works great for quickly testing changes to your code. But a serverless app is a lot more than just code: permissions, service configurations, and other things can go wrong as you glue services together to build a truly cloud-native application. &lt;/p&gt;

&lt;p&gt;So local testing is not enough. The more external services you integrate with, the more you'll have to mock, and the more your tests will take on a tinge of unreality. You'll be testing a system that doesn't exist.&lt;/p&gt;

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

&lt;p&gt;Instead, my rule of thumb is to test code locally and services in the cloud. This might involve the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make an initial deployment in AWS that contains the definitions for services your code depends on (for example, deploying an underlying IAM role or a DynamoDB table). &lt;/li&gt;
&lt;li&gt;Run your Lambda function locally as you develop and test the code, reaching out to cloud services for your dependencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This way, you can quickly iterate on code, but if something breaks on the cloud side, you'll know! When your Lambda function works locally as expected, you can push it to the cloud as well, where you should...&lt;/p&gt;

&lt;h2&gt;
  
  
  Prioritize end-to-end functionality tests over unit tests
&lt;/h2&gt;

&lt;p&gt;As a general rule, we want to be writing &lt;em&gt;less&lt;/em&gt; code in the serverless world. It's quite possible to write a useful service in AWS using mostly configuration. For example, you can build a CRUD API using &lt;a href="https://aws.amazon.com/blogs/compute/using-amazon-api-gateway-as-a-proxy-for-dynamodb/" rel="noopener noreferrer"&gt;API Gateway and DynamoDB alone&lt;/a&gt; - no Lambda functions in the middle required.&lt;/p&gt;

&lt;p&gt;The more "serverless" you get, the less code you can usefully unit test, and the more you have to rely on tests of your &lt;em&gt;deployed infrastructure&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Lately I've been using &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; for end-to-end tests of serverless APIs. It's a Javascript test framework that works with assertion libraries like Jest and Chai. You can query your API directly, no inside knowledge of the code required. This is testing your service the way your users will experience it, so you can be sure your expectations match reality.&lt;/p&gt;

&lt;p&gt;If you want to test AWS resources directly, a cool project to check out is Erez Rokah's &lt;a href="https://github.com/erezrokah/aws-testing-library" rel="noopener noreferrer"&gt;AWS Testing Library&lt;/a&gt;, which lets you write tests directly against deployed resources like DynamoDB tables or SQS queues.&lt;/p&gt;

&lt;p&gt;If you're writing tests, of course, you'll need some place to run them. AWS provides a managed build service called CodeBuild that you should be paying attention to, because...&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS CodeBuild is hugely underrated
&lt;/h2&gt;

&lt;p&gt;Ever had to babysit a build server that was constantly crashing because one rogue job ate all the disk space? That's not a very "serverless" thing to do.&lt;/p&gt;

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

&lt;p&gt;Fortunately, AWS CodeBuild spins up a completely separate Docker-based build environment for every invocation of a build job. It's truly ephemeral compute, pay only for what you use, and it's pretty cheap -- 500 build minutes a month &lt;a href="https://aws.amazon.com/codebuild/pricing/" rel="noopener noreferrer"&gt;will run you a cool $2.00&lt;/a&gt;. The containers can take a few seconds to initialize, but that's way better than managing a pool of build runners that may flake out on you at any time.&lt;/p&gt;

&lt;p&gt;Once you've got CodeBuild in your corner, you should take a look at another AWS tool for developers, because ...&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS CodePipeline is the single best way to manage CloudFormation releases
&lt;/h2&gt;

&lt;p&gt;CloudFormation is AWS's infrastructure-as-config service: you create a template that defines your Lambda functions, IAM roles, etc. (The &lt;a href="https://aws.amazon.com/serverless/sam/" rel="noopener noreferrer"&gt;Serverless Application Model, or SAM&lt;/a&gt;, which makes it easier to define serverless apps on AWS, is just CloudFormation with a little special sauce.)&lt;/p&gt;

&lt;p&gt;Another annoying CI/CD thing I've had to do a lot is write scripts that poll AWS CloudFormation for updates. CodePipeline is really good at doing that for me. So I can trust it to manage my serverless infrastructure deployments as they roll from development, to staging, to production environments.&lt;/p&gt;

&lt;p&gt;None of these services come without gotchas, of course. The most important one I can warn you about right now is this:&lt;/p&gt;

&lt;h2&gt;
  
  
  CodePipeline only supports one branch per pipeline (at least for now)
&lt;/h2&gt;

&lt;p&gt;No doubt this will change someday, but at present you can trigger a CodePipeline from only one source, such as a repository branch. If you have branch-based workflows, this gets frustrating quickly. &lt;/p&gt;

&lt;p&gt;One workaround is to dynamically create a new CodePipeline per branch using a Lambda function, which has worked pretty well for us at Trek10 -- well enough that we recently &lt;a href="https://github.com/aws-quickstart/quickstart-trek10-serverless-enterprise-cicd" rel="noopener noreferrer"&gt;open-sourced the idea as an AWS Quickstart&lt;/a&gt;. That Quickstart contains the CloudFormation templates needed to spin up dynamic CodePipelines that include CodeBuild jobs, Lambda tests, and lots more -- so what are you waiting for? Give it a try, and hit me up in the comments if you have other questions.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cicd</category>
      <category>serverless</category>
      <category>devops</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>
