<?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>Forem: Anna</title>
    <description>The latest articles on Forem by Anna (@annaspies).</description>
    <link>https://forem.com/annaspies</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%2F114867%2Fb741231c-1c7f-4139-9cbc-d6223fd9675b.jpg</url>
      <title>Forem: Anna</title>
      <link>https://forem.com/annaspies</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/annaspies"/>
    <language>en</language>
    <item>
      <title>On Facing Extinction (Again)</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Fri, 19 Dec 2025 22:47:10 +0000</pubDate>
      <link>https://forem.com/annaspies/on-facing-extinction-again-23o5</link>
      <guid>https://forem.com/annaspies/on-facing-extinction-again-23o5</guid>
      <description>&lt;p&gt;This week I was cleaning up my old work laptop before I return it (oh yeah, I quit AWS 🎉), and I finally got around to reading one of the many open tabs that had been lingering in Brave for the past few months of re:Invent madness: &lt;a href="https://www.wreflection.com/p/ai-dial-up-era" rel="noopener noreferrer"&gt;AI's Dial-Up Era by Nowfal Khadar&lt;/a&gt;, which I found to be a well-reasoned and measured take on where we are in regards to AI - namely, in a similar place we were in the mid-90s with the internet as a whole. There was one particular parallel he drew that stuck with me, about how the journalism industry was disrupted by the ascension of the internet during that time.&lt;/p&gt;

&lt;p&gt;Most people I've worked with don't know this, but before I was a software engineer, I was a journalist. While journalism was something I fell into as a way to pay the rent while striving to be a writer, there was absolutely a part of me that idolized Hunter S. and Hemingway and the many others who long ago went deep to find truths and share them with the masses.&lt;/p&gt;

&lt;p&gt;Unfortunately, by the time I entered the industry in the mid-2000s, it was in the process of falling apart and transforming into its present, degraded state. There was no going deep and truth was now a tangential goal, replaced by SEO and appeasing Google rankings long enough to survive another day.&lt;/p&gt;

&lt;p&gt;So I left journalism and got into tech. This was around 2015, when startups were all the rage and unicorns were getting crowned left and right. I started learning to code online while working as a writer for a startup doing tech events, and eventually was creating content for startup founders while dreaming of becoming one myself.&lt;/p&gt;

&lt;p&gt;It actually took several more years (and a move across the world and a coding bootcamp) before I landed a software engineering role, but I was lucky enough to still catch the tail end of the developer heyday, something I had completely missed by the time I was working at a newspaper a decade before.&lt;/p&gt;

&lt;p&gt;So now we get to today; another decade has passed, and I'm facing the dilemma of being in yet another industry facing extinction. My fear is that just like with journalism, it will be to the detriment of society as a whole.&lt;/p&gt;

&lt;p&gt;This juxtaposition in particular stood out to me in Nowfal's piece, where he asks if there will be more or fewer software engineers with the rise of AI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To answer this question, go back to 1995 and ask the same question but with journalists. You might have predicted more journalists because the internet would create more demand by enabling you to reach the whole world. You’d be right for 10 or so years as employment in journalism grew until the early 2000s. But 30 years later, the number of newspapers and the number of journalists both have declined, even though more "journalism" happens than ever. Just not by people we call journalists. Bloggers, influencers, YouTubers, and newsletter writers do the work that traditional journalists used to do.&lt;/p&gt;

&lt;p&gt;The same pattern will play out with software engineers. We’ll see more people doing software engineering work and in a decade or so, what "software engineer" means will have transformed. Consider the restaurant owner from earlier who uses AI to create custom inventory software that is useful only for them. They won’t call themselves a software engineer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One aspect of this transformation he fails to mention is how much worse off we are because of this. Yes, democratization of information as a whole is a net positive, but in that process with journalism we lost expertise. Just as optimizing for SEO led to poorer writing quality (not to mention transforming a creative process into a soul-crushing one), optimizing for likes and subscribes has led to content that values sensationalism over truth. When "fact checker" is no longer a role that exists outside of a very few dying institutions, truth is no longer the outcome - and that was before AI started creating whole new realities.&lt;/p&gt;

&lt;p&gt;Newspapers used to have subject matter experts, fact checkers, embedded reporters, and probably another dozen roles I'd never heard of because they were gone by the time I got into the trade. Similarly, an engineering team has frontend and backend experts, devOps, appsec engineers, support engineers (ideally), UX, and so on - not to mention doc writers, developer advocates, etc., which you don't need if you're just building an app for your local restaurant, but presumably some people will still make software they want to sell. Will we get to a place where those experts only exist in dying institutions that we call FAANG today? Will the expectation of software actually working well and bugs being fixed be a thing of the past?&lt;/p&gt;

&lt;p&gt;Though there is a core difference between journalism and software's role in shaping a society, in that a free and truth-seeking press is essential to a functioning democracy, but apps are not. If anything, the last decade before AI has led to the &lt;a href="https://en.wikipedia.org/wiki/Enshittification" rel="noopener noreferrer"&gt;enshittification&lt;/a&gt; of much of the software we use daily, so perhaps democratization will create a bulwark against the monopolies that allow enshittification to take hold (one can dream).&lt;/p&gt;

&lt;p&gt;Or maybe that won't happen. Today, when anyone can create journalistic content, most people still consume it from others. I imagine once anyone can create software, most will still consume it from a smaller cohort of builders (though that cohort will probably be much larger than those of us employed as developers today). One thing I am pretty certain about is that the highly-paid, highly sought after senior engineer will become increasingly rare. Just as for every Christiane Amanpour there are millions of bloggers doing journalism at widely varying quality and for a lot less money, those highly-paid roles someone out of bootcamp could get a few years ago will become much rarer and require a lot more expertise.&lt;/p&gt;

&lt;p&gt;And we may still be in the middle of the transformation of journalism from what it was into something that once again seeks truth as its goal, and that transformation might just be helped by the democratization of software development.&lt;/p&gt;

&lt;p&gt;Either way, at least I'll be a little more ready for this disruption than I was twenty years ago.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>RIP CRA - Now what?</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Wed, 02 Jul 2025 02:17:13 +0000</pubDate>
      <link>https://forem.com/annaspies/rip-cra-now-what-1h9a</link>
      <guid>https://forem.com/annaspies/rip-cra-now-what-1h9a</guid>
      <description>&lt;p&gt;Maybe I'm strange, but I always feel a little sad when an app or framework that has been a regular part of my workflow goes to the great repository in the sky. That was certainly my initial reaction after hearing that Create React App (or CRA to its friends) was being &lt;a href="https://react.dev/blog/2025/02/14/sunsetting-create-react-app" rel="noopener noreferrer"&gt;deprecated&lt;/a&gt;. My second reaction: now what do I use?&lt;/p&gt;

&lt;p&gt;Luckily, the React team put up a &lt;a href="https://react.dev/learn/creating-a-react-app" rel="noopener noreferrer"&gt;list of possible CRA replacements&lt;/a&gt; as part of the deprecation announcement, and in this post I'm going to focus on the first option: Next.js. Why? IMO it offers the most features for going above and beyond just a simple migration. But we'll get into that later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing CRA with Next.js
&lt;/h2&gt;

&lt;p&gt;If you're new to Next.js, here's a quick breakdown of its 1:1 feature equivalents for CRA users, as well as potential server-side functionality:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;If you used this in CRA&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Client-side equivalent in Next.js&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Server-side possibility in Next.js&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx create-react-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx create-next-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npx create-next-app&lt;/code&gt; (same, supports both modes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--template typescript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx create-next-app --ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full TypeScript setup for both client and server components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react-router&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File-based routing in &lt;code&gt;pages/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;App Router with nested layouts and server components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react-helmet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;next/head&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dynamic &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; updates in server components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static file serving at &lt;code&gt;public/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Same - uses &lt;code&gt;public/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;next/image&lt;/code&gt; with built-in image optimization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fetch data in &lt;code&gt;useEffect&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;useSWR&lt;/code&gt; or &lt;code&gt;fetch()&lt;/code&gt; in a client component&lt;/td&gt;
&lt;td&gt;Fetch data directly in server component, RSC fetch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Component state with &lt;code&gt;useState&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;useState&lt;/code&gt; in client components (&lt;code&gt;'use client'&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Server-rendered props via RSC + cookies/session context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local dev server with Webpack&lt;/td&gt;
&lt;td&gt;Webpack (built-in)&lt;/td&gt;
&lt;td&gt;Webpack + incremental adoption of Turbopack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment variables in &lt;code&gt;.env&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Same, plus bundle for the browser with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix&lt;/td&gt;
&lt;td&gt;Same, plus runtime env vars via Docker image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BYO backend/server&lt;/td&gt;
&lt;td&gt;Requires separate Node server&lt;/td&gt;
&lt;td&gt;Built-in API routes (&lt;code&gt;/api/&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing with Jest&lt;/td&gt;
&lt;td&gt;User-installed Jest or Vitest&lt;/td&gt;
&lt;td&gt;User-installed Jest or Vitest (no native test runner)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS with plain files or modules&lt;/td&gt;
&lt;td&gt;Global CSS and CSS Modules supported, built-in code-splitting&lt;/td&gt;
&lt;td&gt;Full support for Tailwind, Sass, PostCSS, and scoped CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The initial setup and much of the configuration of Next.js will seem familiar to CRA users. Like CRA, Next.js has a one-line &lt;code&gt;init&lt;/code&gt; command: &lt;code&gt;npx create-next-app&lt;/code&gt;. For TypeScript users, adding the &lt;code&gt;--ts&lt;/code&gt; flag means your project is automatically configured for TypeScript, much like the &lt;code&gt;--template typescript&lt;/code&gt; flag for CRA. Both frameworks ship with Webpack, but Next.js also includes &lt;a href="https://nextjs.org/docs/app/api-reference/turbopack" rel="noopener noreferrer"&gt;Turbopack&lt;/a&gt;, a newer and in most cases more performant bundler from the Next.js team. For testing, CRA uses Jest under the hood, while Next.js leaves it up to the user to choose a testing library.&lt;/p&gt;

&lt;p&gt;The main differences between the two frameworks mainly come down to the features Next.js offers that aren't available in CRA. These can be divided into two categories: performance improvements and developer experience (DX) improvements.&lt;/p&gt;

&lt;p&gt;Performance improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-Side Rendering (SSR) for faster initial loads&lt;/li&gt;
&lt;li&gt;Image optimization for responsive, lazy-loaded images&lt;/li&gt;
&lt;li&gt;Built-in code splitting by default at the page level&lt;/li&gt;
&lt;li&gt;Edge and serverless function support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DX improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File-based routing, so no need to manually configure routes&lt;/li&gt;
&lt;li&gt;API routes - build backend logic right in your app&lt;/li&gt;
&lt;li&gt;Built-in support for layouts and nested routing in the App Router&lt;/li&gt;
&lt;li&gt;Next.js Middleware makes A/B testing, i18n, etc. much easier to implement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we've seen how Next.js compares feature-for-feature with CRA, the next question is: why make the switch at all?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why migrate?
&lt;/h2&gt;

&lt;p&gt;If you have a legacy app or side project that hasn't been updated in years, you may be wondering why bother migrating if the CRA setup is working? For deployed apps, the answer is vulnerabilities: at some point, you or dependabot will need to update a library with high severity vulnerabilities, and will likely run into the dreaded &lt;code&gt;Conflicting peer dependency&lt;/code&gt; error. As CRA's dependencies are no longer being updated, that error is more likely to occur the longer you wait.&lt;/p&gt;

&lt;p&gt;Additionally, you'll need to migrate if you want to get newer versions of core libraries such as TypeScript or React. While React 19 currently works with CRA, TypeScript is sadly stuck at version 4, meaning you won't get the speed or size improvements or expanded ESM support of version 5.&lt;/p&gt;

&lt;p&gt;But the best reason to migrate is to take advantage of Next.js server-side features and built-in optimizations. So, let's explore some migration strategies next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating an existing CRA app to Next.js with client-side features only
&lt;/h2&gt;

&lt;p&gt;If you're ready to migrate an existing CRA app to Next.js but want to start with a 1:1 move that keeps your app functioning as a single-page client-side app (SPA), the &lt;code&gt;pages/&lt;/code&gt; directory is the best place to begin. This setup allows you to preserve familiar patterns while getting immediate benefits like built-in routing.&lt;/p&gt;

&lt;p&gt;Let's walk through migrating a simple component; in this case, a minimal login UI from a CRA app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/App.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&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;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&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;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLogin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Log&lt;/span&gt; &lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Next.js, we would first create &lt;code&gt;pages/index.tsx&lt;/code&gt;, to replace &lt;code&gt;src/App.tsx&lt;/code&gt;, then move the component mostly as-is to the new file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/index.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&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;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&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;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLogin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Log&lt;/span&gt; &lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is still a purely client-side component. You get immediate access to Next.js features like routing without giving up control over how your app behaves. To complete the example, we also need to add a login endpoint under &lt;code&gt;pages/api/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/login.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&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;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jane Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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;Pretty straightforward, with minimal code changes. Of course, in a large codebase, things can get more complex, so if you want to see what a migration like this looks like in practice, see &lt;a href="https://github.com/bildungsroman/case-study/commit/255e44254482c1e4a0020e1164659abc3dbeb289" rel="noopener noreferrer"&gt;this commit&lt;/a&gt; of a working example app. If you have an existing CRA app that you're ready to migrate to Next.js, you can follow this &lt;a href="https://nextjs.org/docs/app/guides/migrating/from-create-react-app" rel="noopener noreferrer"&gt;step-by-step migration guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The migration guide and commit linked above offer examples of straightforward, 1:1 SPA migrations. But to really take advantage of Next.js, let's see what a migration to using server-side rendering (SSR) looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrate a SPA to React Server Components
&lt;/h2&gt;

&lt;p&gt;While migrations are often done out of necessity, they can also serve as an opportunity to make significant performance improvements and modernize a legacy app's architecture. Refactoring an SPA to use server components will reduce your client-side JavaScript bundle size, improving initial load times and your users' experience. Server components also allow for resolving authentication state server-side, which has the added benefit of being more secure while also reducing load times. And the final bonus: server-rendered content is better for search engines, if SEO is important to you. &lt;a href="https://react.dev/reference/rsc/server-components" rel="noopener noreferrer"&gt;React Server Components&lt;/a&gt; go one step further than SSR, allowing parts of the component tree to render only on the server, never reaching the client as JavaScript.&lt;/p&gt;

&lt;p&gt;What does this look like in practice? Let's return to our authentication example, now migrated to the App Router. In a server component like &lt;code&gt;app/page.tsx&lt;/code&gt;, we use &lt;code&gt;cookies()&lt;/code&gt; to access login state directly from the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cookies&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;next/headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LoginButton&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;./LoginButton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&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;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt; : &amp;lt;LoginButton /&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the &lt;code&gt;LoginButton&lt;/code&gt; component remains client-side. This keeps the interactive part of the app lightweight, while moving the auth check to the server where it belongs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/login/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&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;POST&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jane Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;httpOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure allows the app to render personalized content without requiring a round trip to the client to determine the user's session. It's a small change that brings meaningful performance and security improvements. To see what this looks like in an existing codebase, &lt;a href="https://github.com/bildungsroman/case-study/commit/c50dfe3ce02a33137a1055249ade0c8caf99d34e" rel="noopener noreferrer"&gt;this commit&lt;/a&gt; from the same repository referenced earlier shows a migration from client components to using server-side API routes and middleware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stretch goal: implement Partial Prerendering for the best of both worlds
&lt;/h2&gt;

&lt;p&gt;If you want to go even further into Next.js's experimental features, you can implement &lt;a href="https://nextjs.org/docs/app/getting-started/partial-prerendering" rel="noopener noreferrer"&gt;Partial Prerendering&lt;/a&gt; (PPR) in the same component. While React Server Components are great for applications that need real-time dynamic data, and static generation is great for infrequently updated data, such as content from a CMS, PPR is a new feature that gives you the best of both. PPR uses React Suspense to separate dynamic content from static content, allowing Next.js to pre-render the static content at build time, while dynamic content is streamed at runtime from the server.&lt;/p&gt;

&lt;p&gt;Here's an example of our auth component using PPR. First, we'd create a dynamic user component and skeleton:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/user.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cookies&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;next/headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&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;User&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Guest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserSkeleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, using &lt;code&gt;Suspense&lt;/code&gt;, we designate the dynamic part of our page while allowing the remainder to load as static content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Suspense&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserSkeleton&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;./user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;experimental_ppr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// This enables PPR for the entire app/ route&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;statically&lt;/span&gt; &lt;span class="nx"&gt;prerendered&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserSkeleton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With just a few tweaks, our legacy CRA auth code has evolved into a modern, flexible component that plays nicely with both static and dynamic rendering.&lt;/p&gt;

&lt;p&gt;So yes, it's always a little sad to say goodbye to an old tool, especially one that's been part of your team's routine. But as we've seen, moving from CRA to Next.js doesn't mean starting over. It just means migrating to a more flexible, more capable way of building React apps.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Return to the World of AI Mechanics</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Thu, 05 Jun 2025 02:55:43 +0000</pubDate>
      <link>https://forem.com/annaspies/return-to-the-world-of-ai-mechanics-43d4</link>
      <guid>https://forem.com/annaspies/return-to-the-world-of-ai-mechanics-43d4</guid>
      <description>&lt;p&gt;It's been over a year since I wrote &lt;a href="https://dev.to/annaspies/entering-the-world-of-ai-mechanics-3776"&gt;Entering the World of AI Mechanics&lt;/a&gt;, and I happened to come back to it today as I've been reading and thinking a lot about the role of AI in engineering as well as in the wider world.&lt;/p&gt;

&lt;p&gt;A year is long enough for several quantum leaps in genAI. Of course new models have come out since then, but the fact that I wrote this before agentic AI was not only a thing, but a regular part of my workflow, makes it feel quaint.&lt;/p&gt;

&lt;p&gt;A year ago, my main experience with AI was adding it as a code snippet-generating tool to one of the apps I work on in my Day Job, and thus my point of view came from thinking about the user experience of a feature we added that wouldn't always act in the way a user (or us, the programmers) expected it to, and how to adjust to that uncertainty. Our users were used to software that always had the same result from the same input, and if it didn't, they'd file a ticket, as clearly we had introduced a regression that needed to be fixed. The genAI feature broke that contract between us and our users, and suddenly we had to account for unexpected results as well as unexpected reactions.&lt;/p&gt;

&lt;p&gt;As a user though, back then genAI was an occasionally helpful, often wrong, ever-annoying chatbot that was being forced on me at work and invading other software I relied on in nonintuitive, rarely useful ways. That AI very much still exists today, and I ignore it much as I did a year ago. Sure, generating images or recipes from ChatGPT has been entertaining and somewhat useful, but not enough to become a habit.&lt;/p&gt;

&lt;p&gt;What changed between then and now is agentic AI. The difference between chat and agents is that I actually &lt;em&gt;want&lt;/em&gt; to use the latter now, because it's not only not terrible, it's actually helpful for select tasks. My go-to example is writing unit tests, because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They have a well-defined scope and purpose&lt;/li&gt;
&lt;li&gt;In a production codebase, there are plenty of examples to give the LLM to show style, which testing library to use, mocking examples, etc.&lt;/li&gt;
&lt;li&gt;I fucking hate writing them&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's been a huge boost to my productivity to tell the AI that I updated the code for &lt;code&gt;myAwesomeFunction.ts&lt;/code&gt;, can you now update the tests in &lt;code&gt;myAwesomeFunction.test.ts&lt;/code&gt; for me? And the best part is, agentic AI will make the changes, run the tests, then revise as needed on its own until tests, type checks, linting, etc. all pass, all while I'm moving on to working on the next feature.&lt;/p&gt;

&lt;p&gt;This is AI that actually has a purpose; I'm not losing anything by not copy-pastaing code from similar tests, there is no test writer on our team that will lose their job because of this, and even junior developers will benefit from not having to do as many tedious tasks as they learn the codebase.&lt;/p&gt;

&lt;p&gt;The caveat here is that at this moment, agents should mainly be used for these kinds of tedious, well-defined tasks, as they still fall apart when given more complexity to work with, even with good guidelines. Additionally, the more complex tasks are the ones I &lt;em&gt;want&lt;/em&gt; to do myself; writing code with the user and developer experience in mind is something I know I can do a hell of a lot better than an LLM right now, and it's also something I enjoy doing (for the most part). I may still tag in an agent to help with debugging, but they're serving the role of a rubber duck more often than actually being helpful.&lt;/p&gt;

&lt;p&gt;But back to my original post, and particularly this part:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quantum physics only applies at the subatomic level. Once you have a mass of atoms comprising, for example, a ball, it behaves in a very classical way. Perhaps masses of software, say at an enterprise or at the infrastructure level, should behave in a classical, predictable, and repeatable way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yesterday, I read Steve Yegge's &lt;a href="https://sourcegraph.com/blog/revenge-of-the-junior-developer" rel="noopener noreferrer"&gt;Revenge of the junior developer&lt;/a&gt; (I'm late to the party, I know. Two months is a geological era these days). The part that hit me the hardest was "Part 5: The agent fleet is coming", because &lt;strong&gt;OF COURSE IT IS&lt;/strong&gt;. This has become so apparent to anyone using agents and seeing people already starting to build workflows where two agents interact with one another, or open-source tooling that lets you pull in multiple agent instances. Fleets are coming, and while I'm ambivalent about the direction this will lead software engineering teams, I'm hopeful in that they may be the answer to the uncertainty I feel when using LLMs today.&lt;/p&gt;

&lt;p&gt;If one LLM is a quantum particle, and agents are several particles interacting at the quantum level, my hope is that fleets bring us back to the classical world of more certain outputs.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>vibecoding</category>
      <category>science</category>
    </item>
    <item>
      <title>Entering the World of AI Mechanics</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Fri, 31 May 2024 19:17:32 +0000</pubDate>
      <link>https://forem.com/annaspies/entering-the-world-of-ai-mechanics-3776</link>
      <guid>https://forem.com/annaspies/entering-the-world-of-ai-mechanics-3776</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Update: I &lt;a href="https://dev.to/annaspies/return-to-the-world-of-ai-mechanics-43d4"&gt;returned&lt;/a&gt; to this world a year later, with some updated thoughts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve been thinking a lot lately about how working with LLMs is spooky, as in “spooky action at a distance” spooky, and some parallels to the world of physics have come up in my mind.&lt;/p&gt;

&lt;p&gt;We’re going through a paradigm shift in tech right now, which is a dramatic statement to make at any time and is often incorrect (ahem, blockchain). But given the size of the current #genAI bandwagon, it feels not far off to say that things will not be the same once LLMs are widely adopted.&lt;/p&gt;

&lt;p&gt;Being somewhat of a physics buff, I can’t help but see comparisons between what’s currently happening in genAI and what happened when quantum mechanics was introduced into a classical mechanics world, as both brought about a dramatic change in thinking.&lt;/p&gt;

&lt;p&gt;In classical mechanics, if you roll a ball from point A, and you know the exact force it was pushed with and the amount of resistance, you’ll know when it will reach point B. If it doesn’t reach point B when you expected it to, something is wrong with your calculations, not with the laws of physics.&lt;/p&gt;

&lt;p&gt;The world of classical computing is much like the world of classical mechanics: input A should always result in output B.  Sure, there are complexities and race conditions, but for the most part, whatever code you’re writing is likely to be buggy because you didn’t think of some side effect, not because the logic suddenly changed on you.&lt;/p&gt;

&lt;p&gt;Not so with LLMs. Input A sometimes results in output B, sometimes in output C, and sometimes in “I’m sorry, I can’t answer that question right now”. And so we enter the quantum world of probabilities, where an atom is X% likely to be in a given position, but you will never be 100% sure until you measure it.&lt;/p&gt;

&lt;p&gt;We can give LLMs safeguards and engineer our prompts in specific ways, but the chance that an answer is what we expect will always be a probability, not a guarantee; we’re never sure of the output until it’s measured by the user’s reaction.&lt;/p&gt;

&lt;p&gt;That means as engineers, we need to change our mindsets, from building in a world of known laws to building in a world of probabilities, and optimizing for the best average or consistent result. &lt;/p&gt;

&lt;p&gt;We also need to realize that for the average user, this will initially appear as a degradation: we went from presenting sure outputs to widely varying outcomes given the same input, which can be jarring at best and a poor experience at worst.&lt;/p&gt;

&lt;p&gt;Rolling out half-baked products without sufficiently sure probabilities of valid results is a good way to frustrate users; no disclaimer will alleviate a bad initial user experience. Most users still live in the classical world, and rather than meeting them where they’re at and easing them into quantum outputs, we’re pulling out the rug and hoping we’ve engineered our prompts correctly, when “correct” is actually a percentage and not a bool.&lt;/p&gt;

&lt;p&gt;There’s a final parallel, though: quantum physics only applies at the subatomic level. Once you have a mass of atoms comprising, for example, a ball, it behaves in a very classical way. Perhaps masses of software, say at an enterprise or at the infrastructure level, should behave in a classical, predictable, and repeatable way.&lt;/p&gt;

&lt;p&gt;That means that there are places and use cases for LLMs, but there also very much areas that should stay classical, where probability is detrimental to the experience, and the blanket “put an AI on it” push is counterproductive. We’re still learning where that line is, but until we do, maybe some use cases should remain in the classical world.&lt;/p&gt;

</description>
      <category>genai</category>
      <category>llm</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Generative (A)IaC in the IDE with Application Composer</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Thu, 18 Jan 2024 19:14:47 +0000</pubDate>
      <link>https://forem.com/annaspies/generative-aiac-in-the-ide-with-application-composer-cfh</link>
      <guid>https://forem.com/annaspies/generative-aiac-in-the-ide-with-application-composer-cfh</guid>
      <description>&lt;p&gt;&lt;em&gt;TL;DR: New tutorial dropped using App Composer + gen AI! Get the &lt;a href="https://github.com/bildungsroman/lex-lambda-bedrock-app-composer" rel="noopener noreferrer"&gt;tutorial repo&lt;/a&gt; or read on to follow along!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/application-composer/" rel="noopener noreferrer"&gt;AWS Application Composer&lt;/a&gt; launched in the AWS Console at re:Invent one year ago, and this re:Invent it expanded to the VS Code IDE as part of the &lt;a href="https://aws.amazon.com/visualstudiocode/" rel="noopener noreferrer"&gt;AWS Toolkit&lt;/a&gt; - but that’s not the only exciting part. When using App Composer in the IDE, users also get access to a generative AI partner that will help them write infrastructure as code (IaC) for all 1100+ &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;AWS CloudFormation&lt;/a&gt; resources that Application Composer now &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/09/aws-application-composer-1000-cloudformation-resources/" rel="noopener noreferrer"&gt;supports&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better than docs
&lt;/h2&gt;

&lt;p&gt;Application Composer lets users create IaC templates by dragging and dropping cards on a virtual canvas that represent AWS CloudFormation resources, and wiring them together to create permissions and references. Initially launching with support for 13 core serverless resources such as AWS Lambda, Amazon S3, and DynamoDB, in September our team &lt;a href="https://twitter.com/annaspies/status/1706790378358374469" rel="noopener noreferrer"&gt;launched support&lt;/a&gt; for all 1100+ resources that AWS CloudFormation allows, updated regularly. That means that users can now build with everything from Amplify to XRay, and hundreds of resources in between.&lt;/p&gt;

&lt;p&gt;However, while the 13 enhanced resources come with defaults based on best practices, standard AWS CloudFormation resources come only with basic configuration - generally required settings only, with a type set by a schema rather than an example. So a user adding an Amplify App resource would be given the following configuration by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  MyAmplifyApp:
    Type: AWS::Amplify::App
    Properties:
      Name: &amp;lt;String&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And they would see this in the console:&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%2Fu2bx19n9mwyfcedooxk8.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%2Fu2bx19n9mwyfcedooxk8.png" alt="Amplify resource in App Composer" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s well and good for the AWS CloudFormation expert, but someone new to IaC or even just that resource would then have to go into the CloudFormation docs to find additional properties and determine their types, or scour the web for example configurations that may or may not fit their use case. Luckily, the newest App Composer feature saves builders those steps - and uses generative AI to generate resource-specific configurations with safeguards right in the IDE.&lt;/p&gt;

&lt;p&gt;When working on a CloudFormation or SAM template in VS Code, users can sign in with their Builder ID and generate multiple suggested configurations in App Composer - here’s an example for our &lt;code&gt;AWS::Amplify::App&lt;/code&gt; type:&lt;/p&gt;

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

&lt;p&gt;These suggestions are specific to the resource type, and are safeguarded by a check against the CloudFormation schema to ensure valid values or helpful placeholders. You can then select, use and modify the suggestions to fit your needs.&lt;/p&gt;

&lt;p&gt;So we now know how to generate a simple example with one resource, but let’s look at building a full application with the help of AI-generated suggestions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building together with AI
&lt;/h2&gt;

&lt;p&gt;You may be aware of &lt;a href="https://serverlessland.com/" rel="noopener noreferrer"&gt;Serverlessland&lt;/a&gt;, a treasure trove of developer-centered content and examples of serverless applications. I decided to take one of their more popular (and AI-focused) tutorials, titled “&lt;a href="https://serverlessland.com/repos/lex-lambda-bedrock-cdk-python" rel="noopener noreferrer"&gt;Use GenAI capabilities to build a chatbot&lt;/a&gt;”, and recreate it with App Composer and our trusty AI assistant. Here we go!&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting started with the AWS Toolkit in VS Code
&lt;/h3&gt;

&lt;p&gt;First of all, if you don’t yet have the &lt;a href="https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.aws-toolkit-vscode" rel="noopener noreferrer"&gt;AWS Toolkit&lt;/a&gt; extension, you can find it under the Extensions tab in VS Code. Install or update it so you’re at least on version 2.1.0, and you’ll see a screen like this that mentions Amazon Q and Application Composer:&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%2Fc90lb8unb15ihlpx7sky.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%2Fc90lb8unb15ihlpx7sky.png" alt="AWS Toolkit in VS Code" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, to enable our AI assistant, we need to enable CodeWhisperer using our Builder ID. The easiest way is to open Q, and select authenticate - you should be taken to this screen, where you can select the Builder ID option and be taken to the browser to create or sign in with your Builder ID.&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%2Fk5l9auj1gxc19gg5w7lb.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%2Fk5l9auj1gxc19gg5w7lb.png" alt="Sign in with Builder ID" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If all goes well, you’ll see your connection in the VS Code toolkit panel:&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%2F825xsqjgf0ve0fdz77z9.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%2F825xsqjgf0ve0fdz77z9.png" alt=" " width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’re connected, you’re ready to start building! &lt;/p&gt;

&lt;h3&gt;
  
  
  Composing your architecture
&lt;/h3&gt;

&lt;p&gt;In a workspace, create a new folder and a blank file called &lt;code&gt;template.yaml&lt;/code&gt;. Open &lt;code&gt;template.yaml&lt;/code&gt;, and you should see the Application Composer icon in the top right. Click on it to initialize App Composer with a blank canvas. Now we can start building our application.&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%2Fycuhfd963cdko507ycw3.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%2Fycuhfd963cdko507ycw3.png" alt="Click the App Composer icon" width="800" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may have noticed that the tutorial includes an architecture diagram that looks like this:&lt;/p&gt;

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

&lt;p&gt;First we’re going to add the services in the diagram to sketch out our application architecture - with the added bonus of creating a deployable CloudFormation template.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;From the &lt;code&gt;Enhanced components&lt;/code&gt; list, drag in a Lambda function and a Lambda layer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Double-click the Function resource to edit its properties. Rename the Lambda function’s Logical ID to &lt;code&gt;LexGenAIBotLambda&lt;/code&gt;.&lt;br&gt;
Change the Source path to &lt;code&gt;src/LexGenAIBotLambda&lt;/code&gt;, and the runtime to Python.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change the handler value to &lt;code&gt;TextGeneration.lambda_handler&lt;/code&gt;, and click Save.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Double-click the Layer resource to edit its properties.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rename the layer &lt;code&gt;Boto3Layer&lt;/code&gt; and change its build method to Python. Change its Source path to &lt;code&gt;src/Boto3PillowPyshorteners.zip&lt;/code&gt;, and click Save.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, connect the layer to the function to add a reference between them.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can follow along with the video to see this in action:&lt;/p&gt;

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

&lt;p&gt;You’ll see that your &lt;code&gt;template.yaml&lt;/code&gt; file has now been updated to include those resources (and some auto-generated ones, such as a Lambda log group for your function). If you go into your source directory, you’ll see some generated function files. Don’t worry about those - we’ll replace them with the tutorial function and layers later.&lt;/p&gt;

&lt;p&gt;So that was the easy part - you added some resources and generated IaC that includes best-practices defaults. Next we'll explore using standard CloudFormation components.&lt;/p&gt;

&lt;h3&gt;
  
  
  With a little help from our (AI) friend
&lt;/h3&gt;

&lt;p&gt;Let’s start by searching for and adding several of the &lt;code&gt;Standard components&lt;/code&gt; needed for our application. These include the types &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt;, two roles of type &lt;code&gt;AWS::IAM::Role&lt;/code&gt;, and one &lt;code&gt;AWS::IAM::Policy&lt;/code&gt;. Some standard resources will have all the defaults you need. For example, when you add the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; resource, all you have to do is replace the placeholder values.&lt;/p&gt;

&lt;p&gt;Other resources, such as the IAM Roles and IAM Policy, have bare-bones config. This is where our AI assistant comes in handy! Choose an IAM Role resource and click &lt;code&gt;Generate suggestions&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Because these suggestions are generated by an LLM, they will likely differ between each generation, but they are tailored to be specific to each resource and to follow valid CloudFormation schema - so go ahead, generate away!&lt;/p&gt;

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

&lt;p&gt;Generating different configurations will give you an idea of what a resource’s policy &lt;em&gt;should&lt;/em&gt; look like, and will often give you keys that you can then fill in with the values you need. Below are the actual settings we’ll be using for each resource, so you can replace the generated values when applicable - be sure to replace each resource’s Logical ID as well!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# CfnLexGenAIDemoRole - type AWS::IAM::Role&lt;/span&gt;
&lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
      &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
      &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lexv2.amazonaws.com&lt;/span&gt;
  &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
&lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AWS::Partition&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:iam::aws:policy/AWSLambdaExecute'&lt;/span&gt;

&lt;span class="c1"&gt;# LexGenAIBotLambdaServiceRole - type AWS::IAM::Role&lt;/span&gt;
&lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
      &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
      &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda.amazonaws.com&lt;/span&gt;
  &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
&lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AWS::Partition&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'&lt;/span&gt;

&lt;span class="c1"&gt;# LexGenAIBotLambdaServiceRoleDefaultPolicy - type AWS::IAM::Policy&lt;/span&gt;
&lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lex:*&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs:*&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:DeleteObject&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:GetObject&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:ListBucket&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:PutObject&lt;/span&gt;
      &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
      &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bedrock:InvokeModel&lt;/span&gt;
      &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
      &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:bedrock:'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AWS::Region&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;::foundation-model/anthropic.claude-v2'&lt;/span&gt;
  &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
&lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LexGenAIBotLambdaServiceRoleDefaultPolicy&lt;/span&gt;
&lt;span class="na"&gt;Roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;LexGenAIBotLambdaServiceRole&lt;/span&gt;

&lt;span class="c1"&gt;# LexGenAIBotLambdaInvoke - type AWS::Lambda::Permission&lt;/span&gt;
&lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda:InvokeFunction&lt;/span&gt;
&lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LexGenAIBotLambda.Arn&lt;/span&gt;
&lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lexv2.amazonaws.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let’s add our Lex bot! In the resource picker, search for and add type &lt;code&gt;AWS::Lex::Bot&lt;/code&gt;. Here’s another chance to see what configuration the AI comes up with!&lt;/p&gt;

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

&lt;p&gt;Change the Lex bot’s Logical ID to &lt;code&gt;LexGenAIBot&lt;/code&gt; and update its configuration to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;DataPrivacy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ChildDirected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;IdleSessionTTLInSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LexGenAIBot&lt;/span&gt;
&lt;span class="na"&gt;RoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;CfnLexGenAIDemoRole.Arn&lt;/span&gt;
&lt;span class="na"&gt;AutoBuildBotLocales&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;BotLocales&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Intents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;InitialResponseSetting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CodeHook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;EnableCodeHookInvocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;IsActive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;PostCodeHookSpecification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="na"&gt;IntentClosingSetting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ClosingResponse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;MessageGroupsList&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;PlainTextMessage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hi there, I'm a GenAI Bot. How can I help you?&lt;/span&gt;
        &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;WelcomeIntent&lt;/span&gt;
        &lt;span class="na"&gt;SampleUtterances&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hi&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hey there&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hello&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;I need some help&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Help needed&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Can I get some help?&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;FulfillmentCodeHook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;IsActive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;PostFulfillmentStatusSpecification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="na"&gt;InitialResponseSetting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CodeHook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;EnableCodeHookInvocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;IsActive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;PostCodeHookSpecification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GenerateTextIntent&lt;/span&gt;
        &lt;span class="na"&gt;SampleUtterances&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate content for&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Create&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Create&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Utterance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Text to be generated for&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;FulfillmentCodeHook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;IsActive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;PostFulfillmentStatusSpecification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="na"&gt;InitialResponseSetting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CodeHook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;EnableCodeHookInvocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;IsActive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;PostCodeHookSpecification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FallbackIntent&lt;/span&gt;
        &lt;span class="na"&gt;ParentIntentSignature&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AMAZON.FallbackIntent&lt;/span&gt;
    &lt;span class="na"&gt;LocaleId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en_US&lt;/span&gt;
    &lt;span class="na"&gt;NluConfidenceThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bot created demonstration of GenAI capabilities.&lt;/span&gt;
&lt;span class="na"&gt;TestBotAliasSettings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BotAliasLocaleSettings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;BotAliasLocaleSetting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;CodeHookSpecification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;LambdaCodeHook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;CodeHookInterfaceVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.0'&lt;/span&gt;
            &lt;span class="na"&gt;LambdaArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LexGenAIBotLambda.Arn&lt;/span&gt;
        &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;LocaleId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en_US&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once all of your resources are configured, your application should look like this:&lt;/p&gt;

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

&lt;p&gt;In case your template looks different or you want to double-check your configuration, you can copy the template directly from my &lt;a href="https://github.com/bildungsroman/lex-lambda-bedrock-app-composer/blob/main/template.yaml" rel="noopener noreferrer"&gt;Github repository&lt;/a&gt;. You’ll want to copy the &lt;a href="https://github.com/bildungsroman/lex-lambda-bedrock-app-composer/blob/main/src/Boto3PillowPyshorteners.zip" rel="noopener noreferrer"&gt;Lambda layer&lt;/a&gt; directly from the repository, and add it to &lt;code&gt;./src/Boto3PillowPyshorteners.zip&lt;/code&gt;. Finally, rename the generated &lt;code&gt;handler.py&lt;/code&gt; to &lt;code&gt;TextGeneration.py&lt;/code&gt; and replace the placeholder code with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;botocore.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClientError&lt;/span&gt;

&lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;region_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;region&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;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s3_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic.claude-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Bedrock client used to interact with APIs around models
&lt;/span&gt;&lt;span class="n"&gt;bedrock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Bedrock Runtime client used to invoke and question the models
&lt;/span&gt;&lt;span class="n"&gt;bedrock_runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock-runtime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_session_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent_request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;session_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intent_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionState&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionAttributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionAttributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fulfillment_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;intent_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionState&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;intent&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;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fulfillment_state&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionState&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;sessionAttributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;session_attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dialogAction&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;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Close&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;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;intent_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionState&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;intent&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;messages&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="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;intent_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionId&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;requestAttributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;intent_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requestAttributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requestAttributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;intent_request&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&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;Event is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&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;span class="n"&gt;accept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inputTranscript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&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;prompt&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="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Human:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Assistant:&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;max_tokens_to_sample&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;top_k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;top_p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stop_sequences&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="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;nHuman:&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="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bedrock_runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;modelId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&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;Response body: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response_body&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;span class="n"&gt;response_message&lt;/span&gt; &lt;span class="o"&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;contentType&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;PlainText&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response_body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completion&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="n"&gt;session_attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_session_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fulfillment_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fulfilled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fulfillment_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ClientError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Exception raised while execution and the error is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&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;p&gt;To deploy your infrastructure, go back to the App Composer extension, and click the &lt;code&gt;Sync&lt;/code&gt; icon. You'll need to have &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/prerequisites.html#prerequisites-configure-credentials" rel="noopener noreferrer"&gt;local IAM credentials&lt;/a&gt; and the &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html" rel="noopener noreferrer"&gt;AWS SAM CLI&lt;/a&gt; installed, so grab it if you don't already have it. Follow the guided AWS SAM instructions to initiate and complete your deployment.&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%2Fcbvyacei4esp2jdfpr15.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%2Fcbvyacei4esp2jdfpr15.png" alt="Click Sync to deploy" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If all goes well, you’ll see the message &lt;code&gt;SAM Sync succeeded&lt;/code&gt; and can navigate to CloudFormation in the AWS Console to see your newly-created resources.&lt;/p&gt;

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

&lt;p&gt;If you want to continue with building your chatbot, be sure to follow the rest of the original &lt;a href="https://github.com/awsarippa/lex-lambda-bedrock-cdk-python#usage" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; to keep building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go build - with help!
&lt;/h2&gt;

&lt;p&gt;I hope that building with AI-generated CloudFormation will help increase your understanding of the different resource settings that are available and commonly used, and accelerate your time from idea to deployed application. As with everything using generative AI today, be sure to read the &lt;a href="https://aws.amazon.com/machine-learning/responsible-ai/policy/" rel="noopener noreferrer"&gt;AWS Responsible AI Policy&lt;/a&gt; before applying these examples yourself. Happy building!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A &lt;a href="https://aws.amazon.com/blogs/compute/using-generative-infrastructure-as-code-with-application-composer/" rel="noopener noreferrer"&gt;version&lt;/a&gt; of this post was originally published over at the &lt;a href="https://aws.amazon.com/blogs/compute/" rel="noopener noreferrer"&gt;AWS Compute Blog&lt;/a&gt; - give them a follow to keep up with all things AWS!&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Loneliness of Trying to #EmbraceEquity in FAANG</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Thu, 16 Mar 2023 00:30:39 +0000</pubDate>
      <link>https://forem.com/annaspies/the-loneliness-of-trying-to-embraceequity-in-faang-4609</link>
      <guid>https://forem.com/annaspies/the-loneliness-of-trying-to-embraceequity-in-faang-4609</guid>
      <description>&lt;p&gt;This year's theme for International Women's Day at my employer was #EmbraceEquity, with a slate of women-led talks and panels and some truly inspiring stories of women rising in the ranks of Amazon and AWS.&lt;/p&gt;

&lt;p&gt;And I love that we're calling out the problem and (in theory) actively working on it, though we still have such a long way to go.&lt;/p&gt;

&lt;p&gt;In practice, though, my day-to-day interactions look like this:&lt;/p&gt;

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

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

&lt;p&gt;with the occasional&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%2Fz2va28q35iso0jqk9h7q.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%2Fz2va28q35iso0jqk9h7q.png" alt=" " width="200" height="33"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(if you know, you know :sevh:)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And then, inspired by a &lt;a href="https://twitter.com/LauraJHyatt/status/1633440151874465792" rel="noopener noreferrer"&gt;tweet&lt;/a&gt; from a colleague, I did the math on my actual team, the people I interact with 90% of the time at work:&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%2Fdepd15rry6wyelcfyxao.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%2Fdepd15rry6wyelcfyxao.png" alt=" " width="598" height="809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and all the hope and optimism IWD is supposed to inspire was brought crashing down by reality.&lt;/p&gt;

&lt;p&gt;Don't get me wrong: I am incredibly lucky to work on a respectful, collaborative team of allies, and I have never felt discriminated by my immediate coworkers - they're awesome dudes! But there is something incredibly lonely about never (or hardly ever, if you count Slack) interacting with another member of your gender &lt;em&gt;in your role&lt;/em&gt;. I'm jealous of my colleagues that don't have to feel that loneliness, and in my experience, no amount of women in tech meetups can fill that day-to-day gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what can we do about it?
&lt;/h2&gt;

&lt;p&gt;I realize that the bigger the company, the more that equity is a marathon, not a sprint. A startup can hire two new engineers and suddenly the numbers are much closer to 50/50. At a FAANG, this process takes years and requires not just desire from the bottom, but buy-in from the top. And the top... well, let's just say it's even lonelier than I am:&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%2Fx267a85zyvd1gfwkzyw2.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%2Fx267a85zyvd1gfwkzyw2.png" alt=" " width="640" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://www.aboutamazon.com/news/workplace/our-workforce-data" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is compared to all corporate employees, which unfortunately are not broken down by job roles, as based on my experience over the past 1.5 years, the numbers for AWS specifically and all engineering roles are much worse:&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%2Fumgcpri1zx2vaed69jco.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%2Fumgcpri1zx2vaed69jco.png" alt=" " width="651" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The good news is that the numbers are getting better! But for someone working day to day in a very male-dominated environment, that change still feels far too slow. AWS has shown numerous times that when something matters, leadership can act much faster in a concentrated effort. I can only hope that this pledge to #EmbraceEquity results in more action than words.&lt;/p&gt;

</description>
      <category>wecoded</category>
      <category>faang</category>
      <category>aws</category>
      <category>womenintech</category>
    </item>
    <item>
      <title>Why Visual Building Matters (Especially for Serverless)</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Fri, 02 Dec 2022 00:45:35 +0000</pubDate>
      <link>https://forem.com/annaspies/why-visual-building-matters-especially-for-serverless-5gld</link>
      <guid>https://forem.com/annaspies/why-visual-building-matters-especially-for-serverless-5gld</guid>
      <description>&lt;p&gt;My team &lt;a href="https://techcrunch.com/2022/12/01/aws-launches-application-composer-a-low-code-tool-for-building-serverless-apps/" rel="noopener noreferrer"&gt;launched&lt;/a&gt; a &lt;a href="https://aws.amazon.com/application-composer/" rel="noopener noreferrer"&gt;thing&lt;/a&gt; today, and while there are so many features I can talk about (and many more to come), it’s a visual builder after all, so let me visually demonstrate what AWS Application Composer does:&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;In a nutshell, you can drag and drop serverless resources such as Lambda functions, API Gateway APIs, EventBridge rules, etc. onto a canvas, connect them to each other to add permissions, and out comes CloudFormation YAML with best practices baked in!&lt;/p&gt;

&lt;p&gt;Oh yeah, and we're using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API" rel="noopener noreferrer"&gt;File System Access API&lt;/a&gt; to save all of this to your local disk, so you can quickly push to git and deploy what you've built using the AWS SAM CLI 🤯&lt;/p&gt;

&lt;p&gt;So why does this matter, especially in the serverless ecosystem? Because while there are numerous solutions for diagramming architectures, you then still have to learn how to provision those resources and get all of the permissions right for them to actually work. Before today, that usually meant learning to code that architecture in some sort of proprietary framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  How visual building delivers the promise of serverless
&lt;/h2&gt;

&lt;p&gt;The promise of serverless was initially this: focus on your business logic, and let the cloud provider take care of the rest. Somehow, that promise evolved into "here are 153 ways to provision 200+ resources via yaml". And then CDK came along, and made that process friendlier for developers, but it still requires learning how to code resources in a specific framework. So here's a novel idea:&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%2Fcuop8itpf16nt4zlqwy9.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%2Fcuop8itpf16nt4zlqwy9.png" alt=" " width="800" height="801"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Forget code, at least when it comes to your infrastructure! Save it for your Lambda logic and the things that differentiate your application. The world doesn't need more Terraform experts, but it does need cloud architects and devOps engineers who can quickly spin up infrastructure.&lt;/p&gt;

&lt;p&gt;Oh, and with App Composer you get the bonus of being able to visualize existing architectures too, which is a killer feature when onboarding new team members:&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;Anyway, I’m mostly excited to finally be able to talk about what I’ve been working on the past ~8 months 😅 App Composer is a frontend-only, canvas-based service, so stay tuned if you want to know more about how we actually built the thing.&lt;/p&gt;

&lt;p&gt;Now excuse me while I go sleep for a week.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>lowcode</category>
      <category>webdev</category>
      <category>aws</category>
    </item>
    <item>
      <title>Crickets, or hey, I work for a FAANG now and everything is NDA</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Fri, 08 Apr 2022 22:54:08 +0000</pubDate>
      <link>https://forem.com/annaspies/crickets-or-hey-i-work-for-a-faang-now-and-everything-is-nda-1a4c</link>
      <guid>https://forem.com/annaspies/crickets-or-hey-i-work-for-a-faang-now-and-everything-is-nda-1a4c</guid>
      <description>&lt;p&gt;The other week, I hit my six-month milestone at AWS. It's been... intense. Mostly in good ways! My team is still mainly my old startup team, plus some great new people that fit right in with us. There's always so much to learn, and most other engineers are supportive and happy to answer my noob questions. And in addition to writing code, thanks to Amazon's famous &lt;a href="https://thehustle.co/02162021-amazon-writing/" rel="noopener noreferrer"&gt;writing culture&lt;/a&gt;, I've also been writing a ton in general. The problem is, none of it is writing I can share on here.&lt;/p&gt;

&lt;p&gt;Hence... crickets 🦗.&lt;/p&gt;

&lt;p&gt;And I have no side project tutorials to document, because, well, that whole work-life balance thing is real and necessary and I'd rather be snowboarding than hacking away on a new project on my weekends, so that's what I've been doing.&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%2Ff3h0sfwwwy0ycnqe94hr.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%2Ff3h0sfwwwy0ycnqe94hr.jpg" alt="Snowboard on Mt. Hood" width="800" height="599"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Proof&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I even got my first speaking gig in over two years later this month... at an internal conference I can't share.&lt;/p&gt;

&lt;p&gt;So I guess this is more of an explanation of my absence from this wonderful Dev community over the last nine or so months, and probably ongoing absence in the near future, until there's something I can publicly write about related to my work. Or maybe until the winter season ends and I feel like hacking together another &lt;a href="https://dev.to/annaspies/i-used-cypress-as-an-xbox-web-scraper-and-i-regret-nothing-1bn4"&gt;silly serverless abomination&lt;/a&gt; for fun, not profit, one weekend. TBD.&lt;/p&gt;

&lt;p&gt;So. It feels pretty silly to write a whole blog post about not being able to write blog posts, but here we are.&lt;/p&gt;

&lt;p&gt;But because you took the time to read my silly rantings, you should at least be rewarded with a picture of my cat resting happily on her window perch. Less code, more cats: that's a promise I can keep for now.&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%2Fiexs7k1qk3aftz5p1itk.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%2Fiexs7k1qk3aftz5p1itk.jpg" alt="Cat on window bed" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>aws</category>
      <category>devjournal</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Gentle Reminder to Take a Vacation</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Thu, 20 May 2021 17:56:38 +0000</pubDate>
      <link>https://forem.com/annaspies/a-gentle-reminder-to-take-a-vacation-4ffn</link>
      <guid>https://forem.com/annaspies/a-gentle-reminder-to-take-a-vacation-4ffn</guid>
      <description>&lt;p&gt;I landed on my own Github profile the other day, which is a pretty rare occurrence as I'm usually looking for repos in my company's account, but I was in search of a tutorial project I had done a while back. It had been a minute, and I noticed the Github wizards had further expanded the contributions panel (yay, code reviews are finally acknowledged!). But I also saw something more disturbing: too much green.&lt;/p&gt;

&lt;p&gt;Let me explain.&lt;/p&gt;

&lt;p&gt;I started coding for a living in 2018. This is what my contributions look like from that year:&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%2Fm5m9v7vkt47xv8iw9ynm.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%2Fm5m9v7vkt47xv8iw9ynm.png" alt="2018 Github contributions" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The big gap in May is the period between finishing code school and getting a job in June (during which I went to Mexico, because YOLO). There's a gap in September when I took a week off to meet my parents in San Francisco and drive up through the redwoods with them back to Portland (highly recommended), and then a winter holiday break in December into January 2019.&lt;/p&gt;

&lt;p&gt;Next, there's 2019, my first full year of working as a software engineer:&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%2Fqdmffkdqd79ua59gsrl1.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%2Fqdmffkdqd79ua59gsrl1.png" alt="2019 Github contributions" width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are some solid gaps in there: May 2019, when family from Poland came to visit and we toured up and down the West Coast. October, when I flew to Germany and Poland for more family time and a wedding. Another couple of weeks off for the holidays in December-January. Good times.&lt;/p&gt;

&lt;p&gt;And then... 2020.&lt;/p&gt;

&lt;p&gt;I suppose this one won't be too surprising - there was this little thing about a global pandemic and all:&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%2Fbbtnka1rkbrpxpyjw1z5.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%2Fbbtnka1rkbrpxpyjw1z5.png" alt="2020 Github contributions" width="800" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wow, so solid green it would give that subway station envy. Ok, there's one little gap: in October, around my birthday, I took a road trip to the &lt;a href="https://traveloregon.com/things-to-do/destinations/parks-forests-wildlife-areas/the-alvord-desert/" rel="noopener noreferrer"&gt;Alvord Desert&lt;/a&gt; (also highly recommended). But that didn't even last a full week.&lt;/p&gt;

&lt;p&gt;And yeah, it makes sense. We were in lockdown. There was literally nowhere to go most of the year, and a pandemic killing millions that made even weekend trips seem like unnecessary risks. Understandable.&lt;/p&gt;

&lt;p&gt;But what I saw that disturbed me was my 2021 so far:&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%2Fira2zkxpeam8n58f2jnt.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%2Fira2zkxpeam8n58f2jnt.png" alt="2021 Github contributions" width="776" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty dang solid. And possibly on the road to burnout (anyone else feel that?).&lt;/p&gt;

&lt;p&gt;Luckily, I'm now fully vaccinated, and taking my first full week off next week to go on a road trip (by car, to a pretty remote area, because the pandemic's not over). Even planning it a few weeks back felt strange, as if vacations are another thing the pandemic took away. But that doesn't have to be the case.&lt;/p&gt;

&lt;p&gt;So, for whoever needs to hear it: this is a gentle reminder to take a vacation. Even if you can't go anywhere yet, your mind and body need time away from the green squares. You'll thank yourself later.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>career</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Global State in Gatsby, or Having and Eating Your Cake, Too</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Tue, 04 May 2021 00:00:32 +0000</pubDate>
      <link>https://forem.com/annaspies/global-state-in-gatsby-or-having-and-eating-your-cake-too-3bjj</link>
      <guid>https://forem.com/annaspies/global-state-in-gatsby-or-having-and-eating-your-cake-too-3bjj</guid>
      <description>&lt;p&gt;Gatsby is a fantastic way to build and maintain static websites. React is a fantastic way to build and maintain dynamic web applications. But what if one wants to dream the &lt;del&gt;impossible&lt;/del&gt; improbable dream of having your static cake while eating it dynamically, too? (Too many metaphors, I know.)&lt;/p&gt;

&lt;p&gt;This was the problem I encountered when I needed to add and persist user data across an existing Gatsby site, without screwing up what was already there. Hopefully this will help the next developer who also dares to dream that improbable dream.&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%2Fnhvkasz7d5y23pivj15j.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%2Fnhvkasz7d5y23pivj15j.gif" alt="Impossible dream gif" width="480" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The big Why?
&lt;/h2&gt;

&lt;p&gt;First of all, &lt;em&gt;why&lt;/em&gt; was I setting out on this improbable task of adding global state to a static site? Was I so bored at work that my PM decided to let me run amok and make Gatsby harder? Not quite (though I do look forward to that day).&lt;/p&gt;

&lt;p&gt;In reality, we were implementing a new feature that requires users to log in to be authenticated with Github, so that we could have access to visualize and run an audit on a private repository. That means we needed to persist that user state across several components, like the navbar that showed a different button depending on the user's logged-in state, as well as the components handling the audit logic. Thus far, we'd only used local state in the site, scoped to each component doing its thing. This was going to be a whole new world of Gatsby functionality for me.&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%2Faqbuqp061ep7ok5015p0.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%2Faqbuqp061ep7ok5015p0.gif" alt="Aladdin gif" width="245" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding imperfect solutions
&lt;/h2&gt;

&lt;p&gt;The first task I set upon myself was research: was there a Gatsby plugin that could already do what I needed? (Spoiler alert: nope). How about existing tutorials? I already knew &lt;em&gt;what&lt;/em&gt; I needed: global state throughout my Gatsby app. I needed to learn the &lt;em&gt;how&lt;/em&gt;, or at least be pointed to potential solutions. This short &lt;a href="https://medium.com/swlh/gatsbys-global-state-management-with-react-s-context-5f8064e93351" rel="noopener noreferrer"&gt;blog post on global state in Gatsby&lt;/a&gt; gave me a great start, and led me to consider React's Context as a potential solution.&lt;/p&gt;

&lt;p&gt;The next step for me is always: read the docs! Specifically, the &lt;a href="https://reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;React Context docs&lt;/a&gt;. Providers! Consumers! Ok, this sounds like exactly what I need, except this was the React example...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ThemeContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Toolbar&lt;/span&gt; &lt;span class="nx"&gt;changeTheme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ThemeContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Huh. React apps have an &lt;code&gt;App&lt;/code&gt; component. Gatsby apps do not. So I needed a Gatsby-specific way to do this Context thing. More research!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/2wXs8n1LBQueHbzJIz/source.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/2wXs8n1LBQueHbzJIz/source.gif" alt="Research gif" width="1920" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How about the Gatsby docs? That's where I found this incredibly helpful &lt;a href="https://www.gatsbyjs.com/blog/2019-01-31-using-react-context-api-with-gatsby/" rel="noopener noreferrer"&gt;tutorial on React Context with Gatsby&lt;/a&gt;, which got me started on the right path. Unfortunately, it's from 2019, which may as well be the prehistoric era when we're talking about React advancements. The example uses class components and no hooks (😱), and is all the way back in React 16.3, while we'd already been in the trenches with 16.8, and were up to 17. What are we, savages? It was definitely time for an update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cake-baking time
&lt;/h2&gt;

&lt;p&gt;With a decent understanding of React Context from their docs, as well as knowing I wanted to implement global state using &lt;a href="https://reactjs.org/docs/hooks-intro.html" rel="noopener noreferrer"&gt;React Hooks&lt;/a&gt;, specifically &lt;code&gt;useContext()&lt;/code&gt; and &lt;code&gt;useState()&lt;/code&gt;, I set about customizing and updating the sample code I had found to work for my use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a user context and provider
&lt;/h3&gt;

&lt;p&gt;In a nutshell, React Context is a way to set and use global state without passing a prop to every component that needs it. Instead, you create a Context, then set a Provider that provides that context throughout the application, and a Consumer that, well, consumes (or makes available) that context. Then, you use the &lt;code&gt;useContext()&lt;/code&gt; hook to get the value of the global state object and, if needed, the function that sets the value in individual components.&lt;/p&gt;

&lt;p&gt;In my &lt;code&gt;src&lt;/code&gt; directory, I created &lt;code&gt;contexts/UserContext.js&lt;/code&gt; and added the following:&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="c1"&gt;// ./src/contexts/UserContext.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUser&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;../utils/cognito&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;loggedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultState&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;UserProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCurrentUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Set the current user in the global context&lt;/span&gt;
      &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;loggedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;userid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;setUser&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/UserContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserProvider&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we're setting a &lt;code&gt;defaultState&lt;/code&gt; - anyone who's used Redux should be familiar with that concept. It's the blank state every user visiting the website starts with.&lt;/p&gt;

&lt;p&gt;Next, we're using React's &lt;a href="https://reactjs.org/docs/context.html#reactcreatecontext" rel="noopener noreferrer"&gt;createContext&lt;/a&gt; API to create a context object based on our default values for &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;setUser&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, we use the &lt;a href="https://reactjs.org/docs/hooks-state.html" rel="noopener noreferrer"&gt;useState&lt;/a&gt; hook to set the &lt;code&gt;user&lt;/code&gt; object, and initialize the &lt;code&gt;setUser&lt;/code&gt; function that will be used to update that global &lt;code&gt;user&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;The next hook we use is &lt;a href="https://reactjs.org/docs/hooks-effect.html" rel="noopener noreferrer"&gt;useEffect&lt;/a&gt; - this was a new one for me, but essentially it's the Hooks-y way of triggering a &lt;code&gt;ComponentDidMount&lt;/code&gt; / &lt;code&gt;ComponentDidUpdate&lt;/code&gt; lifecycle event. When it's initialized with an empty array as in the example above, it acts as &lt;code&gt;ComponentDidMount&lt;/code&gt;, in that it's only executed once on a render. That's perfect for our use case, as I want to call an async function called &lt;code&gt;getCurrentUser&lt;/code&gt; (which is using the AWS Cognito API in the background to fetch user data), and if the user is already logged in, use the &lt;code&gt;setUser&lt;/code&gt; hook to update the &lt;code&gt;user&lt;/code&gt; object. If not, nothing happens and the &lt;code&gt;user&lt;/code&gt; is still in the default state.&lt;/p&gt;

&lt;p&gt;Finally, we use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;setUser&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/UserContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to wrap all of the children elements with the context of &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;setUser&lt;/code&gt;. Then we export both &lt;code&gt;UserContext&lt;/code&gt; and &lt;code&gt;UserProvider&lt;/code&gt;, as we'll need both throughout our codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap that root
&lt;/h3&gt;

&lt;p&gt;So remember that example from the React docs that wrapped the root &lt;code&gt;App&lt;/code&gt; component in the Provider? Yeah, that's not gonna work with Gatsby. Luckily, Gatsby has a super-handy &lt;a href="https://www.gatsbyjs.com/docs/reference/config-files/gatsby-browser/#wrapRootElement" rel="noopener noreferrer"&gt;wrapRootElement&lt;/a&gt; API that basically does the same thing, and it's implemented in &lt;code&gt;gatsby-browser&lt;/code&gt; like so:&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="c1"&gt;// ./gatsby-browser.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/contexts/UserContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapRootElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;element&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProvider&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;element&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/UserProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all there is to it! Now each component will have access to the &lt;code&gt;UserProvider&lt;/code&gt; context.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the Provider provides, the Consumer consumes
&lt;/h3&gt;

&lt;p&gt;Next, we need a place for the &lt;code&gt;UserProvider&lt;/code&gt; Consumer. This should be a parent element to the child components that will need access to the &lt;code&gt;user&lt;/code&gt; context. In my codebase, I chose the &lt;code&gt;Layout&lt;/code&gt; component, as it wraps just about every page of the site and is where we have another Provider already, the &lt;code&gt;ThemeProvider&lt;/code&gt;. Your implementation may vary in this regard, but it's safe to say that most Gatsby starters do include a universal &lt;code&gt;Layout&lt;/code&gt; component of some sort.&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="c1"&gt;// ./src/layouts/Layout.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Fragment&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ThemeProvider&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;@emotion/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NavBar&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;layouts&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;UserContext&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;../contexts/UserContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ThemeProvider&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&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="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Consumer&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;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fragment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NavBar&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Footer&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Fragment&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/UserContext.Consumer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ThemeProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, it's possible to use multiple Providers and Consumers within one app, though we'll keep our focus on the &lt;code&gt;UserContext.Consumer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because we initialized&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;back in the context, we can access both &lt;code&gt;UserContext.Provider&lt;/code&gt; and &lt;code&gt;UserContext.Consumer&lt;/code&gt;. The code above just places the consumer above all the &lt;code&gt;children&lt;/code&gt; components of the app. Because of the way the Consumer is set up, it &lt;strong&gt;requires a function as a child&lt;/strong&gt;. That's why we have &lt;code&gt;&amp;lt;UserContext.Consumer&amp;gt; {user =&amp;gt; (...)}&amp;lt;/UserContext.Consumer&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that we're not passing the &lt;code&gt;user&lt;/code&gt; object to the &lt;code&gt;NavBar&lt;/code&gt; component here, though we very well could with &lt;code&gt;&amp;lt;NavBar user={props.user} setUser={props.setUser} /&amp;gt;&lt;/code&gt;. But then, how would we pass that same data to &lt;code&gt;{children}&lt;/code&gt;? That's where the handy &lt;code&gt;useContext()&lt;/code&gt; hook comes in!&lt;/p&gt;

&lt;h3&gt;
  
  
  Hooks or it didn't happen
&lt;/h3&gt;

&lt;p&gt;So we've got our Provider, we've got our Consumer in the site's &lt;code&gt;Layout&lt;/code&gt; component, and now we need to pull the &lt;code&gt;user&lt;/code&gt; object and, in some cases, the &lt;code&gt;setUser&lt;/code&gt; function from the global context. Let's start with our &lt;code&gt;NavBar&lt;/code&gt; component, which will either render a button or an icon depending on whether a user is logged in or not:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Logged out view&lt;/em&gt;&lt;br&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%2Fdn9vs2y41e51ajmrqc54.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%2Fdn9vs2y41e51ajmrqc54.png" alt="nav-logged-out" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Logged in view&lt;/em&gt; &lt;br&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%2F8kvnqwsppawwji2gcdjg.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%2F8kvnqwsppawwji2gcdjg.png" alt="nav-logged-in" width="800" height="169"&gt;&lt;/a&gt;&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="c1"&gt;// ./src/layouts/NavBar.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useContext&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;react&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;UserContext&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;../contexts/UserContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signOut&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;../utils/cognito&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NavBar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserContext&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;handleSignOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;loggedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;userid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Nav&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navbar navbar-expand-lg fixed-top&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navbar-brand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&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;other&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nav-item nav-button&lt;/span&gt;&lt;span class="dl"&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;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedIn&lt;/span&gt;
              &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserIcon&lt;/span&gt; &lt;span class="nx"&gt;handleSignOut&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSignOut&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://app.stackery.io/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sign In&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Nav&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Header&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's start right at the top: we've got our &lt;code&gt;useContext&lt;/code&gt; hook, and like a magical fairy pulling a bag of gold out of thin air, &lt;code&gt;useContext&lt;/code&gt; pulls &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;setUser&lt;/code&gt; out of the React ether and assures us that they're the global values our app depends on!&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%2F61evzk7x89vylmih9w5b.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%2F61evzk7x89vylmih9w5b.gif" alt="Magic" width="480" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So with &lt;code&gt;const { user, setUser } = useContext(UserContext);&lt;/code&gt;, we can now see if the &lt;code&gt;user&lt;/code&gt; object exists, and if &lt;code&gt;user.loggedIn&lt;/code&gt; is true, we'll show a component called &lt;code&gt;UserIcon&lt;/code&gt; that shows the user icon and has a dropdown that allows the user to sign out. And as we can see in &lt;code&gt;handleSignOut()&lt;/code&gt;, we use &lt;code&gt;setUser&lt;/code&gt; to update the global user state back to the defaults when the user has signed out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context in class components
&lt;/h3&gt;

&lt;p&gt;Finally, we've got a class component where we also need access to the &lt;code&gt;user&lt;/code&gt; object and &lt;code&gt;setUser&lt;/code&gt; function. There are two options for this: if you have a direct parent component that's a functional component, you can pass those values as props like so:&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="c1"&gt;// ./src/pages/Registry.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useContext&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RegistryContainer&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;components/registry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Layout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RegistryContainer&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Layout&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Registry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in &lt;code&gt;RegistryContainer&lt;/code&gt;, we access &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;setUser&lt;/code&gt; just as we would any other props in a class component:&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="c1"&gt;// ./src/components/registry/RegistryContainer.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegistryContainer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;componentDidUpdate &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loggedIn&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;githubAuthState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// do the oauth things!&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
      &lt;span class="c1"&gt;// then update global user&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;githubAuthState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;githubAuthStates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTHED&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nf"&gt;render &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RegistryForm&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Option two, which I did not end up implementing, uses the &lt;a href="https://reactjs.org/docs/context.html#classcontexttype" rel="noopener noreferrer"&gt;contextType&lt;/a&gt; class property, and would look something like this:&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="c1"&gt;// example from https://www.taniarascia.com/using-context-api-in-react/&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&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;react&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;UserContext&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;../contexts/UserContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;contextType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;componentDidMount&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// { name: 'Tania', loggedIn: true }&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Either way should work depending on your codebase, I just went for the clean and simple &lt;code&gt;useContext()&lt;/code&gt; option throughout.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cake is not a lie!
&lt;/h2&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%2Frzdecsmn1jc7wutuhhgr.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%2Frzdecsmn1jc7wutuhhgr.gif" alt="Cake cat gif" width="300" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there we go: we've got access to our &lt;code&gt;user&lt;/code&gt; anywhere we fancy on our Gatsby site, and all it took was a little bit of Context.&lt;/p&gt;

&lt;p&gt;So now, if a user is logged in and on the Registry page, they'll see the same state in two different components:&lt;/p&gt;

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

&lt;p&gt;And if they're logged out, all the components know:&lt;br&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%2Fv30f0zifvl4squs5u3br.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%2Fv30f0zifvl4squs5u3br.png" alt="logged-out-state" width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope this is helpful to future Gatsby tinkerers, and feel free to ask questions or point out bugs (no, using semi-standard is not a bug) in the comments. And if you'd like to see this functionality live in the wild, check out &lt;a href="https://www.stackery.io/registry/" rel="noopener noreferrer"&gt;stack.new&lt;/a&gt; for yourself! &lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>react</category>
      <category>webdev</category>
      <category>wecoded</category>
    </item>
    <item>
      <title>Alexa, How Can I Win an Echo (and learn to build an Alexa app)?</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Tue, 15 Dec 2020 18:03:58 +0000</pubDate>
      <link>https://forem.com/annaspies/alexa-how-can-i-win-an-echo-and-learn-to-build-an-alexa-app-52c</link>
      <guid>https://forem.com/annaspies/alexa-how-can-i-win-an-echo-and-learn-to-build-an-alexa-app-52c</guid>
      <description>&lt;h2&gt;
  
  
  Build an Alexa Skill with Stackery for re:Invent and get an Amazon Echo Dot!
&lt;/h2&gt;

&lt;p&gt;As this year's virtual, three-week AWS-palooza that is re:Invent 2020 wraps up, I'm sure many of you are as eager as I am to implement some of that new knowledge.&lt;/p&gt;

&lt;p&gt;One thing I learned just before re:Invent that I thought I'd share is how to create and deploy an Alexa Skill. Turns out, if you have a tutorial to follow, it's not too hard at all!&lt;/p&gt;

&lt;p&gt;So my company decided to encourage others to get into the Skills game by sending out Echos to anyone who deploys. So, want to learn something new and get some free stuff? Read on!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to enter and get an Echo Dot
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Follow the tutorial below to build an Alexa quiz game Skill on AWS, using Stackery to deploy your serverless function that will serve as the Skill backend&lt;/li&gt;
&lt;li&gt;You can deploy the code as-is, or modify the content of the quiz - it's about AWS services, but you can make it about any topic you'd like!&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/stackeryio" rel="noopener noreferrer"&gt;Tweet us&lt;/a&gt; a screenshot of your deployed backend. It'll look something like this:&lt;/li&gt;
&lt;/ol&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%2Fi%2Fpfes6eh07mre8hoenlhg.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%2Fi%2Fpfes6eh07mre8hoenlhg.png" alt="alexa1" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh, and bonus points for playing the quiz game - find it here: &lt;a href="https://www.amazon.com/dp/B08QMSCTHG/ref=sr_1_1?dchild=1&amp;amp;keywords=stackery&amp;amp;qid=1608054745&amp;amp;s=digital-skills&amp;amp;sr=1-1" rel="noopener noreferrer"&gt;Stackery re:Invent Quiz Alexa Skill&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sound good? Then let's get to it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, I have questions!
&lt;/h2&gt;

&lt;p&gt;Ok, let's have them:&lt;/p&gt;

&lt;h4&gt;
  
  
  What's an Alexa Skill?
&lt;/h4&gt;

&lt;p&gt;A Skill is a voice app that can be opened on an Alexa-enabled device, such as a smart speaker, a smart home device that's Alexa-enabled, a smartwatch... basically, if it has the word "smart" in its name, it probably supports Alexa.&lt;/p&gt;

&lt;h4&gt;
  
  
  What does this Skill do?
&lt;/h4&gt;

&lt;p&gt;This Skill asks users questions about AWS services, such as Lambda, Cognito or EC2, in a 10-question quiz game. It can also give users facts about certain AWS services.&lt;/p&gt;

&lt;h4&gt;
  
  
  Do I need to be an AWS expert to follow this tutorial?
&lt;/h4&gt;

&lt;p&gt;Nope! Stackery makes it easy for &lt;em&gt;anyone&lt;/em&gt; to deploy serverless apps on AWS, and adding infrastructure is as simple as dragging and dropping! The only thing you need is an AWS account, a modern browser, and a computer connected to the Internet.&lt;/p&gt;

&lt;h4&gt;
  
  
  Do I need to be a programming ninja to follow this tutorial?
&lt;/h4&gt;

&lt;p&gt;Again, nope! If you can copy-paste, you can build and deploy this app. If you do want to customize the quiz, you will need to know JavaScript/NodeJS at a basic level.&lt;/p&gt;

&lt;h4&gt;
  
  
  Do I need an Alexa-enabled device to test my Skill?
&lt;/h4&gt;

&lt;p&gt;Not at all - the Alexa developer console includes an in-browser Alexa simulator for testing. Of course, if you deploy and share your Alexa Skill, we'll send you an Echo Dot to test your skill out in the wild.&lt;/p&gt;

&lt;h4&gt;
  
  
  Will this make my AWS bill go crazy?
&lt;/h4&gt;

&lt;p&gt;Not if you follow the tutorial. Everything here should fit easily within AWS's free tier, and both the AWS account and Amazon Developer account you'll need for this tutorial are free.&lt;/p&gt;

&lt;h4&gt;
  
  
  Can I make Alexa speak in a sarcastic tone?
&lt;/h4&gt;

&lt;p&gt;Sadly, no (believe me, I tried). But there are some cool things you can do with &lt;a href="https://developer.amazon.com/en-US/docs/alexa/custom-skills/speech-synthesis-markup-language-ssml-reference.html" rel="noopener noreferrer"&gt;SSML&lt;/a&gt;, the markup language used to dictate how Alexa generates phrases.&lt;/p&gt;

&lt;h4&gt;
  
  
  Are there terms &amp;amp; conditions you legally need to include in the post?
&lt;/h4&gt;

&lt;p&gt;Of course there are!&lt;/p&gt;

&lt;p&gt;Terms and conditions apply. While supplies last. Amazon gift cards for the cost of one Amazon Echo will be sent within 3 business days of deployment. Additional shipping charges may apply depending on location.&lt;/p&gt;

&lt;h4&gt;
  
  
  Where can I learn more about building Alexa Skills?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://developer.amazon.com/en-US/docs/alexa/ask-overviews/build-skills-with-the-alexa-skills-kit.html" rel="noopener noreferrer"&gt;Alexa Developer docs&lt;/a&gt; are a great place to start&lt;/li&gt;
&lt;li&gt;This tutorial is largely based on and borrows generously from &lt;a href="https://github.com/alexa/skill-sample-nodejs-quiz-game" rel="noopener noreferrer"&gt;this Skill sample&lt;/a&gt;, and I recommend diving into that codebase if you want to go further&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, no more questions? Then...&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get to it!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create an Alexa Skill
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;For this step you'll need a &lt;a href="https://developer.amazon.com/en-US/alexa/alexa-skills-kit" rel="noopener noreferrer"&gt;free Amazon Developer account&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to the &lt;a href="https://developer.amazon.com/alexa/console/ask" rel="noopener noreferrer"&gt;Alexa Developer Console&lt;/a&gt;, and click the &lt;strong&gt;Create Skill&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Name your skill whatever you'd like&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Custom&lt;/strong&gt; as your model&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Provision your own&lt;/strong&gt; as the method to host your skill's backend resources&lt;/li&gt;
&lt;li&gt;Scroll back up, make sure everything looks right, and click &lt;strong&gt;Create skill&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You'll now see your newly-created skill in a list of all skills:&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%2Fi%2Fg3meyk3apw8rc15tecju.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%2Fi%2Fg3meyk3apw8rc15tecju.png" alt="alexa2" width="800" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the skill name, and navigate to &lt;strong&gt;Interaction Model&lt;/strong&gt; -&amp;gt; &lt;strong&gt;JSON editor&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is where you will paste a JSON file that describes the different forms of interactions your Skill will have with users, as well as the custom data that serves as the allowable answers to quiz questions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy the entire contents of &lt;a href="https://raw.githubusercontent.com/stackery/alexa-reinvent-quiz/master/src/models/en-US.json" rel="noopener noreferrer"&gt;this JSON file&lt;/a&gt; from our &lt;a href="https://github.com/stackery/alexa-reinvent-quiz" rel="noopener noreferrer"&gt;alexa-reinvent-quiz repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Paste the copied contents into the Alexa JSON editor, then click &lt;strong&gt;Save Model&lt;/strong&gt; at the top&lt;/li&gt;
&lt;/ol&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%2Fi%2F3tni1mpqb7c43ax85pso.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%2Fi%2F3tni1mpqb7c43ax85pso.png" alt="alexa3" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you save, you will see that your Intents were auto-populated. Feel free to poke around and view the sample utterances and &lt;a href="https://developer.amazon.com/en-US/docs/alexa/custom-skills/slot-type-reference.html" rel="noopener noreferrer"&gt;slot types&lt;/a&gt; that are there now. When you're done, return to the main console where all of your skills are listed, as you'll need to get your Skill ID in a few moments, so be sure to leave this browser tab open.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Build your backend in Stackery
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;For this step you'll need a &lt;a href="https://www.stackery.io/sign-up/" rel="noopener noreferrer"&gt;free Stackery account&lt;/a&gt;, a Git provider, and a code editor. If you're a first-time Stackery user, you'll need to link your Git provider and AWS account the first time you commit and deploy a stack. Don't worry, the process is fairly quick and simple and the app will walk you through it.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In a new tab, log in to Stackery, and create a new stack with a new repo&lt;/li&gt;
&lt;/ol&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%2Fi%2Fxnsv0khi5xc5mgottsf4.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%2Fi%2Fxnsv0khi5xc5mgottsf4.gif" alt="alexa-step1-small" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Visual edit mode, add a function and give it the name &lt;code&gt;AlexaHandler&lt;/code&gt; and change its code source directory to &lt;code&gt;src/AlexaHandler&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&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%2Fi%2Fdga1528qtm6or0ya1dpv.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%2Fi%2Fdga1528qtm6or0ya1dpv.gif" alt="alexa-step2-small" width="600" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Flip to the Template edit mode, and add the following YAML as part of the &lt;code&gt;Properties&lt;/code&gt; of your &lt;code&gt;AlexaHandler&lt;/code&gt; function:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AlexaSkillEvent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AlexaSkill&lt;/span&gt;
          &lt;span class="na"&gt;SkillId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;your-skill-id&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;your-skill-id&amp;gt;&lt;/code&gt; with the &lt;code&gt;Skill ID&lt;/code&gt; you noted above&lt;sup&gt;1&lt;/sup&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is what allows your Lambda function to be accessed by the specific skill you're building, and not any other.&lt;/p&gt;

&lt;p&gt;Your template should look now something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AlexaHandler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::StackName}-AlexaHandler&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWSXrayWriteOnlyAccess&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AlexaSkillEvent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AlexaSkill&lt;/span&gt;
          &lt;span class="na"&gt;SkillId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amzn1.ask.skill.some-long-numbers-and-letters&lt;/span&gt;
&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to double-check your formatting, you can refer to our &lt;a href="https://github.com/stackery/alexa-reinvent-quiz/blob/master/template.yaml" rel="noopener noreferrer"&gt;SAM template&lt;/a&gt; in the tutorial repo.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If everything looks right, click the &lt;strong&gt;Commit...&lt;/strong&gt; button to commit your changes to your Git repository&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Follow the repo link below the stack name to access your newly-created repository&lt;br&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%2Fi%2Ffnadqfl9ousimgnc5gvu.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%2Fi%2Ffnadqfl9ousimgnc5gvu.png" alt="alexa4" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clone your repo to your computer and open it in your favorite (or least-favorite, we're not particular) IDE&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Add function code
&lt;/h3&gt;

&lt;p&gt;When you created a function in Stackery, it stubbed out some function code for you in the chosen runtime, which is Node 12 in the case of this tutorial. We're going to replace the function code with the Alexa backend from the &lt;a href="https://github.com/stackery/alexa-reinvent-quiz" rel="noopener noreferrer"&gt;tutorial repo&lt;/a&gt;, as well as its &lt;code&gt;package.json&lt;/code&gt; contents to add the required dependencies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;your-repo/src/AlexaHandler/index.js&lt;/code&gt; and replace its contents with the contents of &lt;a href="https://raw.githubusercontent.com/stackery/alexa-reinvent-quiz/master/src/AlexaHandler/index.js" rel="noopener noreferrer"&gt;Stackery's Alexa Skill code&lt;/a&gt;. Save the file&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;your-repo/src/AlexaHandler/package.json&lt;/code&gt; and replace its contents with the following and save:
&lt;/li&gt;
&lt;/ol&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;"alexahandler"&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;"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;"Stackery"&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;"MIT"&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;"aws-sdk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.796.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="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;"ask-sdk-core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.9.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;"ask-sdk-model"&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.34.1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Commit the changes and push to your Git repo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you kept your Stackery tab open, you'll have noticed that it detected the changes you pushed up. Go ahead and hit the refresh link:&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%2Fi%2Fkifzz9gi2buhkt7thbgq.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%2Fi%2Fkifzz9gi2buhkt7thbgq.png" alt="alexa5" width="800" height="579"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  4. Deploy to AWS
&lt;/h3&gt;

&lt;p&gt;That's it for the actual coding portion of the tutorial, until you're ready to play around with the code itself to alter the quiz. But let's not get ahead of ourselves: first, we deploy!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Stackery app, navigate to the &lt;strong&gt;Deploy&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Select an environment to deploy to (if this is your first time deploying with Stackery, you'll be guided through linking to AWS first). Click &lt;strong&gt;Prepare new deployment&lt;/strong&gt; and then &lt;strong&gt;Prepare&lt;/strong&gt; to start your deployment&lt;/li&gt;
&lt;/ol&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%2Fi%2Fk429ix0fuz7y7hn3zq42.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%2Fi%2Fk429ix0fuz7y7hn3zq42.gif" alt="alexa-step3" width="600" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Once the deployment is prepared (which will take about a minute), click &lt;strong&gt;Deploy&lt;/strong&gt;. A tab will open in your AWS Console, where you'll need to click &lt;strong&gt;Execute&lt;/strong&gt; to kick off the deployment&lt;sup&gt;2&lt;/sup&gt;
&lt;/li&gt;
&lt;/ol&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%2Fi%2Fpn0hgrskmyqwbpxjiggs.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%2Fi%2Fpn0hgrskmyqwbpxjiggs.gif" alt="alexa-step4" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will take a few minutes - get yourself a coffee and a pat on the back, because you're 90% done with deploying your first Alexa Skill!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You'll get a notification when your stack has deployed. Click the &lt;strong&gt;View&lt;/strong&gt; tab to see your live stack and grab your screenshot for Twitter!&lt;/li&gt;
&lt;li&gt;While you're at it, double-click the &lt;code&gt;AlexaHandler&lt;/code&gt; function to pull up some handy data and links. Copy the function's ARN, as you'll need it for the final step of this tutorial
&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%2Fi%2Fdqp708sx2d2hg51bxkbi.gif" alt="alexa-step5" width="720" height="398"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5. Connect your backend to your Skill
&lt;/h3&gt;

&lt;p&gt;This is it: the final stage, when we connect all the dots and test our Alexa Skill!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Back in the Amazon Developer Console, select &lt;strong&gt;Endpoint&lt;/strong&gt; from the menu&lt;/li&gt;
&lt;li&gt;Enter the ARN you copied in the previous step like so:&lt;/li&gt;
&lt;/ol&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%2Fi%2Fharzmeoigkmtec5n38pn.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%2Fi%2Fharzmeoigkmtec5n38pn.png" alt="alexa6" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Save Endpoints&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In the side tab, return to &lt;strong&gt;Invocation&lt;/strong&gt; and click &lt;strong&gt;Save Model&lt;/strong&gt;, then &lt;strong&gt;Build Model&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you're ready to test your Skill! Navigate to &lt;strong&gt;Test&lt;/strong&gt;, and say or type "Start Stackery re:Invent Quiz" to kick off the quiz. You can try the quiz yourself, or get some trivia information about specific AWS services. Knock yourself out - this is the fun part!&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%2Fi%2Fcyldvvscal114x3di7et.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%2Fi%2Fcyldvvscal114x3di7et.png" alt="alexa7" width="800" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you return to the Stackery Dashboard, you'll even notice that your function was successfully invoked while you were testing!&lt;br&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%2Fi%2Fs0qwlvoa96xmbq9sm96j.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%2Fi%2Fs0qwlvoa96xmbq9sm96j.png" alt="alexa8" width="800" height="419"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Hopefully, this little tutorial has piqued your interests in building Alexa Skills. With a Lambda backend, you can build skills in just about any runtime, and Stackery helps you deploy changes quickly (and automatically if you use our &lt;a href="https://docs.stackery.io/docs/using-stackery/dashboard#deployment-pipeline" rel="noopener noreferrer"&gt;Deployment Pipelines&lt;/a&gt;). I'd love to see what you build - feel free to send your projects &lt;a href="https://twitter.com/annaspies" rel="noopener noreferrer"&gt;my way on Twitter&lt;/a&gt;, and of course remember to send your deployed stack to &lt;a href="https://twitter.com/stackeryio" rel="noopener noreferrer"&gt;Stackery on Twitter&lt;/a&gt; and we'll DM you to get the deets for your Echo. Happy deploying!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;sup&gt;1&lt;/sup&gt; For the sake of this tutorial, we are hard-coding the Skill ID. &lt;strong&gt;If you are saving your Skill ID directly in the template, make sure your repo is private.&lt;/strong&gt; Alternatively, you can use Stackery's &lt;a href="https://docs.stackery.io/docs/using-stackery/environments#setting-parameter-store-values" rel="noopener noreferrer"&gt;Environments and Parameter Store&lt;/a&gt; to follow best practices and store your Skill ID as in AWS's &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html" rel="noopener noreferrer"&gt;Systems Manager Parameter Store&lt;/a&gt; and reference it at build time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;sup&gt;2&lt;/sup&gt; This tutorial walks you through deploying manually in the browser, but there are other ways to deploy that will likely suit your workflow better. You can deploy with the &lt;a href="https://docs.stackery.io/docs/using-stackery/cli" rel="noopener noreferrer"&gt;Stackery CLI&lt;/a&gt; with just one command, or completely automate this process upon a merge to the repo's main branch, including automated test runs, with Stackery's &lt;a href="https://docs.stackery.io/docs/using-stackery/dashboard#deployment-pipeline" rel="noopener noreferrer"&gt;Deployment Pipelines&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>alexa</category>
      <category>tutorial</category>
      <category>aws</category>
    </item>
    <item>
      <title>I used Cypress as an Xbox web scraper and I regret nothing</title>
      <dc:creator>Anna</dc:creator>
      <pubDate>Wed, 02 Dec 2020 17:41:10 +0000</pubDate>
      <link>https://forem.com/annaspies/i-used-cypress-as-an-xbox-web-scraper-and-i-regret-nothing-1bn4</link>
      <guid>https://forem.com/annaspies/i-used-cypress-as-an-xbox-web-scraper-and-i-regret-nothing-1bn4</guid>
      <description>&lt;p&gt;Like many people, I would like to get my hands on the &lt;a href="https://www.xbox.com/en-US/consoles/xbox-series-x" rel="noopener noreferrer"&gt;new Xbox&lt;/a&gt;. And like everyone but the most diligent online shoppers, I have so far failed in my efforts to do so, and have instead been relentlessly greeted by images such as this one:&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%2Fi%2Fv6aju8curl42kxqqnct1.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%2Fi%2Fv6aju8curl42kxqqnct1.png" alt="costco" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what does an enterprising/desperate web developer do? Build their own alert system, of course!&lt;/p&gt;

&lt;p&gt;Now, a web scraper is a pretty simple application and generally the ideal use case for this sort of thing. But I wanted to add a visual element to it, to make sure I wasn't getting false positives, and because I tend to prefer user interfaces over bare code (I do work at &lt;a href="https://www.stackery.io/" rel="noopener noreferrer"&gt;Stackery&lt;/a&gt;, after all). Also, I've been playing with the &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; test suite for the past month or so, and absolutely love it for frontend testing, so I've been looking for more ways to implement it in my projects.&lt;/p&gt;

&lt;p&gt;Now, I should say: I'm guessing this is &lt;em&gt;not exactly&lt;/em&gt; the use case the devs at Cypress.io had in mind when they built the browser-based testing library, but as the famous saying goes, "You can invent a hammer, but you can't stop the first user from using it to hit themselves in the head&lt;sup&gt;1&lt;/sup&gt;".&lt;/p&gt;

&lt;p&gt;So without further ado, let's hit ourselves in the proverbial head and get that Xbox!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup: get yourself a Cypress account
&lt;/h2&gt;

&lt;p&gt;Cypress has a very neat feature that allows you to view videos from your automated test runs in their web app. In order to do so, you'll need a free developer account:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://dashboard.cypress.io/signup" rel="noopener noreferrer"&gt;Cypress sign-up page&lt;/a&gt; and create an account&lt;/li&gt;
&lt;li&gt;Once you're in their dashboard, go ahead and create a new project. Name it "Xbox stock scraper", "testing abomination", or whatever you'd like. I generally name my projects the same as my repo, because that's how my brain works&lt;/li&gt;
&lt;li&gt;Now, you'll want to take note of the &lt;code&gt;projectId&lt;/code&gt; as well as the record &lt;code&gt;key&lt;/code&gt;, as you'll need this later&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Create a serverless stack for your scraper
&lt;/h2&gt;

&lt;p&gt;Because store inventories changes frequently, we'll want to run our scraper regularly - every hour to start, though it's easy to adjust that up or down as you see fit. Of course, we want to automate these runs, because the whole point is that you have a life and are trying to avoid refreshing web pages on the reg. Is it me, or is this starting to sound like an ideal serverless use case? Not just me? Thought so!&lt;/p&gt;

&lt;p&gt;I originally wanted to run the whole thing in a Lambda, but after an hours-long rabbit-hole, I &lt;a href="https://github.com/cypress-io/cypress/issues/1232" rel="noopener noreferrer"&gt;found out&lt;/a&gt; that's really, really hard, and ultimately not worth it when a &lt;a href="https://aws.amazon.com/codebuild/" rel="noopener noreferrer"&gt;CodeBuild job&lt;/a&gt; will do the trick just fine.&lt;/p&gt;

&lt;p&gt;I'll be using Stackery to build my stack, so these instructions go through that workflow. This part is optional, as you can also do this in the AWS Console, but I like doing things the easy way, and Stackery is serverless on easy mode&lt;sup&gt;2&lt;/sup&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you don't already have one, &lt;a href="https://www.stackery.io/sign-up" rel="noopener noreferrer"&gt;create a free Stackery account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to /stacks, and click the &lt;strong&gt;Add a Stack&lt;/strong&gt; dropdown arrow to select &lt;strong&gt;With a new repo&lt;/strong&gt;. Here's what that looks like for me:&lt;br&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%2Fi%2Fyzpvxn01byuqmhh4hy52.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%2Fi%2Fyzpvxn01byuqmhh4hy52.png" alt="xbox-1" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Normally, you'd add resources one by one in the Design Canvas, but as this stack is mainly based on a CodeBuild job and related roles, it's easier to copy-pasta an AWS SAM template like so:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fi%2F2f9cnlxiovf11vziijhh.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%2Fi%2F2f9cnlxiovf11vziijhh.gif" alt="xbox-compressed" width="560" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Edit Mode&lt;/strong&gt;, click &lt;strong&gt;Template&lt;/strong&gt;, clear out the existing template, and paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;SendMessage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::StackName}-SendMessage&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Stack ${StackTagName} Environment ${EnvironmentTagName} Function ${ResourceName}&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ResourceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SendMessage&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/SendMessage&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs12.x&lt;/span&gt;
      &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3008&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
      &lt;span class="na"&gt;Tracing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Active&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWSXrayWriteOnlyAccess&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;SNSPublishMessagePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;TopicName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;XboxAlert.TopicName&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;EventRule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EventBridgeRule&lt;/span&gt;
          &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws.codebuild&lt;/span&gt;
              &lt;span class="na"&gt;detail-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CodeBuild Build State Change&lt;/span&gt;
              &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;build-status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SUCCEEDED&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FAILED&lt;/span&gt;
                &lt;span class="na"&gt;project-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cypress-xbox-scraper&lt;/span&gt;
          &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;StackeryName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TriggerMessage&lt;/span&gt;
      &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;TOPIC_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;XboxAlert.TopicName&lt;/span&gt;
          &lt;span class="na"&gt;TOPIC_ARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;XboxAlert&lt;/span&gt;
  &lt;span class="na"&gt;CodeBuildIAMRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
          &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codebuild.amazonaws.com&lt;/span&gt;
          &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
      &lt;span class="na"&gt;RoleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::StackName}-CodeBuildIAMRole&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::aws:policy/AdministratorAccess&lt;/span&gt;
  &lt;span class="na"&gt;CypressScraper&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NO_ARTIFACTS&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cypress Xbox Scraper&lt;/span&gt;
      &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ComputeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BUILD_GENERAL1_SMALL&lt;/span&gt;
        &lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws/codebuild/standard:2.0&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LINUX_CONTAINER&lt;/span&gt;
        &lt;span class="na"&gt;PrivilegedMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cypress-xbox-scraper&lt;/span&gt;
      &lt;span class="na"&gt;ServiceRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CodeBuildIAMRole&lt;/span&gt;
      &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BuildSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;buildspec.yml&lt;/span&gt;
        &lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/&amp;lt;github-user&amp;gt;/&amp;lt;repo-name&amp;gt;.git&lt;/span&gt;
        &lt;span class="na"&gt;SourceIdentifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BUILD_SCRIPTS_SRC&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GITHUB&lt;/span&gt;
        &lt;span class="na"&gt;Auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OAUTH&lt;/span&gt;
  &lt;span class="na"&gt;CypressScraperTriggerIAMRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
          &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;events.amazonaws.com&lt;/span&gt;
          &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TriggerCypressScraperCodeBuild&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;codebuild:StartBuild&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;codebuild:BatchGetBuilds&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;CypressScraper.Arn&lt;/span&gt;
      &lt;span class="na"&gt;RoleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::StackName}-CypressScraperTriggerRole&lt;/span&gt;
  &lt;span class="na"&gt;TriggerScraper&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Events::Rule&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ScheduleExpression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rate(1 hour)&lt;/span&gt;
      &lt;span class="na"&gt;State&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
      &lt;span class="na"&gt;RoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;CypressScraperTriggerIAMRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;CypressScraper.Arn&lt;/span&gt;
          &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cypress-xbox-scraper&lt;/span&gt;
          &lt;span class="na"&gt;RoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;CypressScraperTriggerIAMRole.Arn&lt;/span&gt;
  &lt;span class="na"&gt;XboxAlert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SNS::Topic&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;TopicName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::StackName}-XboxAlert&lt;/span&gt;
&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;StackTagName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Stack Name (injected by Stackery at deployment time)&lt;/span&gt;
  &lt;span class="na"&gt;EnvironmentTagName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Environment Name (injected by Stackery at deployment time)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down a bit. For those new to serverless, this is an &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html" rel="noopener noreferrer"&gt;AWS SAM&lt;/a&gt; template. While using Stackery means you generally can avoid writing template files, there are a few things worth noting, and one line you'll need to input your own data into.&lt;/p&gt;

&lt;p&gt;We'll start with lines 55-74:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;CypressScraper&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CodeBuild::Project&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NO_ARTIFACTS&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cypress Xbox Scraper&lt;/span&gt;
      &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ComputeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BUILD_GENERAL1_SMALL&lt;/span&gt;
        &lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws/codebuild/standard:2.0&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LINUX_CONTAINER&lt;/span&gt;
        &lt;span class="na"&gt;PrivilegedMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cypress-xbox-scraper&lt;/span&gt;
      &lt;span class="na"&gt;ServiceRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CodeBuildIAMRole&lt;/span&gt;
      &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BuildSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;buildspec.yml&lt;/span&gt;
        &lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/&amp;lt;github-user&amp;gt;/&amp;lt;repo-name&amp;gt;.git&lt;/span&gt;
        &lt;span class="na"&gt;SourceIdentifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BUILD_SCRIPTS_SRC&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GITHUB&lt;/span&gt;
        &lt;span class="na"&gt;Auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OAUTH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the CodeBuild project that will be created to run Cypress in a Linux container in one of AWS's magical server estates. &lt;strong&gt;You'll need to replace line 70 with the Git repo you just created.&lt;/strong&gt; This also means you may need to authenticate your Git provider with AWS, but I'll walk you through that a bit later.&lt;/p&gt;

&lt;p&gt;Line 101 is where you can change the frequency at which messages are sent. Learn more about &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html" rel="noopener noreferrer"&gt;AWS schedule expressions here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, if you switch back to &lt;strong&gt;Visual&lt;/strong&gt; mode, you'll see that several resources were just auto-magically populated from the template:&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%2Fi%2F81secxao3htxksg6kp2r.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%2Fi%2F81secxao3htxksg6kp2r.png" alt="xbox-3" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TriggerScraper&lt;/code&gt;: The CloudWatch event rule that triggers the Cypress CodeBuild job every hour&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TriggerMessage&lt;/code&gt;: The EventBridge Rule that triggers the &lt;code&gt;SendMessage&lt;/code&gt; function once the CodeBuild job succeeds or fails&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SendMessage&lt;/code&gt;: The Lambda function that sends a the SNS message if Xboxes are back in stock&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;XboxAlert&lt;/code&gt;: The SNS topic for sending SMS messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can double-click each resource to see its individual settings.&lt;/p&gt;

&lt;p&gt;Look at that: a whole backend, and you didn't even have to open the AWS Console!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hit the &lt;strong&gt;Commit...&lt;/strong&gt; button to commit this to your Git repo, then follow the link below the stack name to your new repo URL, clone the stack locally, and open it in your favorite VSCode (or other text editor, if you must)&lt;/li&gt;
&lt;/ol&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%2Fi%2Flsj77p3hnxc0rbszwph3.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%2Fi%2Flsj77p3hnxc0rbszwph3.png" alt="xbox-2" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  To the code!
&lt;/h2&gt;

&lt;p&gt;As you can see, Stackery created some directories for your function, as well as an AWS SAM template you'll be able to deploy. Thanks, Stackery!&lt;/p&gt;

&lt;p&gt;First we'll want to add Cypress:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From the root of your repo, run &lt;code&gt;npm install cypress --save&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Once it's installed, run &lt;code&gt;./node_modules/.bin/cypress open&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cypress will create its own directory, with a bunch of example code. You can go ahead and delete &lt;code&gt;cypress/integration/examples&lt;/code&gt; and create &lt;code&gt;cypress/integration/scraper.spec.js&lt;/code&gt;. Here's what will go in there:&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="c1"&gt;// xbox-stock-alert/cypress/integration/scraper.spec.js&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Xbox out-of-stock scraper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checks to see if Xboxes are out of stock at Microsoft&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.xbox.com/en-us/configure/8WJ714N3RBTL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gzip, deflate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keepAlive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[aria-label="Checkout bundle"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.disabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break that down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cypress will visit a specific URL - in this case, it's the product page of the Xbox Series X console&lt;/li&gt;
&lt;li&gt;The added headers allow the page to actually load without the dreaded &lt;a href="https://github.com/cypress-io/cypress/issues/943" rel="noopener noreferrer"&gt;ESOCKETTIMEDOUT error&lt;/a&gt; (I found this out the hard way, so you don't have to!)&lt;/li&gt;
&lt;li&gt;Cypress looks for an element with the &lt;code&gt;aria-label&lt;/code&gt; "Checkout bundle" and checks if it's disabled. If it is, the test ends and it is considered successful. If it isn't, the test ends as a failure (but we all know it tried really, really hard)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, why the specific "Checkout bundle" element? Well, if you go to the Xbox page in your browser and inspect it, you'll see that it's actually the checkout button that would be enabled were the Xbox in stock:&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%2Fi%2Fqfjgva9a4yazrm2gvizy.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%2Fi%2Fqfjgva9a4yazrm2gvizy.png" alt="Checkout button" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's automate this sh*t!
&lt;/h2&gt;

&lt;p&gt;Ok, we've got our test, and we've got a chron timer set to run once an hour. Now we need to add the CodeBuild job that actually runs this test. We also need to add code to our &lt;code&gt;SendMessage&lt;/code&gt; function that notifies us if the test failed, meaning the checkout button is enabled and we're one step closer to new Xbox bliss.&lt;/p&gt;

&lt;p&gt;Remember that Cypress &lt;code&gt;projectId&lt;/code&gt; and record &lt;code&gt;key&lt;/code&gt; you noted forever ago? Here's where those come in.&lt;/p&gt;

&lt;p&gt;Create a new file in the root directory called &lt;code&gt;buildspec.yml&lt;/code&gt; and add the following and save&lt;sup&gt;3&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;
&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runtime-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;nodejs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install &amp;amp;&amp;amp; npm run cypress -- --headless --browser electron --record --key &amp;lt;your-record-key&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up &lt;code&gt;cypress.json&lt;/code&gt; and replace it with the following and save:&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;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.xbox.com/en-us/configure/8WJ714N3RBTL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"defaultCommandTimeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chromeWebSecurity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projectId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-projectId&amp;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;Next, we'll add the function code that sends an alert should the test fail. Open up &lt;code&gt;src/SendMessage/index.js&lt;/code&gt; and replace it with the following:&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="c1"&gt;// xbox-stock-alert/src/SendMessage/index.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SNS&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-west-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Xbox alert! Click me now: https://www.xbox.com/en-us/configure/8WJ714N3RBTL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No Xboxes available, try again later&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Log the event argument for debugging and for use in local development&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// If the CodeBuild job was successful, that means Xboxes are not in stock and no message needs to be sent&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build-status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUCCEEDED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultMessage&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build-status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FAILED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If the CodeBuild job failed, that means Xboxes are back in stock!&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sending message: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create SNS parameters&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* required */&lt;/span&gt;
      &lt;span class="na"&gt;TopicArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TOPIC_ARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;MessageAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AWS.SNS.SMS.SMSType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;String&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;StringValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Promotional&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AWS.SNS.SMS.SenderID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;String&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;StringValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;XboxAlert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&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;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Message sent! Xbox purchase, commence!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sending failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh, and while you're at it, you may want to add &lt;code&gt;node_modules&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt;, unless polluting Git repos is your thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to deploy this bad boy
&lt;/h2&gt;

&lt;p&gt;Be sure to git add, commit, and push your changes. When deploying, AWS will need access to your Git provider. &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/sample-access-tokens.html" rel="noopener noreferrer"&gt;Follow these instructions&lt;/a&gt; to set up access tokens in your account if you've never done that before. (&lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/sample-access-tokens.html" rel="noopener noreferrer"&gt;This doc&lt;/a&gt; might also come in handy for noobs like me).&lt;/p&gt;

&lt;p&gt;If you're using Stackery to deploy, like the smart and also good-looking developer you are, all you need to do is run the following command in the root of your repo:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This will take a few minutes, during which time you can daydream about how awesome that new Xbox is going to be once it's hooked up to your 4K TV.&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%2Fup7f1xwll1wv1p4p2r9c.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%2Fup7f1xwll1wv1p4p2r9c.gif" alt="waiting gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Done? Ok! Next step: adding your phone number for text alerts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can I get your digits?
&lt;/h2&gt;

&lt;p&gt;As I mentioned above, one of the resources created in your stack was the &lt;code&gt;XboxAlert&lt;/code&gt; &lt;a href="https://docs.amazonaws.cn/en_us/sns/latest/dg/welcome.html" rel="noopener noreferrer"&gt;SNS topic&lt;/a&gt;. It was created during the deployment, but right now it's not doing anything. Let's change that.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the AWS Console, and navigate to the SNS Dashboard&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Topics&lt;/strong&gt;, you should see your freshly-minted topic, called something like &lt;code&gt;xbox-stock-alert-&amp;lt;env&amp;gt;-XboxAlert&lt;/code&gt;. Click its name&lt;/li&gt;
&lt;li&gt;Click the big orange &lt;strong&gt;Create subscription&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Fill out the form like so with your mobile number, and click &lt;strong&gt;Create subscription&lt;/strong&gt; again:&lt;/li&gt;
&lt;/ol&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%2Fi%2F5xenes9uf47a1si1gvzl.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%2Fi%2F5xenes9uf47a1si1gvzl.png" alt="subscription" width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll need to verify your phone number if you haven't used it with SNS before, and then you're good to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing time
&lt;/h2&gt;

&lt;p&gt;Still in AWS, you should now be able to open up the CodeBuild console and see a new project in there:&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%2Fi%2Flpk1il7y2ooat8snvmd6.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%2Fi%2Flpk1il7y2ooat8snvmd6.png" alt="xbox-4" width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll want to run it manually to make sure everything works before setting and forgetting it, so go ahead and select your project and hit that &lt;strong&gt;Start build&lt;/strong&gt; button. This will take some time as well, but you can tail the CloudWatch logs by clicking the project name and selecting the most recent build run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vids or it didn't happen
&lt;/h2&gt;

&lt;p&gt;Hopefully, your build was a success (and if it wasn't, &lt;a href="https://twitter.com/annaspies" rel="noopener noreferrer"&gt;hit me up&lt;/a&gt; - I think I hit all the errors while building this out and may be able to help).&lt;/p&gt;

&lt;p&gt;But how can you make sure? Well, you can go back to your project in Cypress.io, and see if there's anything in your latest runs. If all went well, you'll be able to watch a video of the headless browser running your spec!&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%2Fi%2Fjpmd746oibjav7pci99z.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%2Fi%2Fjpmd746oibjav7pci99z.png" alt="xbox-5" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, should one day that test fail 🤞, you'll get a notification straight to your phone letting you know that Xbox is right there for the taking. Good luck!&lt;/p&gt;

&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; I actually just made that up, but I imagine the inventor of the hammer said that at some point.&lt;br&gt;
&lt;sup&gt;2&lt;/sup&gt; I also just made that up, but that doesn't make it any less true.&lt;br&gt;
&lt;sup&gt;3&lt;/sup&gt; A much better way to do this is to use &lt;a href="https://docs.stackery.io/docs/using-stackery/environments#setting-parameter-store-values" rel="noopener noreferrer"&gt;environment parameters&lt;/a&gt; stored in &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html" rel="noopener noreferrer"&gt;AWS Systems Manager Parameter Store&lt;/a&gt; to store your record key, but for the sake of brevity my example hard-codes the key. &lt;strong&gt;Just make sure your repo is private if you follow my bad example&lt;/strong&gt; 🙏&lt;/p&gt;
&lt;h2&gt;
  
  
  Postscript
&lt;/h2&gt;

&lt;p&gt;It's possible to extend the scraper spec to add more retailers, though I ran into issues with a few, such as Walmart's bot detector:&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%2Fi%2Fnv0c5h4325xi2j7jyhdt.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%2Fi%2Fnv0c5h4325xi2j7jyhdt.png" alt="walmart" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wasn't able to get these running without errors, but maybe someone else will have more luck and can comment with their solutions:&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="c1"&gt;// xbox-stock-alert/cypress/integration/scraper.spec.js&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Xbox out-of-stock scraper - more retailers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checks to see if Xboxes are out of stock at GameStop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.gamestop.com/accessories/xbox-series-x/products/xbox-series-x/11108371.html?condition=New&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gzip, deflate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keepAlive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span.delivery-out-of-stock&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span.store-unavailable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checks to see if Xboxes are out of stock at Best Buy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.bestbuy.com/site/microsoft-xbox-series-x-1tb-console-black/6428324.p?skuId=6428324&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gzip, deflate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keepAlive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-sku-id="6428324"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.disabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checks to see if Xboxes are out of stock at Walmart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.walmart.com/ip/Xbox-Series-X/443574645&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gzip, deflate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keepAlive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.spin-button-children&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Get in-stock alert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checks to see if Xboxes are out of stock at Costco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.costco.com/xbox-series-x-1tb-console-with-additional-controller.product.100691493.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gzip, deflate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keepAlive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;pageLoadTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.oos-overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>serverless</category>
      <category>cypress</category>
      <category>node</category>
      <category>xbox</category>
    </item>
  </channel>
</rss>
