<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Gustmann</title>
    <description>The latest articles on DEV Community by Michael Gustmann (@beavearony).</description>
    <link>https://dev.to/beavearony</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%2F147932%2F49278dd3-133a-4030-9697-801b747d5371.jpg</url>
      <title>DEV Community: Michael Gustmann</title>
      <link>https://dev.to/beavearony</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/beavearony"/>
    <language>en</language>
    <item>
      <title>Building Tailwind CSS in an Angular CLI project</title>
      <dc:creator>Michael Gustmann</dc:creator>
      <pubDate>Fri, 04 Oct 2019 10:40:45 +0000</pubDate>
      <link>https://dev.to/beavearony/building-tailwind-css-in-an-angular-cli-project-e04</link>
      <guid>https://dev.to/beavearony/building-tailwind-css-in-an-angular-cli-project-e04</guid>
      <description>&lt;p&gt;If you are wondering how to use Tailwind CSS in an Angular CLI project and automatically create an optimized CSS bundle during production builds, you've come to the right place!&lt;/p&gt;

&lt;p&gt;You can check out a working minimal solution in this &lt;a href="https://github.com/BeaveArony/tailwindcss-angular.git"&gt;repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular CLI
&lt;/h2&gt;

&lt;p&gt;Ejecting out of the Angular CLI seems like a nightmare to me. Too much of a benefit of generating code, building and configuring quickly. Many tools like &lt;a href="https://nx.dev/angular"&gt;NX&lt;/a&gt; build on top of it. Since version 6 of Angular it is not even possible to eject out of the CLI anymore. Instead we've got an extensible builder system to include customization, in case we need it.&lt;/p&gt;

&lt;p&gt;The Angular CLI uses webpack under the hood to build and bundle the web app. WebPack might be replaced by another build tool, like Bazel, if it proves to be better. Most cases don't need to customize specific build details, but in our case, it is beneficial to hook into the build system. Fortunately, we have the ability to customize the webpack build by using custom builders.&lt;/p&gt;

&lt;p&gt;With the help of &lt;a href="https://www.npmjs.com/package/@angular-builders/custom-webpack"&gt;@angular-builders/custom-webpack&lt;/a&gt; we can add some logic to extend the underlying webpack build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt; is an awesome library to remove the headache of coming up with class names. Everything I need in CSS is defined in HTML and unused styles can be stripped away at build time. I remember those times where I was afraid to remove CSS classes, because these might be used somewhere else. No more! The machine can do it for me.&lt;/p&gt;

&lt;p&gt;We will setup an Angular CLI project to compile &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt; with the help of &lt;a href="https://www.npmjs.com/package/@angular-builders/custom-webpack"&gt;@angular-builders/custom-webpack&lt;/a&gt; to include a build step into the default webpack config. &lt;a href="https://github.com/postcss/postcss#usage"&gt;PostCSS&lt;/a&gt; will load our Tailwind root file and process it with additional vendor prefixes and remove unused classes with the &lt;a href="https://github.com/FullHuman/postcss-purgecss"&gt;PurgeCSS&lt;/a&gt; plugin. You can find more information about this process in the official &lt;a href="https://tailwindcss.com/docs/controlling-file-size"&gt;Tailwind Documentation about &lt;strong&gt;Controlling File Size&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To not slow us down during development, we will configure it to only run the PurgeCSS process during production builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separating Tailwind CSS from the rest
&lt;/h2&gt;

&lt;p&gt;I find it better to separate Tailwind into its own file &lt;code&gt;./src/tailwind.scss&lt;/code&gt; instead of putting the Tailwind's base, components, and utilities styles directly into our CSS. This way I can target the custom build to only this file and leave the rest as it is. Other component libraries like &lt;a href="https://material.angular.io"&gt;Angular Material&lt;/a&gt; can be added without worrying about the intermingled build process.&lt;/p&gt;

&lt;p&gt;We can also use whatever style system we like (ie. CSS, SCSS, LESS ...) in our app. Tailwind will just work, can be used in any template and will only put the styles we actually use into our final bundle. I don't see a reason to write any CSS in any components with this setup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to change the default &lt;strong&gt;&lt;em&gt;ng generate component&lt;/em&gt;&lt;/strong&gt; component to &lt;strong&gt;NOT&lt;/strong&gt; create the styles file, you can use this command to default to inline styles in your app:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng config projects.&amp;lt;my-project&amp;gt;.schematics.@schematics/angular:component.inlineStyle &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set it up
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create the Angular Project
&lt;/h3&gt;

&lt;p&gt;Install the Angular CLI, if you haven't done so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @angular/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new Angular CLI project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new tailwindcss-angular
&lt;span class="nb"&gt;cd &lt;/span&gt;tailwindcss-angular
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install dependencies
&lt;/h3&gt;

&lt;p&gt;Install the packages into the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i tailwindcss
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @angular-builders/custom-webpack @fullhuman/postcss-purgecss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initialize Tailwind CSS
&lt;/h3&gt;

&lt;p&gt;Create a tailwind.config.js file by running this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tailwind init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be used to customize Tailwind with themes, colors, breakpoints, spacing and many others.&lt;/p&gt;

&lt;p&gt;Create a new file &lt;code&gt;src/tailwind.scss&lt;/code&gt; with this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Customize the build process
&lt;/h3&gt;

&lt;p&gt;Create another new file &lt;code&gt;extra-webpack.config.js&lt;/code&gt; in the root folder of our project and add this content:&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;purgecss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;@fullhuman/postcss-purgecss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
  &lt;span class="c1"&gt;// Specify the paths to all of the template files in your project&lt;/span&gt;
  &lt;span class="na"&gt;content&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;./src/**/*.html&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;./src/**/*.component.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// Include any special characters you're using in this regular expression&lt;/span&gt;
  &lt;span class="na"&gt;defaultExtractor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\w&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;:&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;(?&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;!:&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Using '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' mode`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/tailwind&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;scss$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;use&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="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nx"&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;tailwindcss&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;./tailwind.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nx"&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;autoprefixer&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&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;purgecss&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="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="nx"&gt;config&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;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/postcss/autoprefixer"&gt;Autoprefixer&lt;/a&gt; automatically adds vendor prefixes. Leave it out, if you don't need this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configure angular.json
&lt;/h2&gt;

&lt;p&gt;Then, to use these files open up &lt;code&gt;angular.json&lt;/code&gt; and look for the &lt;strong&gt;&lt;em&gt;"architect"&lt;/em&gt;&lt;/strong&gt; section. You need to customize the &lt;strong&gt;ng build&lt;/strong&gt; and &lt;strong&gt;ng serve&lt;/strong&gt; parts.&lt;/p&gt;

&lt;p&gt;You can do it by editing the &lt;code&gt;angular.json&lt;/code&gt; file directly or use the &lt;strong&gt;ng config&lt;/strong&gt; command.&lt;/p&gt;

&lt;p&gt;So you do this twice, once for &lt;strong&gt;build&lt;/strong&gt; and once for &lt;strong&gt;serve&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replace &lt;strong&gt;&lt;em&gt;@angular-devkit/build-angular&lt;/em&gt;&lt;/strong&gt; with &lt;strong&gt;&lt;em&gt;@angular-builders/custom-webpack&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add the &lt;strong&gt;&lt;em&gt;customWebpackConfig&lt;/em&gt;&lt;/strong&gt; path to the &lt;strong&gt;&lt;em&gt;options&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Make changes to the &lt;strong&gt;build&lt;/strong&gt; section
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng config projects.&amp;lt;my-project&amp;gt;.architect.build.builder @angular-builders/custom-webpack:browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng config projects.&amp;lt;my-project&amp;gt;.architect.build.options.customWebpackConfig.path extra-webpack.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Make changes to the &lt;strong&gt;serve&lt;/strong&gt; section
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng config projects.&amp;lt;my-project&amp;gt;.architect.serve.builder @angular-builders/custom-webpack:dev-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng config projects.&amp;lt;my-project&amp;gt;.architect.serve.options.customWebpackConfig.path extra-webpack.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the tailwind.scss file to the styles array
&lt;/h3&gt;

&lt;p&gt;Don't forget to add the &lt;code&gt;tailwind.scss&lt;/code&gt; path to the styles array. This is probably best done directly in the &lt;code&gt;angular.json&lt;/code&gt; file. In case you want to do it with a command, this will add the file to the second index of the array or replace anything that is configured in that slot. So be careful not to overwrite anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng config projects.&amp;lt;my-project&amp;gt;.architect.build.options.styles[1] src/tailwind.scss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Summarized changes to &lt;strong&gt;angular.json&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In total there are 9 lines affected. The final outcome of all our changes should look 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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;architect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;builder&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;@angular-builders/custom-webpack:browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ←&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;options&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;customWebpackConfig&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;// ←&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&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;extra-webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// ←&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;                                  &lt;span class="c1"&gt;// ←&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;styles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/tailwind.scss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;// ←&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;builder&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;@angular-builders/custom-webpack:dev-server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ←&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;options&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;customWebpackConfig&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;// ←&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&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;extra-webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// ←&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;                                  &lt;span class="c1"&gt;// ←&lt;/span&gt;
    &lt;span class="c1"&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;h2&gt;
  
  
  Test it out
&lt;/h2&gt;

&lt;p&gt;Everything should be in place now. To see if if it worked correctly, replace the content of &lt;code&gt;./src/app.component.html&lt;/code&gt; with some HTML that uses Tailwind CSS classes or use this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-lg mx-auto bg-white shadow-lg rounded-lg overflow-hidden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sm:flex sm:items-center px-6 py-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center justify-center sm:flex-col sm:w-24 sm:border sm:border-gray-500 sm:rounded-full"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block sm:mx-auto mx-0 flex-shrink-0 h-16 sm:h-12"&lt;/span&gt;
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://angular.io/assets/images/logos/angular/angular.svg"&lt;/span&gt;
        &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Angular Logo"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block sm:mx-auto mx-0 flex-shrink-0 h-16 sm:h-12"&lt;/span&gt;
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://tailwindcss.com/android-chrome-512x512.png"&lt;/span&gt;
        &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Tailwind CSS Logo"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-4 sm:mt-0 sm:ml-4 text-center sm:text-left"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-xl leading-tight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Tailwind CSS &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Angular CLI&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm leading-tight text-gray-600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        With custom webpack configuration!
      &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
          &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-purple-500 hover:text-white hover:bg-purple-500 border border-purple-500 text-xs font-semibold rounded-full px-4 py-1 leading-normal"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          GO
        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see the result in a web browser run &lt;code&gt;ng serve&lt;/code&gt;. To see the build output run &lt;code&gt;ng build --prod&lt;/code&gt; and take a look into the &lt;code&gt;dist/tailwindcss-angular/styles.########.css&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Tailwind CSS automatically adds &lt;a href="https://github.com/necolas/normalize.css"&gt;normalize.css&lt;/a&gt;. You will see these styles in the beginning of the file.&lt;br&gt;
It will also &lt;strong&gt;only&lt;/strong&gt; contain the css classes used by your app. Everything else is stripped out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We learned how to make changes to the build process of our Angular CLI project to compile Tailwind CSS with just a few simple steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the custom-webpack, purgecss and tailwindcss dependencies&lt;/li&gt;
&lt;li&gt;Init tailwindcss and create a &lt;code&gt;tailwind.scss&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;extra-webpack.config.js&lt;/code&gt; file with the build config&lt;/li&gt;
&lt;li&gt;Adjust &lt;code&gt;angular.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Happy styling your Angular components without writing CSS!&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>html</category>
      <category>angular</category>
    </item>
    <item>
      <title>AWS Amplify CLI, how to automatically add a user to a Cognito User Pool with a Lambda-Trigger</title>
      <dc:creator>Michael Gustmann</dc:creator>
      <pubDate>Tue, 30 Apr 2019 13:47:23 +0000</pubDate>
      <link>https://dev.to/beavearony/aws-amplify-cli-how-to-automatically-add-a-user-to-a-cognito-user-pool-with-a-lambda-trigger-208j</link>
      <guid>https://dev.to/beavearony/aws-amplify-cli-how-to-automatically-add-a-user-to-a-cognito-user-pool-with-a-lambda-trigger-208j</guid>
      <description>&lt;h1&gt;
  
  
  Handling Cognito-UserPool-Groups in our AWS Amplify CLI project
&lt;/h1&gt;

&lt;p&gt;AWS Amplify Transform provides an &lt;code&gt;@auth&lt;/code&gt; directive. With it we have the possibility to grant access to certain parts of our API based on either static or dynamic groups. See the &lt;a href="https://aws-amplify.github.io/docs/cli/graphql#auth"&gt;documentation&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;Say we have task api and we want:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each user to log in to use our app and grant read access to our API&lt;/li&gt;
&lt;li&gt;To have managers, that can additionally create and update a task&lt;/li&gt;
&lt;li&gt;To have admins, that can do everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will not cover the API nor the &lt;code&gt;@auth&lt;/code&gt; directive, but tackle the problem, that upon Sign-Up, we expect all users to be in a group named &lt;em&gt;'Users'&lt;/em&gt;. Managers and Admins will be added to the groups by hand in the AWS Management Console, but each user will at least belong to the &lt;strong&gt;'Users'&lt;/strong&gt; group.&lt;/p&gt;

&lt;p&gt;We want our code to handle the infrastructure as much as possible without leaving the Amplify CLI.&lt;/p&gt;

&lt;p&gt;Let's see how to automate things in each o our environment stacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create all user groups ('Users', 'Managers', 'Admins')&lt;/li&gt;
&lt;li&gt;Add a trigger function to automatically add a user to a group&lt;/li&gt;
&lt;li&gt;Allow the function to add a user to a group&lt;/li&gt;
&lt;li&gt;Configure a trigger in the Cognito UserPool to run the function after Sign-Up&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Custom CloudFormation templates
&lt;/h2&gt;

&lt;p&gt;When adding or modifying categories with the Amplify CLI, some templates are not safe and will be overwritten. We can safely add custom resources to the &lt;code&gt;amplify/backend/api/${api-name}/stacks/CustomResources.json&lt;/code&gt; file or add other json file to this directory. These stacks are part of the api category, but could be used to create anything that CloudFormation supports. The emphasis is on &lt;strong&gt;create&lt;/strong&gt;! We can reference other parts of our backend templates, but we cannot change them (at least to our knowledge).&lt;/p&gt;

&lt;p&gt;We need to change &lt;code&gt;amplify/backend/auth/cognitoxxxx/cognitoxxxx-cloudformation-template.yml&lt;/code&gt;, which will get overwritten by operations like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify update auth
amplify add analytics
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, we need to be prepared to redo our changes multiple times. Checking in our code in source control will help us with that though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate Groups
&lt;/h2&gt;

&lt;p&gt;We want three groups to be added to our UserPools.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admins&lt;/li&gt;
&lt;li&gt;Managers&lt;/li&gt;
&lt;li&gt;Users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we don't care to redo the changes, when Amplify CLI updates this file, we can edit the &lt;strong&gt;'auth'&lt;/strong&gt; CloudFormation template.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;amplify/backend/auth/cognitoxxxx/cognitoxxxx-cloudformation-template.yml&lt;/code&gt; add at the end just before &lt;code&gt;Outputs :&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;## Other resources ...&lt;/span&gt;
  &lt;span class="c1"&gt;## ...&lt;/span&gt;
  &lt;span class="na"&gt;UserPoolGroupAdmins&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Cognito::UserPoolGroup'&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;GroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Admins&lt;/span&gt;
      &lt;span class="na"&gt;UserPoolId&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;UserPool&lt;/span&gt;

  &lt;span class="na"&gt;UserPoolGroupManagers&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Cognito::UserPoolGroup'&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;GroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Managers&lt;/span&gt;
      &lt;span class="na"&gt;UserPoolId&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;UserPool&lt;/span&gt;

  &lt;span class="na"&gt;UserPoolGroupUsers&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Cognito::UserPoolGroup'&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;GroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Users&lt;/span&gt;
      &lt;span class="na"&gt;UserPoolId&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;UserPool&lt;/span&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Otherwise we can go the safe route and use the &lt;code&gt;amplify/backend/api/${api-name}/stacks/CustomResources.json&lt;/code&gt; file to create these groups.&lt;/p&gt;

&lt;p&gt;Add the following to the &lt;em&gt;"Resources"&lt;/em&gt; section, right after &lt;em&gt;"EmptyResource"&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"Resources"&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;"EmptyResource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Custom::EmptyResource"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AlwaysFalse"&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;"UserPoolGroupAdmins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS::Cognito::UserPoolGroup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GroupName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Admins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"UserPoolId"&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;"Ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AuthCognitoUserPoolId"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"UserPoolGroupManagers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS::Cognito::UserPoolGroup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GroupName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Managers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"UserPoolId"&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;"Ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AuthCognitoUserPoolId"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"UserPoolGroupUsers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS::Cognito::UserPoolGroup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GroupName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"UserPoolId"&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;"Ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AuthCognitoUserPoolId"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The downside to this is, that it will show up as if the &lt;em&gt;&lt;strong&gt;api&lt;/strong&gt;&lt;/em&gt; category changed and not the &lt;em&gt;&lt;strong&gt;auth&lt;/strong&gt;&lt;/em&gt; category.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically add a User to a Cognito User Pool Group with a Lambda-Trigger on Sign-Up
&lt;/h2&gt;

&lt;p&gt;Now that we automatically create Cognito-UserPool-Groups, we want to assign users to one of those groups upon Sign-Up. &lt;/p&gt;

&lt;p&gt;Cognito offers triggers during certain life-cycle events. One of these triggers is 'PostConfirmation', which may run a Lambda function after a user was successfully confirmed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a new Lambda Function
&lt;/h2&gt;

&lt;p&gt;The AWS Amplify CLI offers the ability to add a function to our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify add &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Choose the name &lt;em&gt;'addUserToGroup'&lt;/em&gt;, when asked for the friendly name and Lambda function name and as a template we want &lt;em&gt;Hello world function&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Change the file &lt;code&gt;index.js&lt;/code&gt; of our newly created function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;afterConfirmationTrigger&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;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&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;AWS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;cognitoISP&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="nx"&gt;CognitoIdentityServiceProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2016-04-18&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;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;GroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;UserPoolId&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;userPoolId&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;event&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="nx"&gt;cognitoISP&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adminAddUserToGroup&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="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&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;callback&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="nx"&gt;event&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;callback&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="nx"&gt;event&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 &lt;code&gt;event.json&lt;/code&gt; we can change the payload content to our liking to test the function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;"userPoolId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"the-user-pool-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userPoolGroupName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Users"&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;To run a test use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify invoke &lt;span class="k"&gt;function &lt;/span&gt;addUserToGroup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will not work yet. Since we need to give the function the permission to do what we want it to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add IAM Policy to allow adding user to group
&lt;/h2&gt;

&lt;p&gt;To allow our new function to actually add a user to a group, it needs a policy attached. This time we need to edit the CloudFormation template of the Lambda function. This should be pretty safe, since other categories usually don't have side effects on this template.&lt;/p&gt;

&lt;p&gt;We need to specify the resource ARN of our UserPool target. We can resort to using a wildcard character or use parameters to get the exact ARN of our UserPool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Without parameters
&lt;/h3&gt;

&lt;p&gt;We edit the file &lt;code&gt;amplify/backend/function/addUserToGroup/addUserToGroup-cloudformation-template.json&lt;/code&gt; and add to the array &lt;code&gt;lambdaexecutionpolicy.Properties.PolicyDocument.Statement&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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="s2"&gt;"cognito-idp:AdminAddUserToGroup"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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;"Fn::Sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:cognito-idp:${region}:${account}:userpool/${region}*"&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;"region"&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;"Ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS::Region"&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;"account"&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;"Ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS::AccountId"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is simple and in one place but will give the function the permission to access all UserPools in our region.&lt;/p&gt;

&lt;h3&gt;
  
  
  With parameters and least privileges
&lt;/h3&gt;

&lt;p&gt;To target a single UserPool istead, we need to know its ARN. To get access, we create a parameter and pass it to our CloudFormation template.&lt;/p&gt;

&lt;p&gt;We create a new file &lt;code&gt;amplify/backend/function/addUserToGroup/parameters.json&lt;/code&gt; and add the ARN of the UserPool to it. We can take a look at the &lt;strong&gt;authcognitoname&lt;/strong&gt; in &lt;code&gt;amplify/backend/api/${api-name}/parameters.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;"AuthCognitoUserPoolArn"&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;"Fn::Sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Fn::GetAtt"&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="s2"&gt;"--authcognitoname--"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Outputs.UserPoolId"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To pass the parameters to the CloudFormation template, we need to edit &lt;code&gt;amplify/backend/function/addUserToGroup/addUserToGroup-cloudformation-template.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, we have to define the parameter. We do this by adding the following to the "Parameters" array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;"Parameters"&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;parameters&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"AuthCognitoUserPoolArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"String"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we add a statement to&lt;code&gt;lambdaexecutionpolicy.Properties.PolicyDocument.Statement&lt;/code&gt; which references the &lt;strong&gt;"AuthCognitoUserPoolArn"&lt;/strong&gt; parameter and allows the action &lt;strong&gt;"cognito-idp:AdminAddUserToGroup"&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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="s2"&gt;"cognito-idp:AdminAddUserToGroup"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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;"Ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AuthCognitoUserPoolArn"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can run &lt;code&gt;amplify push&lt;/code&gt; to deploy the function and check if the policy was added successfully by going to the Lambda Management Console and find our function. If we click on the &lt;strong&gt;'Amazon Cognito User Pools'&lt;/strong&gt; box, we can inspect the policy below it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Lambda PostConfirmation-Trigger
&lt;/h2&gt;

&lt;p&gt;To assign the Lambda function to a Cognito-UserPool-Trigger, we need to edit the &lt;strong&gt;'auth'&lt;/strong&gt; CloudFormation template.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This might get overwritten on certain Amplify CLI operations, but we don't see another choice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;code&gt;amplify/backend/auth/cognitoxxxx/cognitoxxxx-cloudformation-template.yml&lt;/code&gt; we add our trigger somewhere in that section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BEGIN USER POOL RESOURCES&lt;/span&gt;
&lt;span class="na"&gt;UserPool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Created upon user selection&lt;/span&gt;
  &lt;span class="c1"&gt;# Depends on SNS Role for Arn if MFA is enabled&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::Cognito::UserPool&lt;/span&gt;
  &lt;span class="na"&gt;UpdateReplacePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Other properties ...&lt;/span&gt;
    &lt;span class="c1"&gt;# Add this:&lt;/span&gt;
    &lt;span class="na"&gt;LambdaConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PostConfirmation&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;arn:aws:lambda:${region}:${account}:function:addUserToGroup-${env}&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Region'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;account&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::AccountId'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To deploy the changes to our UserPool we run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Checking our work
&lt;/h2&gt;

&lt;p&gt;When everything worked, we can sign in to our AWS Management Console and check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cognito UserPool should have created our three groups&lt;/li&gt;
&lt;li&gt;There is a PostConfirmation trigger pointing to our addUserToGroup Lambda function&lt;/li&gt;
&lt;li&gt;The Lambda function was created in our environment&lt;/li&gt;
&lt;li&gt;The function has the right policy attached, which allows it to add a user to a group in our UserPool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To see it in action, we sign up a new user in our Amplify CLI app of our choice and watch how this new user is automatically added to the &lt;strong&gt;'Users'&lt;/strong&gt; Cognito-UserPool-Group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;We created UserPool-Groups in CustomResources.json of the api category, added a Lambda function, attached a policy to it to access the UserPool and edited our Cognito CloudFormation template to define a PostConfirmation Trigger that executes our new Lambda function. Now each time a user signs up it will be assigned to a group.&lt;/p&gt;

&lt;p&gt;As the development of Amplify CLI continues we should see help in this area. So this procedure might get irrelivant in a hopefully short while.&lt;/p&gt;

</description>
      <category>awsamplify</category>
    </item>
    <item>
      <title>Personal Growth with Git Flow and AWS Amplify Console - Part 3</title>
      <dc:creator>Michael Gustmann</dc:creator>
      <pubDate>Thu, 11 Apr 2019 09:17:13 +0000</pubDate>
      <link>https://dev.to/beavearony/personal-growth-with-git-flow-and-aws-amplify-console-part-3-1f6c</link>
      <guid>https://dev.to/beavearony/personal-growth-with-git-flow-and-aws-amplify-console-part-3-1f6c</guid>
      <description>&lt;p&gt;Having built a personalized Todo App with a GraphQL API in &lt;a href="https://dev.to/beavearony/getting-started-with-a-angularnx-workspace-backed-by-an-aws-amplify-graphql-api---part-1-24m0"&gt;Part 1&lt;/a&gt; and &lt;a href="https://dev.to/beavearony/creating-angular-ui-components-and-connecting-them-to-the-graphql-appsync-api-part-2-33n0"&gt;Part 2&lt;/a&gt; with the help of the &lt;a href="https://aws-amplify.github.io/docs" rel="noopener noreferrer"&gt;AWS Amplify CLI&lt;/a&gt; using &lt;a href="https://angular.io" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; and the tools of &lt;a href="https://nx.dev" rel="noopener noreferrer"&gt;Nx&lt;/a&gt; we have actually quite a lot of time on our hands.&lt;br&gt;
We didn't have to build Authentication and Authorization, we did not have to think about how to set up a workspace for additional future apps or team-mates in mind, we saved on writing lengthy GraphQL schemas with pagination, filtering, input objects, resolvers and ownership and we wrote everything in code to easily push an entire backend environment to the cloud.&lt;/p&gt;
&lt;h1&gt;
  
  
  Personal Growth with Git Flow and AWS Amplify Console - Part 3
&lt;/h1&gt;

&lt;p&gt;We feel good about our life's choices! Free time to concentrate on our health and personal growth! So, we go to a seminar on the weekend to listen to a famous life coach. It is a great speech, were we learn how to be thankful and appreciate everything in life. He encourages everyone to tell the group about the things we are thankful for. So, as we are feeling good, we actually stand up and tell everyone about our achievements. About writing a personalized todo-app, thinking big about future plans and other team mates. We also shared our thoughts about our plans on publishing the app worldwide, adding git flow and continuous integration. Of course, we couldn't leave out the concept of "You build, you own it" and how we can iterate fast by easily spinning up service-full cloud environments by writing a few powerful Amplify and Angular commands.&lt;/p&gt;

&lt;p&gt;Talking about "two-pizza-teams" crosses our minds, but we quickly dismiss the thought, not wanting to go down the rabbit-whole of healthy nourishment.&lt;/p&gt;

&lt;p&gt;We're not sure how much we made sense to everyone in the room, but the coach was extremely excited! Setting personal goals for ourselves and checking them off, when completed is a great way to empower us for greater things. Building things and taking the responsibility for it, while continuously integrating that flow into our lives will connect us to our environment in the "here and now" and beyond the clouds. Staying service-full while giving commands will amplify our personal growth.&lt;/p&gt;

&lt;p&gt;Wow, we did not expect this response at all! Being experienced in hallway chats on summits and conferences we stay to talk to the speaker afterwards. He lets us in on his plans to build an online life coaching platform to share videos, articles and documents. Chat groups between paying members and an app to track personal goals should also be a part of this offering.&lt;br&gt;
He sais, he likes the way we are thinking ahead. Having a production ready todo-app with these wonderful tools in our belt, we are the perfect candidates to build up a small team to take up this endeavor.&lt;/p&gt;

&lt;p&gt;After exchanging our social media contact information, we promise to send him a link to our web app. On our way home we start to think about how to quickly get our todo-app published worldwide. We could use the &lt;code&gt;amplify hosting&lt;/code&gt; command to simply setup a static website, but since we feel like we will need to publish several staging environments and branches in the future, we decide on using the git-flow workflow.&lt;/p&gt;

&lt;p&gt;Setting up a complete CI/CD pipeline is usually a non-trivial task, but since the introduction of &lt;a href="https://amplify.aws" rel="noopener noreferrer"&gt;Amplify Console&lt;/a&gt; it got cut down to a few minutes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preparing the repo
&lt;/h2&gt;

&lt;p&gt;OK, let's start where we last left off. We have a GIT repository hosted at GitHub with a single master branch. We have a single Amplify environment called 'master', when we previously ran &lt;code&gt;amplify init&lt;/code&gt; and &lt;code&gt;amplify push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We want to end up with two permanent git branches 'master' and 'develop'. These branches should be each connected to a similar named amplify stack in the cloud. The information about our cloud resources should be stored in each branch.&lt;/p&gt;

&lt;p&gt;We can add a new cloud environment by typing &lt;code&gt;amplify init&lt;/code&gt; and choosing &lt;em&gt;No&lt;/em&gt; when asked, if we want to use an existing environment and &lt;em&gt;develop&lt;/em&gt; as the new name. The next question will be about our AWS profile we want to use, so we choose the one we used for master as well.&lt;/p&gt;

&lt;p&gt;Working in a non-open-source-team, we should revert the change we made in part one, where we put team-provider-info.json in &lt;code&gt;.gitignore&lt;/code&gt;. Looking into this file, we will see our newly created &lt;em&gt;develop&lt;/em&gt; environment added.&lt;/p&gt;

&lt;p&gt;So far, we have&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;one 'master' git branch locally and on origin&lt;/li&gt;
&lt;li&gt;two backend environments 'master' and 'develop' in &lt;code&gt;team-provider-info.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;one 'master' backend environment stack pushed to the cloud ('develop' env only locally so far)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Creating another app in our workspace
&lt;/h2&gt;

&lt;p&gt;We don't have a requirement for another app yet, but as we created a workspace, that supports multiple apps connected to one backend, we feel the urge in our belly to test this out anyways.&lt;/p&gt;

&lt;p&gt;We make it quick (&lt;a href="https://dev.to/beavearony/getting-started-with-a-angularnx-workspace-backed-by-an-aws-amplify-graphql-api---part-1-24m0"&gt;remembering that we covered the process quite thoroughly in part 1&lt;/a&gt;) by typing &lt;code&gt;ng g app second-app&lt;/code&gt; and accepting all defaults. A few more adjustments are needed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Adding 'global' to &lt;code&gt;polyfills.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Importing the css theme in &lt;code&gt;styles.css&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Concatinating the &lt;em&gt;AppsyncModule&lt;/em&gt; to the &lt;em&gt;imports&lt;/em&gt; array in &lt;code&gt;app.module.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prepending the content of &lt;code&gt;app.component.html&lt;/code&gt; with &lt;code&gt;&amp;lt;amplify-authenticator&amp;gt;&amp;lt;/amplify-authenticator&amp;gt;&lt;/code&gt; on line one&lt;/li&gt;
&lt;li&gt;Appending another build command to &lt;code&gt;package.json&lt;/code&gt;'s scripts section: &lt;code&gt;"build:second": "npm run rename:aws:config || ng build second-app --prod; ng build second-app --prod"&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once we make sure this runs locally with &lt;code&gt;ng serve second-app&lt;/code&gt; and &lt;code&gt;npm run build:second&lt;/code&gt;, we commit and push the changes to the 'master' branch.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding Git-Flow
&lt;/h2&gt;

&lt;p&gt;Since we decided to use git-flow and like to have an opinionated workflow, we agree to install a helper library for guidance on our Mac. We can come back to this &lt;a href="https://danielkummer.github.io/git-flow-cheatsheet" rel="noopener noreferrer"&gt;cheat sheet&lt;/a&gt; at any point, if we run into unknown git-flow territory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;git-flow-avh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and being on the root level of our workspace we run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git flow init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while keeping all defaults. Doing this, we end up on a new &lt;em&gt;develop&lt;/em&gt; branch, which we can push to our source control provider by using this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not much changed here. We simply have a new branch 'develop' without any new commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Amplify Console
&lt;/h2&gt;

&lt;p&gt;We are ready to start our CI/CD pipeline. We go to &lt;a href="https://aws.amazon.com/amplify/console/" rel="noopener noreferrer"&gt;AWS Amplify Console&lt;/a&gt; and click on the &lt;em&gt;&lt;strong&gt;CONNECT APP&lt;/strong&gt;&lt;/em&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzr6uon3f21yd7oup1hp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzr6uon3f21yd7oup1hp3.png" alt="CONNECT APP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Four simple steps to set up our build and deploy pipeline:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - Add repository service
&lt;/h3&gt;

&lt;p&gt;Selecting our GIT provider and giving Amplify Console read-only permission&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Add repository branch
&lt;/h3&gt;

&lt;p&gt;Picking our repository and the &lt;em&gt;'master'&lt;/em&gt; branch from the dropdown options&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Configure build settings
&lt;/h3&gt;

&lt;p&gt;As &lt;em&gt;&lt;strong&gt;App name&lt;/strong&gt;&lt;/em&gt; we like 'amplified'. Both backend environments from &lt;code&gt;team-provider-info.json&lt;/code&gt; should be detected. We have an option to create a new environment right here, which we could reuse in Amplify CLI by importing it later on, but we set up our two environments earlier. So, we pick the &lt;em&gt;'master'&lt;/em&gt; environment in this step.&lt;/p&gt;

&lt;p&gt;We need to select a service role in our AWS account. If we don't have existing one, we can create a new one so Amplify Console may access our resources.&lt;/p&gt;

&lt;p&gt;We can edit the yaml build settings file and set custom &lt;em&gt;&lt;strong&gt;Environment variables&lt;/strong&gt;&lt;/em&gt; for any secrets, that don't belong in source control. AWS offers some variables for usage in our build scripts, like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS_JOB_ID&lt;/li&gt;
&lt;li&gt;AWS_APP_ID&lt;/li&gt;
&lt;li&gt;AWS_BRANCH&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4 - Review
&lt;/h3&gt;

&lt;p&gt;Well, ... we review and click &lt;em&gt;&lt;strong&gt;Save and deploy&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the 'develop' branch
&lt;/h2&gt;

&lt;p&gt;Completing these four steps should have started our build for &lt;em&gt;'master'&lt;/em&gt;. And while waiting for it to finish, we can take a deep breath or two and behold the powers of our fingertips.&lt;/p&gt;

&lt;p&gt;Feeling calm and refreshed we return to connect our second branch &lt;em&gt;'develop'&lt;/em&gt; to the equally named backend. This time only needing to go through two steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - Add repository branch
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Step 2 - Review
&lt;/h3&gt;

&lt;p&gt;So far, our todo-app's branches master and develop are connected to Amplify Console. Anytime we &lt;code&gt;git push&lt;/code&gt; to either of those branches, we will see our frontend update at the edge and our backend resources synced with our code from our repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amplify Console region support
&lt;/h2&gt;

&lt;p&gt;What if Amplify Console does not support our region yet? Depending on when we are trying this, the region we chose when we configured the Amplify CLI might not be supported by the Amplify Console yet. No Biggy, choosing a supported region of Amplify Console, we can add a line right before the &lt;code&gt;amplify push&lt;/code&gt; command in the build script:&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;backend&lt;/span&gt;&lt;span class="pi"&gt;:&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;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="c1"&gt;# replace {our-region} with the previously used region&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo -e "[default]\nregion={our-region}" &amp;gt; ~/.aws/config&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="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Execute&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Amplify&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CLI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;helper&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;script'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;amplifyPush --simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will set our region in the default AWS profile on the current build container instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying a second app
&lt;/h2&gt;

&lt;p&gt;Looking at the build script in Amplify Console, we see a way to specify a single frontend app and a single backend stack per git branch. The frontend app will end up at a URL unique to a specific branch, like &lt;code&gt;https://master.ourappid.amplifyapp.com&lt;/code&gt; and the backend stack will create unique resources in a region with a similar naming schema. But we would like a URL for each app on the same branch, i.e.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://master.app-one.ourappid.amplifyapp.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://master.app-two.ourappid.amplifyapp.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://master.app-x.ourappid.amplifyapp.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unfortunately, this is currently not possible.&lt;/p&gt;

&lt;p&gt;Amplify Console seems to be designed so that we can connect an "Amplify Console"-App to a git repository very simply. Different frontends and backend stacks are configured or connected via git branches. This allows us to deploy our full stack app to various staging environments, like production, develop, test or feature sandboxes.&lt;/p&gt;

&lt;p&gt;What about creating a &lt;strong&gt;new Amplify-Console-App-Connection&lt;/strong&gt; to the same repo?&lt;/p&gt;

&lt;p&gt;Trying to connect another app in Amplify Console, we will quickly see, that we can't choose the same repository again. Picking &lt;strong&gt;a different region&lt;/strong&gt; and connecting an app from the same repo is a workaround, that would work, but we are not very happy with managing this in the future.&lt;/p&gt;

&lt;p&gt;But there's another option to connect an app, which we can explore to see if it could help us:&lt;/p&gt;

&lt;h2&gt;
  
  
  One-Click Deploy Button
&lt;/h2&gt;

&lt;p&gt;Very recently AWS added a new feature, the &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/one-click.html" rel="noopener noreferrer"&gt;One-Click Deploy Button&lt;/a&gt; to deploy a GitHub repository to Amplify Console. A single click on a button and the repo is deployed in our AWS account. We can use it to share our open source project with the world or onboard a new team mate in minutes! Depending on how quickly our todo-app grows, this might come quite handy, when new recruitments arrive.&lt;/p&gt;

&lt;p&gt;We can put it in README.md files or in blog posts, so here is our button to the corresponding repo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://console.aws.amazon.com/amplify/home#/deploy?repo=https://github.com/BeaveArony/amplify-angular-nx-todo" rel="noopener noreferrer"&gt;&lt;br&gt;
    &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Foneclick.amplifyapp.com%2Fbutton.svg" alt="Deploy to Amplify Console"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking this button opens a new browser tab with our region and GitHub link encoded in the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://{aws-region}.console.aws.amazon.com/amplify/home#/deploy?repo={github-url}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7mtoxin83mexlv67d97b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7mtoxin83mexlv67d97b.png" alt="One-Click Deploy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When clicking the &lt;strong&gt;Connect to GitHub&lt;/strong&gt; button, we type the name 'amplified-again' and after reviewing almost nothing, we will have our GitHub repo's default branch connected to a new backend environment stack called &lt;em&gt;'amplify'&lt;/em&gt;. Indeed, very simple and quick!&lt;/p&gt;

&lt;h3&gt;
  
  
  Reusing the newly created 'amplify' backend
&lt;/h3&gt;

&lt;p&gt;Once the connection and the build #1 completed, we could tell our Amplify CLI about this new backend environment, so we could reuse it. Or as in our case, since we want our existing backends, we want to delete the newly created stack.&lt;/p&gt;

&lt;p&gt;Finding the &lt;strong&gt;Backend&lt;/strong&gt; build step in the &lt;strong&gt;Build activity&lt;/strong&gt; will reveal the needed information and instructions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F917oiexzx3uuu6mqtuzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F917oiexzx3uuu6mqtuzu.png" alt="Environment Settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Selecting the &lt;em&gt;'More Info'&lt;/em&gt; button will give us an amplify cli command to run in the root of our project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvext1g3nimqeddk55p2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvext1g3nimqeddk55p2p.png" alt="Reuse Backend"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After we do this, the &lt;em&gt;'amplify'&lt;/em&gt; backend environment will end up in our &lt;code&gt;team-provider-info.json&lt;/code&gt; file ready to be chosen on our command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;amplify &lt;span class="nb"&gt;env &lt;/span&gt;list

| Environments |
| &lt;span class="nt"&gt;------------&lt;/span&gt; |
| amplify      |
| master       |
| &lt;span class="k"&gt;*&lt;/span&gt;develop     |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The copied command includes the aws profile &lt;em&gt;'default'&lt;/em&gt;, so we should remember to adjust this to our profile name, if needed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We could just use it and commit the changes to GitHub, but we want to delete this backend stack in the cloud and locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify &lt;span class="nb"&gt;env &lt;/span&gt;remove amplify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks a little bit weird, asking amplify to remove itself... Well, the result is in, nothing broke!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;amplify &lt;span class="nb"&gt;env &lt;/span&gt;list

| Environments |
| &lt;span class="nt"&gt;------------&lt;/span&gt; |
| master       |
| &lt;span class="k"&gt;*&lt;/span&gt;develop     |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Almost nothing, if we would run the build in the Amplify Console, we would see it fail, having removed the backend under its nose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reusing our previous 'master' backend
&lt;/h3&gt;

&lt;p&gt;So where we're at right now? We have a second "Amplify Console"-App connected to the same repo. The default master branch uses the newly created (and secretly deleted) &lt;em&gt;'amplify'&lt;/em&gt; backend environment, but we want to reuse the same 'master' and 'develop' backend environments as in our original "Amplify Console"-App.&lt;/p&gt;

&lt;p&gt;To do this, we have to disconnect the master branch in &lt;em&gt;&lt;strong&gt;App settings: General&lt;/strong&gt;&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fkboljceskpfqa41xavny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fkboljceskpfqa41xavny.png" alt="Disconnect Master Branch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once done, we can connect our 'master' and 'develop' branches to the equally named environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  One-Click Deploy the second app
&lt;/h2&gt;

&lt;p&gt;The build-settings are still building our first app. To change this, we go to &lt;strong&gt;App settings: Build settings&lt;/strong&gt; to see, that we have to grant access to our GitHub repository.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And this is where we realize that we can actually deploy repositories from others without the necessity to fork them first!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To build and deploy the second frontend app, we edit two lines in the yaml file:&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.1&lt;/span&gt;
&lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&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;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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Execute&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Amplify&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CLI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;helper&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;script'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;amplifyPush --simple&lt;/span&gt;
&lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&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;preBuild&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 ci&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 run build:second&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- Change to second app&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;baseDirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dist/apps/second-app&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- Change to second app&lt;/span&gt;
    &lt;span class="na"&gt;files&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;**/*'&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/**/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving the build settings and hitting the &lt;strong&gt;Redeploy this version&lt;/strong&gt; button in both branches, we're all set!&lt;/p&gt;

&lt;p&gt;Violà!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flv8k1s7kj6cgj7kwkqaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flv8k1s7kj6cgj7kwkqaf.png" alt="Both Apps Deployed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding another frontend app works with the help of the one-click-deploy-button! When we push new code to either our 'master' or 'develop' branch, both apps will build and deploy everything.&lt;/p&gt;

&lt;p&gt;Playfully exploring new features of Amplify Console showed us a way we can add further frontend apps to our backend. Apps like admin-consoles, customer-views, support-tickets, manager-charts and so on are possible. Even if we might find this route too complicated or limiting in the future, we still have the means to use the build-settings in our first-app to install, build and deploy anything, like we were used to before Amplify Console came out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;We feel confident to pursue further development with AWS Amplify. We understand the connection between our repo, the branches and the backend environments. Onboarding new team mates will be a joy.&lt;/p&gt;

&lt;p&gt;We could have used the pipeline functionality of our favorite git provider to continuously deploy our static websites to S3 and CloudFront and manually writing the commands to deploy the backends. We would still benefit from the typical AWS features like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Globally available CDN hosting&lt;/li&gt;
&lt;li&gt;Free SSL certificates&lt;/li&gt;
&lt;li&gt;Serverless backends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having created and built several build pipelines in the past, we can appreciate the simplicity of using Amplify Console to host our serverless full stack app, but what else have we gained?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy custom domain setup with Route53&lt;/li&gt;
&lt;li&gt;Atomic deployments&lt;/li&gt;
&lt;li&gt;CDN invalidation&lt;/li&gt;
&lt;li&gt;Password protected feature branches&lt;/li&gt;
&lt;li&gt;Screenshots of the app on different mobile resolutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, we're getting excited - time to calm down, close our eyes, picture a big smile on our face and take a deep breath or two...&lt;/p&gt;

</description>
      <category>angular</category>
      <category>graphql</category>
      <category>awsamplify</category>
      <category>awsappsync</category>
    </item>
    <item>
      <title>AWS Amplify Auth &amp; Angular RxJS Simple State Management</title>
      <dc:creator>Michael Gustmann</dc:creator>
      <pubDate>Tue, 02 Apr 2019 15:54:25 +0000</pubDate>
      <link>https://dev.to/beavearony/aws-amplify-auth-angular-rxjs-simple-state-management-3jhd</link>
      <guid>https://dev.to/beavearony/aws-amplify-auth-angular-rxjs-simple-state-management-3jhd</guid>
      <description>&lt;p&gt;Recent updates to the &lt;a href="https://aws-amplify.github.io/docs/js/hub#working-with-the-api"&gt;aws-amplify hub&lt;/a&gt; make it easier to listen to (AWS Cognito) auth state changes without using the &lt;a href="https://github.com/aws-amplify/amplify-js/tree/master/packages/aws-amplify-angular"&gt;aws-amplify-angular&lt;/a&gt; package. If we are not using the prebuild UI-components of that package, we might save a few hundred KB by just importing the modules we need.&lt;/p&gt;

&lt;p&gt;But even if we use &lt;a href="https://github.com/aws-amplify/amplify-js/tree/master/packages/aws-amplify-angular"&gt;aws-amplify-angular&lt;/a&gt; package, we can take a look at how we can manage our authentication and user state in a very Angular'esque way without any state management libraries, like &lt;a href="https://github.com/reduxjs/redux"&gt;Redux&lt;/a&gt;, &lt;a href="https://ngrx.io"&gt;NGRX&lt;/a&gt;, &lt;a href="https://www.apollographql.com/docs/link/links/state"&gt;apollo-link-state&lt;/a&gt;, &lt;a href="https://github.com/mobxjs/mobx"&gt;MobX&lt;/a&gt;, &lt;a href="https://github.com/datorama/akita"&gt;Akita&lt;/a&gt; or &lt;a href="https://ngxs.gitbook.io/ngxs"&gt;NGXS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the features of Angular is their dependency injection system. We can define tree-shakable injectables to use in any of our components or other injectables. It's a good place to store the state of feature modules in one or more services.&lt;/p&gt;

&lt;p&gt;If our app grows more complex there might come a time, where we feel the need to use a single store to manage our state. We might be interested in better developer ergonomics with tools like &lt;a href="https://github.com/reduxjs/redux-devtools"&gt;Redux DevTools&lt;/a&gt; or reap the benefits of clearly defined patterns of the library of our choice.&lt;/p&gt;

&lt;p&gt;But until this time comes or if we decide, that we want more than one store, this might be a good pattern to use in our Angular apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  RxJS BehaviorSubject
&lt;/h2&gt;

&lt;p&gt;Angular has a peer dependency on &lt;a href="https://angular.io/guide/rx-library"&gt;RxJS&lt;/a&gt; and uses it in some of their packages. Learning Angular usually entails learning RxJS as well. RxJS offers a Subject, which is an Observable and an Observer in one. The BehaviorSubject additionally stores the last state and can't be created without an initial state. This is pretty ideal for our state management needs, because the subscriber gets the last emitted value immediately after subscribing to the observable. Having an initial state is common to many state management libraries and removes the need to check against null or undefined.&lt;/p&gt;

&lt;p&gt;If we want to protect the state against changes from outside, we have a way to make it private and only expose the observable to the public.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth-Service
&lt;/h2&gt;

&lt;p&gt;To create a service using the Angular CLI type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ng g s auth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will generate a service &lt;code&gt;auth.service.ts&lt;/code&gt; in our app.&lt;/p&gt;

&lt;p&gt;We need to import our service somewhere, so the bundler includes it. Since this is a app-wide service that should run all the time, we can simply inject the service in &lt;code&gt;app.module.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="cm"&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;class&lt;/span&gt; &lt;span class="nx"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthService&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="nx"&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;starting AppModule&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;This will instantiate the AuthService as a singleton on the start of the app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In a real-world app, we would probably want to move the service in a separate module (ie. &lt;code&gt;auth.module.ts&lt;/code&gt;) and inject it in its constructor. Importing that module in &lt;code&gt;app.module.ts&lt;/code&gt; would have the same effect.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is our implementation of &lt;code&gt;auth.service.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&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;@angular/core&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;Auth&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-amplify/auth&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;Hub&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-amplify/core&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;BehaviorSubject&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;rxjs&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;map&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;rxjs/operators&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&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="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;initialAuthState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;isLoggedIn&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;id&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;email&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&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;class&lt;/span&gt; &lt;span class="nx"&gt;AuthService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;_authState&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;BehaviorSubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AuthState&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;initialAuthState&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/** AuthState as an Observable */&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;auth$&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;_authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asObservable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="cm"&gt;/** Observe the isLoggedIn slice of the auth state */&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;isLoggedIn$&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;auth$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the user on creation of this service&lt;/span&gt;
    &lt;span class="nx"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentAuthenticatedUser&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&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="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;setUser&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;_err&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;_authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialAuthState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Use Hub channel 'auth' to get notified on changes&lt;/span&gt;
    &lt;span class="nx"&gt;Hub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth&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;payload&lt;/span&gt;&lt;span class="p"&gt;:&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;data&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="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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signIn&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;// On 'signIn' event, the data is a CognitoUser object&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;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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialAuthState&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;setUser&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="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&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="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="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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sub&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="nx"&gt;email&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="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;isLoggedIn&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="nx"&gt;id&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="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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;We define our public interface of our state in &lt;code&gt;AuthState&lt;/code&gt; and create an initial object &lt;code&gt;initialAuthState&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We decided to use the default providedIn property to make this service tree-shakable (optional)&lt;/li&gt;
&lt;li&gt;We define and initialize a private BehaviorSubject and expose it as an observable&lt;/li&gt;
&lt;li&gt;We write a private function &lt;code&gt;setUser(user: any)&lt;/code&gt; to take a cognito user object, desctructure it and create the next state out of it&lt;/li&gt;
&lt;li&gt;We create an optional way of exposing a slice of the state, the &lt;code&gt;isLoggedIn&lt;/code&gt; boolean value of our state object.&lt;/li&gt;
&lt;li&gt;In the constructor we request the current user by writing &lt;code&gt;Auth.currentAuthenticatedUser()&lt;/code&gt; on instantiation of this injectable and emitting either the user object or the &lt;code&gt;initialAuthState&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We start listening on the hub channel named 'auth' and construct the next state on whether the user is signed in or not&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it! We can subscribe to state changes anywhere in our where we inject this service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using our auth state service
&lt;/h2&gt;

&lt;p&gt;To access our state, we inject the service and subscribe to any of the exposed observables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... imports&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&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;./app.component.css&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;authService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;authService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoggedIn$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;isLoggedIn&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isLoggedIn&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;authService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&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="nx"&gt;username&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="o"&gt;=&amp;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;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;id&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="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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I hope this helps to see how to handle state in Angular in a modular and simple way by moving it into a service and informing anyone interested of changes with the help of a RxJS BehaviorSubject.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>rxjs</category>
      <category>awsamplify</category>
    </item>
    <item>
      <title>Creating Angular UI components and connecting them to the GraphQL AppSync API - Part 2</title>
      <dc:creator>Michael Gustmann</dc:creator>
      <pubDate>Wed, 27 Mar 2019 17:43:08 +0000</pubDate>
      <link>https://dev.to/beavearony/creating-angular-ui-components-and-connecting-them-to-the-graphql-appsync-api-part-2-33n0</link>
      <guid>https://dev.to/beavearony/creating-angular-ui-components-and-connecting-them-to-the-graphql-appsync-api-part-2-33n0</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/beavearony/getting-started-with-a-angularnx-workspace-backed-by-an-aws-amplify-graphql-api---part-1-24m0"&gt;first part of the series&lt;/a&gt; we setup a workspace, configured AWS cloud resources in a shared library, added Authentication to protect private data, generated typings for the GraphQL API, wrote a mutation helper wrapper function and discussed various topics around this. &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/beavearony" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F147932%2F49278dd3-133a-4030-9697-801b747d5371.jpg" alt="beavearony"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/beavearony/getting-started-with-a-angularnx-workspace-backed-by-an-aws-amplify-graphql-api---part-1-24m0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Getting started with a Angular/NX workspace backed by an AWS Amplify GraphQL API - Part 1&lt;/h2&gt;
      &lt;h3&gt;Michael Gustmann ・ Mar 26 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#angular&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#graphql&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#awsamplify&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#awsappsync&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;br&gt;
We were busy, but until this point, we still haven't done much towards our UI. Let's create some components using the Angular CLI in this second part!

&lt;p&gt;I will omit some styles for brevity reasons, but you can find them &lt;a href="https://github.com/BeaveArony/amplify-angular-nx-todo" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/BeaveArony" rel="noopener noreferrer"&gt;
        BeaveArony
      &lt;/a&gt; / &lt;a href="https://github.com/BeaveArony/amplify-angular-nx-todo" rel="noopener noreferrer"&gt;
        amplify-angular-nx-todo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Angular with NX multi-app setup powered by an AWS Amplify GraphQL Backend&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/BeaveArony/amplify-angular-nx-todoapp-screenshot.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FBeaveArony%2Famplify-angular-nx-todoapp-screenshot.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting started&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;To deploy this project, hit this button ...&lt;/p&gt;
&lt;a href="https://console.aws.amazon.com/amplify/home#/deploy?repo=https://github.com/BeaveArony/amplify-angular-nx-todo" rel="nofollow noopener noreferrer"&gt;
    &lt;img src="https://camo.githubusercontent.com/eaa36be4db77fd727ce4c94d06d56e54db3d80f5a7f827e129c30d96a582bebf/68747470733a2f2f6f6e65636c69636b2e616d706c6966796170702e636f6d2f627574746f6e2e737667" alt="Deploy to Amplify Console"&gt;
&lt;/a&gt;
&lt;p&gt;... or go through the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone this repo&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/BeaveArony/amplify-angular-nx-todo.git&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Change into the directory &amp;amp; install the dependencies&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;

yarn&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Initialize the Amplify project&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;amplify init&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Create the resources in your account&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;amplify push&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Start the app&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm start&lt;/pre&gt;

&lt;/div&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/BeaveArony/amplify-angular-nx-todo" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Building the UI
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The todo-page that wraps the whole UI. The component, that will be imported by app-component and is going to be the only container/smart component of this lib&lt;/li&gt;
&lt;li&gt;The form to create new todos&lt;/li&gt;
&lt;li&gt;The filter component to switch visibility on the &lt;em&gt;completed&lt;/em&gt; property&lt;/li&gt;
&lt;li&gt;The todo-list component with its todo-item components&lt;/li&gt;
&lt;li&gt;A component to show error messages
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g c --project=todo-ui --export  todo-page
ng g c --project=todo-ui -c=OnPush create-form
ng g c --project=todo-ui -c=OnPush todo-list
ng g c --project=todo-ui -c=OnPush todo-item
ng g c --project=todo-ui -c=OnPush filter-bar
ng g c --project=todo-ui -c=OnPush error-alert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would like to style the app like the AWS authenticator component. To help with the styling, replace the content of &lt;code&gt;apps/todo-app/src/styles.css&lt;/code&gt; with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* You can add global styles to this file, and also import other style files */&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'~aws-amplify-angular/theme.css'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-accent-brown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Amplify UI fixes */&lt;/span&gt;
&lt;span class="nc"&gt;.amplify-form-input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.amplify-alert&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.amplify-greeting&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.amplify-greeting-flex-spacer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.amplify-authenticator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--component-width-desktop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-white&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--box-shadow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.card-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;320px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;480px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;.amplify-authenticator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--component-width-mobile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.card-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.amplify-alert&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4%&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;We are setting up html / body and card classes that look very similar to the Amplify Components styles. We are reusing the CSS variables imported earlier. The amplify-authenticator has rounded corners in the mobile view, so we are changing this in the media-query as well.&lt;/p&gt;

&lt;p&gt;We are also fixing the styling of the html input tags, because they are extending too far to the right in mobile.&lt;/p&gt;

&lt;p&gt;There are still some styling issues with the amplify angular authenticator component, but I think it's good enough for a simple drop in UI component to support the general sign-up/sign-in procedure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Todo-page
&lt;/h2&gt;

&lt;p&gt;The todo-page component is the starting point of the todo-ui module. It is the only component exported, leaving all other components internal to this lib. We could introduce routing at a later stage and even lazy load this module for a snappier start of our app.&lt;/p&gt;

&lt;p&gt;This component is also going to be the only container component handling the API communication and providing data to the other presentational components in this module. Since we haven't written any code in our components yet, we compose our view with what we got so far. All components were generated by the &lt;code&gt;ng generate&lt;/code&gt; commands and can be referenced in our HTML markup.&lt;/p&gt;

&lt;p&gt;Change &lt;code&gt;libs/todo-ui/src/lib/todo-page/todo-page.component.html&lt;/code&gt; to this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;my-create-form&amp;gt;&amp;lt;/my-create-form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;my-filter-bar&amp;gt;&amp;lt;/my-filter-bar&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-error-alert&amp;gt;&amp;lt;/my-error-alert&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;my-todo-list&amp;gt;&amp;lt;/my-todo-list&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and import the todo-ui module in our root app module &lt;code&gt;apps/todo-app/src/app/app.module.ts&lt;/code&gt; like this&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;// ... other imports&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;TodoUiModule&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;@my/todo-ui&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="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BrowserModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AppsyncModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TodoUiModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To display the todo-page in the app change &lt;code&gt;apps/todo-app/src/app/app.component.html&lt;/code&gt; to this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-todo-page&amp;gt;&amp;lt;/my-todo-page&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far this is just the starting point for our UI. You can run the app with &lt;code&gt;npm start&lt;/code&gt; and glance on a basic skeleton.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Authenticator UI-Component
&lt;/h2&gt;

&lt;p&gt;AWS Amplify provides several drop-in UI components for Angular/Ionic, React and Vue. We can choose between&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticator&lt;/li&gt;
&lt;li&gt;Photo Picker&lt;/li&gt;
&lt;li&gt;Album&lt;/li&gt;
&lt;li&gt;Chatbot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To add the AWS Authenticator to our app change &lt;code&gt;apps/todo-app/src/app/app.component.html&lt;/code&gt; to both include it and the todo-page component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;amplify-authenticator&amp;gt;&amp;lt;/amplify-authenticator&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-todo-page&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"hydrated &amp;amp;&amp;amp; isLoggedIn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-todo-page&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the content of &lt;code&gt;apps/todo-app/src/app/app.component.ts&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="k"&gt;import&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="nx"&gt;OnInit&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;@angular/core&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;AppSyncClient&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;@my/appsync&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;AmplifyService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-amplify-angular&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&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;./app.component.css&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;hydrated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;amplifyService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AmplifyService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;AppSyncClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hydrated&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hydrated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;client&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;amplifyService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authStateChange$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authState&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signedOut&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;AppSyncClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearStore&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;User signed out, store cleared!&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signedIn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmSignIn&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AppSyncClient persists its redux store, so we should render the todo-ui only when the store is rehydrated. AppSyncClient offers a &lt;code&gt;AppSyncClient.hydrated()&lt;/code&gt; function we can use for this.&lt;br&gt;
The AmplifyService offers an Observable &lt;code&gt;authStateChange$&lt;/code&gt; we can subscribe to and only show the todo-ui when a user is logged In. To not leave any cached items around when a user signs out we clear the store on that event. The &lt;code&gt;&amp;lt;my-todo-page&amp;gt;&lt;/code&gt; component is hidden while the client is not rehydrated or while no user is signed in with the &lt;code&gt;*ngIf&lt;/code&gt; directive. The &lt;code&gt;&amp;lt;amplify-authenticator&amp;gt;&lt;/code&gt; is showing a greeting message by default and a link to log out when a user is logged in, so we don't need to hide it.&lt;/p&gt;

&lt;p&gt;We can configure this component &lt;a href="https://aws-amplify.github.io/docs/js/angular#signup-configuration" rel="noopener noreferrer"&gt;by passing in a config-object&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;amplify-authenticator&lt;/span&gt; &lt;span class="na"&gt;[signUpConfig]=&lt;/span&gt;&lt;span class="s"&gt;"signUpConfig"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/amplify-authenticator&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or change the framework to Ionic and hide certain parts, like the SignOut link&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;amplify-authenticator&lt;/span&gt;
  &lt;span class="na"&gt;framework=&lt;/span&gt;&lt;span class="s"&gt;"ionic"&lt;/span&gt;
  &lt;span class="na"&gt;[hide]=&lt;/span&gt;&lt;span class="s"&gt;"['SignOut']"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/amplify-authenticator&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the presentational UI
&lt;/h2&gt;

&lt;p&gt;We start with the presentational components, that do not use any API and get their data only through &lt;code&gt;@Input()&lt;/code&gt; and emit events through &lt;code&gt;@Output()&lt;/code&gt; using Angular's &lt;code&gt;EventEmitter&lt;/code&gt;. Later we&lt;br&gt;
connect them in the todo-page component.&lt;/p&gt;
&lt;h3&gt;
  
  
  model
&lt;/h3&gt;

&lt;p&gt;Create a file &lt;code&gt;libs/todo-ui/src/lib/model.ts&lt;/code&gt; to keep types specific to this UI-Library. In our case we only type our Todo's visibility states.&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_ALL&lt;/span&gt;&lt;span class="dl"&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;SHOW_ACTIVE&lt;/span&gt;&lt;span class="dl"&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;SHOW_COMPLETED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  create-form component
&lt;/h3&gt;

&lt;p&gt;This create-form component emits a new todo name when the reactive form is submitted. We also disable the submit button, when the required text field is empty. Once we emitted a new todo, we clear the input field. Using the form tag together with the submit button gives us the advantage to just hit 'Enter' after typing our todo text.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;libs/todo-ui/src/lib/create-form/create-form.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;[formGroup]=&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt; &lt;span class="na"&gt;(ngSubmit)=&lt;/span&gt;&lt;span class="s"&gt;"createTodo()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"todoName"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;[disabled]=&lt;/span&gt;&lt;span class="s"&gt;"!form.valid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;libs/todo-ui/src/lib/create-form/create-form.component.ts&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ChangeDetectionStrategy&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="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Output&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;@angular/core&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;FormControl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&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;@angular/forms&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-create-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./create-form.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&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;./create-form.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&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;class&lt;/span&gt; &lt;span class="nc"&gt;CreateFormComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;created&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;EventEmitter&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;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;todoName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&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="nf"&gt;createTodo&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;todoName&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;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&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;todoName&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;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoName&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;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;todoName&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  filter-bar component
&lt;/h3&gt;

&lt;p&gt;The filter-bar component holds a bar of toggle buttons to change the visibility of the todo-list. The selected button will be disabled and styled so that it looks active. We can use CSS variables from the Amplify Angular Theme for that.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;libs/todo-ui/src/lib/filter-bar/filter-bar.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
    &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let btn of buttons"&lt;/span&gt;
    &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"filter(btn.value)"&lt;/span&gt;
    &lt;span class="na"&gt;[disabled]=&lt;/span&gt;&lt;span class="s"&gt;"currentFilter === btn.value"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ btn.name }}
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;libs/todo-ui/src/lib/filter-bar/filter-bar.component.css&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--button-height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;128px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;text-transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-accent-blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:disabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&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;&lt;code&gt;libs/todo-ui/src/lib/filter-bar/filter-bar.component.ts&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ChangeDetectionStrategy&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="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Output&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;@angular/core&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;TodoFilterType&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;../model&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-filter-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./filter-bar.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&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;./filter-bar.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&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;class&lt;/span&gt; &lt;span class="nc"&gt;FilterBarComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;filtered&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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TodoFilterType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nl"&gt;currentFilter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_ALL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}[]&lt;/span&gt; &lt;span class="o"&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_ALL&lt;/span&gt;&lt;span class="dl"&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;All&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_ACTIVE&lt;/span&gt;&lt;span class="dl"&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;Active&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_COMPLETED&lt;/span&gt;&lt;span class="dl"&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;Completed&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;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&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;currentFilter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;query&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;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&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;currentFilter&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;We emit a filter query string depending on the selected button. The parent todo-page component will listen to this and pass the query to its child todo-list component as Input, so it can filter the list on the &lt;em&gt;completed&lt;/em&gt; property.&lt;/p&gt;

&lt;h3&gt;
  
  
  todo-item component
&lt;/h3&gt;

&lt;p&gt;The todo-list renders a list of todo-item components. These items are responsible to display the provided input values. We use HTML entities as icons. When clicking on the delete icon we emit the &lt;code&gt;deleted&lt;/code&gt; event and when clicked on the rest of this item a &lt;code&gt;toggled&lt;/code&gt; event is emitted to change the &lt;em&gt;completed&lt;/em&gt; property of this todo item.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;libs/todo-ui/src/lib/todo-item/todo-item.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"checkmark"&lt;/span&gt;
    &lt;span class="na"&gt;[class.checked]=&lt;/span&gt;&lt;span class="s"&gt;"todo.completed"&lt;/span&gt;
    &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"toggled.emit()"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ todo.completed ? '✔' : '🗸' }}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;
    &lt;span class="na"&gt;[ngStyle]=&lt;/span&gt;&lt;span class="s"&gt;"{ textDecoration: todo.completed ? 'line-through' : 'none' }"&lt;/span&gt;
    &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"toggled.emit()"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ todo.name }}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"deleted.emit()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;✗&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;libs/todo-ui/src/lib/todo-item/todo-item.component.ts&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ChangeDetectionStrategy&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="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Output&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;@angular/core&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;ListTodos_listTodos_items&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;@my/appsync&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-todo-item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./todo-item.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&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;./todo-item.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&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;class&lt;/span&gt; &lt;span class="nc"&gt;TodoItemComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListTodos_listTodos_items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;toggled&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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;deleted&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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  todo-list component
&lt;/h3&gt;

&lt;p&gt;The todo-list component renders each todo item through the previously created todo-item component. To react on the Inputs to filter the todos we use getters and setters. Component life cycle hook &lt;code&gt;ngOnChanges&lt;/code&gt; would also work here.&lt;/p&gt;

&lt;p&gt;The visibility filter is the only client state in our app and is managed in our components. Apollo Client, which is used by AWSAppSyncClient under the hoods, keeps the data in a normalized redux store.&lt;br&gt;&lt;br&gt;
We have many choices here, when we think about our architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manage the state in an Angular provider using a Behavior Subject&lt;/li&gt;
&lt;li&gt;Write actions and reducers to extend the underlying redux store&lt;/li&gt;
&lt;li&gt;Use a separate state management library for our client state&lt;/li&gt;
&lt;li&gt;Extend Apollo Client to use apollo-link-state, which offers querying and mutating our local state with GraphQL operations&lt;/li&gt;
&lt;li&gt;Keep it in our components&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extending the underlying redux store is a little bit risky, since the AWSAppSyncClient can change the implementation internals at any time. We would get stuck with a certain version or need to refactor our client state.&lt;/p&gt;

&lt;p&gt;For this simple app using a Behavior Subject is probably the easiest. For non-trivial apps I would use a state management library. Using apollo-link-state feels like the cleanest solution, having local and remote state in one place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- libs/todo-ui/src/lib/todo-list/todo-list.component.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-todo-item&lt;/span&gt;
  &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let todo of visibleTodos"&lt;/span&gt;
  &lt;span class="na"&gt;[todo]=&lt;/span&gt;&lt;span class="s"&gt;"todo"&lt;/span&gt;
  &lt;span class="na"&gt;(toggled)=&lt;/span&gt;&lt;span class="s"&gt;"toggled.emit(todo)"&lt;/span&gt;
  &lt;span class="na"&gt;(deleted)=&lt;/span&gt;&lt;span class="s"&gt;"deleted.emit(todo)"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-todo-item&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"!visibleTodos || visibleTodos?.length === 0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;No todos&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// libs/todo-ui/src/lib/todo-list/todo-list.component.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;ChangeDetectionStrategy&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="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Output&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;@angular/core&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;ListTodos_listTodos_items&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;@my/appsync&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;TodoFilterType&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;../model&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-todo-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./todo-list.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&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;./todo-list.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Default&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;class&lt;/span&gt; &lt;span class="nc"&gt;TodoListComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;toggled&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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ListTodos_listTodos_items&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="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;deleted&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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ListTodos_listTodos_items&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;private&lt;/span&gt; &lt;span class="nx"&gt;_filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListTodos_listTodos_items&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;_filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&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;_filter&lt;/span&gt; &lt;span class="o"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visibleTodos&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="nf"&gt;getVisibleTodos&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;todos&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="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ListTodos_listTodos_items&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_todos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListTodos_listTodos_items&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;_todos&lt;/span&gt; &lt;span class="o"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visibleTodos&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="nf"&gt;getVisibleTodos&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nl"&gt;visibleTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListTodos_listTodos_items&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;getVisibleTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListTodos_listTodos_items&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_ALL&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="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_COMPLETED&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="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_ACTIVE&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="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nl"&gt;default&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;todos&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever either the filter query or the provided todos change, we calculate the visible todos and store them in &lt;em&gt;visibleTodos&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  error-alert component
&lt;/h2&gt;

&lt;p&gt;This reusable component receives an error message and emits an event if the close button is clicked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"errorMessage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⚠&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"msg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ errorMessage }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"close"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"closed.emit()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;×&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.flex-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.close&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-gray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.msg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--red&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;38px&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ChangeDetectionStrategy&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="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;EventEmitter&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;@angular/core&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-error-alert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./error-alert.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&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;./error-alert.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&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;class&lt;/span&gt; &lt;span class="nc"&gt;ErrorAlertComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&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;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;closed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Watching todos
&lt;/h2&gt;

&lt;p&gt;Now that we have build our presentational components, we can come back to our wrapping component, that handles all the events, interacts with our API and displays possible errors.&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;// imports ...&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-todo-page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./todo-page.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&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;./todo-page.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Default&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;class&lt;/span&gt; &lt;span class="nc"&gt;TodoPageComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ListTodos_listTodos_items&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&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;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;AppSyncClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;watchQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ListTodos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ListTodosVariables&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LIST_TODOS&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoList&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoList&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;todoList&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;listTodos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdOnClientAt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;localeCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdOnClientAt&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleError&lt;/span&gt; &lt;span class="o"&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="nx"&gt;ApolloError&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&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="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;We listen to any local changes of our todos in &lt;em&gt;ngOnInit&lt;/em&gt; life cycle hook. Using &lt;em&gt;watchQuery()&lt;/em&gt; for this, informs our component of local operations like adding or removing items. To listen to remote changes, we would need to set up a GraphQL subscription. Fortunately &lt;em&gt;watchQuery()&lt;/em&gt; is generic so we can specify the previously generated query and variables types. This offers us complete IntelliSense in the subscribe block! If we receive data, we sort them on the client on the &lt;em&gt;createdOnClientAt&lt;/em&gt; property.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;watchQuery()&lt;/em&gt; returns a zen-observable, not one from rxjs! So, we can't use the async pipe here. We could write a converter or simply use apollo-angular to do this for us.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have to think about change detection here. If this component would use the OnPush ChangeDetectionStragy, our UI would not react to changes coming from this observable. We can either change to the Default strategy like above or inject &lt;em&gt;ChangeDetectorRef&lt;/em&gt; to invoke &lt;em&gt;markForCheck()&lt;/em&gt; in the subscribe block.&lt;/p&gt;

&lt;p&gt;Something like this, focussing on the changes:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectorRef&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;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&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;class&lt;/span&gt; &lt;span class="nc"&gt;TodoPageComponent&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectorRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;AppSyncClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;watchQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ListTodos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ListTodosVariables&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LIST_TODOS&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoList&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;// ...&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;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markForCheck&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Angular's async pipe would handle this automatically for us!&lt;/p&gt;

&lt;p&gt;Change one line in the html file to pass our todos to the todo-list component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-todo-list&lt;/span&gt; &lt;span class="na"&gt;[todos]=&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-todo-list&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interact with the error-alert component by passing the error message and setting it to &lt;em&gt;null&lt;/em&gt; when the &lt;em&gt;closed&lt;/em&gt; event emits&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-error-alert&lt;/span&gt;
  &lt;span class="na"&gt;(closed)=&lt;/span&gt;&lt;span class="s"&gt;"errorMessage = null"&lt;/span&gt;
  &lt;span class="na"&gt;[errorMessage]=&lt;/span&gt;&lt;span class="s"&gt;"errorMessage"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-error-alert&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating todos
&lt;/h2&gt;

&lt;p&gt;To have items to display in our list, we need to be able to create a new todo. So, let's implement a handler in our component class and pass it the event payload of the &lt;em&gt;created&lt;/em&gt; event of the create-form component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-create-form&lt;/span&gt; &lt;span class="na"&gt;(created)=&lt;/span&gt;&lt;span class="s"&gt;"create($event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-create-form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoPageComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nf"&gt;create&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;executeMutation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CreateTodoVariables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CreateTodo_createTodo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CREATE_TODO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;variablesInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;completed&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;createdOnClientAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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="na"&gt;cacheUpdateQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LIST_TODOS&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo&lt;/span&gt;&lt;span class="dl"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleError&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;Here happens quite a lot in a single statement. Let's break it down!&lt;/p&gt;

&lt;p&gt;We invoke our generic helper function &lt;em&gt;executeMutation()&lt;/em&gt; and pass it an object. This object needs the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;mutation&lt;/strong&gt;: The graphql mutation operation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;variablesInfo&lt;/strong&gt;: The input object we want to send to our API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cacheUpdateQuery&lt;/strong&gt;: A list of queries we would like to be updated in our cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;typename&lt;/strong&gt;: The value of the &lt;em&gt;__typename&lt;/em&gt; property&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we add a new item to our list, we need to update the &lt;em&gt;LIST_TODOS&lt;/em&gt; query in the cache. It's as simple as adding it to the &lt;em&gt;cacheUpdateQuery&lt;/em&gt;'s array. Doing this will invoke the watchQuery observable twice. First with an optimistic response using a temporary ID, like we would expect our API to respond if all goes well and finally with the actual result from the server.&lt;/p&gt;

&lt;p&gt;If we would receive an error when trying to create the todo, the second call would not contain this new todo anymore, negating the optimistic UI update. Additionally, we are handling the error in the &lt;em&gt;catch&lt;/em&gt; part of the returned promise and displaying it with our error-alert component. This way the user gets a hint of what just happened, even though the error message is quite technical.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note, everything is type safe, even the &lt;em&gt;typename&lt;/em&gt; string!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Updating a todo
&lt;/h2&gt;

&lt;p&gt;To update the completed property of a todo, we listen to the &lt;em&gt;toggled&lt;/em&gt; event of the todo-list component. We pass the payload to the &lt;em&gt;toggle()&lt;/em&gt; function, destructuring it in the process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-todo-list&lt;/span&gt; &lt;span class="na"&gt;[todos]=&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt; &lt;span class="na"&gt;(toggled)=&lt;/span&gt;&lt;span class="s"&gt;"toggle($event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-todo-list&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoPageComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nf"&gt;toggle&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="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;createdOnClientAt&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="nx"&gt;ListTodos_listTodos_items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;executeMutation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UpdateTodoVariables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UpdateTodo_updateTodo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UPDATE_TODO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;variablesInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;completed&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="nx"&gt;createdOnClientAt&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;cacheUpdateQuery&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;typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo&lt;/span&gt;&lt;span class="dl"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleError&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;This looks very similar to the create mutation. We provide the updated item and this time we don't need to take care of updating the cache. Apollo Client is handling this automatically, so we can pass &lt;em&gt;null&lt;/em&gt; or an empty array to &lt;em&gt;cacheUpdateQuery&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;ListTodos_listTodos_items&lt;/em&gt; looks pretty weird as an input type. It's the type of one item of the list, but I guess that's the price we have to pay when this gets generated automatically!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deleting a todo
&lt;/h2&gt;

&lt;p&gt;To delete a todo, we listen to the &lt;em&gt;deleted&lt;/em&gt; event of the todo-list component. We pass the payload to the &lt;em&gt;toggle()&lt;/em&gt; function, destructuring only the &lt;em&gt;id&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-todo-list&lt;/span&gt;
  &lt;span class="na"&gt;[todos]=&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;
  &lt;span class="na"&gt;(toggled)=&lt;/span&gt;&lt;span class="s"&gt;"toggle($event)"&lt;/span&gt;
  &lt;span class="na"&gt;(deleted)=&lt;/span&gt;&lt;span class="s"&gt;"delete($event)"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-todo-list&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoPageComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="k"&gt;delete&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="nx"&gt;ListTodos_listTodos_items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;executeMutation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DeleteTodoVariables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DeleteTodo_deleteTodo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DELETE_TODO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;variablesInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;cacheUpdateQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LIST_TODOS&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo&lt;/span&gt;&lt;span class="dl"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleError&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;Deleting an item from a list requires us to take care of the cache again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling the filter
&lt;/h2&gt;

&lt;p&gt;The only thing left is listening to the filter-bar component and passing the value to the todo-list component. We start with &lt;em&gt;'SHOW_ALL'&lt;/em&gt;, but don't do anything else with the filter variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-filter-bar&lt;/span&gt; &lt;span class="na"&gt;(filtered)=&lt;/span&gt;&lt;span class="s"&gt;"filter = $event"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/my-filter-bar&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-todo-list&lt;/span&gt;
  &lt;span class="na"&gt;[todos]=&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;
  &lt;span class="na"&gt;[filter]=&lt;/span&gt;&lt;span class="s"&gt;"filter"&lt;/span&gt;
  &lt;span class="na"&gt;(toggled)=&lt;/span&gt;&lt;span class="s"&gt;"toggle($event)"&lt;/span&gt;
  &lt;span class="na"&gt;(deleted)=&lt;/span&gt;&lt;span class="s"&gt;"delete($event)"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-todo-list&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoFilterType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOW_ALL&lt;/span&gt;&lt;span class="dl"&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 it. Please take a look at &lt;a href="https://github.com/BeaveArony/amplify-angular-nx-todo" rel="noopener noreferrer"&gt;this repo&lt;/a&gt; to see the complete source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking it for a spin
&lt;/h2&gt;

&lt;p&gt;Let's run &lt;code&gt;npm start&lt;/code&gt; to get a chance to admire our work. Here are a few things for you to play around with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create at least two users to see how each one has its own todos&lt;/li&gt;
&lt;li&gt;Add new todos&lt;/li&gt;
&lt;li&gt;Try out the responsive UI&lt;/li&gt;
&lt;li&gt;Open the dev-tools and tick the &lt;em&gt;offline&lt;/em&gt; checkbox, do a few operations and finally go online again.&lt;/li&gt;
&lt;li&gt;Open the redux dev-tools (if installed) and inspect the &lt;em&gt;outbox&lt;/em&gt; of redux-offline, the normalized entities of apollo client's InMemoryCache and the rehydration actions of redux-persist&lt;/li&gt;
&lt;li&gt;Take a look at things stored in IndexedDB and the JWT in LocalStorage&lt;/li&gt;
&lt;li&gt;Set a breakpoint in &lt;em&gt;watchQuery()&lt;/em&gt; and observe the optimistic response in action&lt;/li&gt;
&lt;li&gt;Change the name variable to &lt;em&gt;null&lt;/em&gt; in &lt;em&gt;create()&lt;/em&gt; and enjoy the Undo taking place in the list paired with the splendid error message displayed&lt;/li&gt;
&lt;li&gt;Reload the app to appreciate the persisted cache&lt;/li&gt;
&lt;li&gt;Login to the AWS management console and analyze the DynamoDB Table entries created&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;We created our UI with Angular components. The wrapping component orchestrates everything by connecting the presentational components, listening to changes in the cache and talking to the API using helpers we set up in &lt;a href="https://dev.to/beavearony/getting-started-with-a-angularnx-workspace-backed-by-an-aws-amplify-graphql-api---part-1-24m0"&gt;Part 1&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;In &lt;a href="https://dev.to/beavearony/personal-growth-with-git-flow-and-aws-amplify-console-part-3-1f6c"&gt;Part 3&lt;/a&gt; we are going to explore how to add a CI/CD pipeline the easy way using the amplify console and even look at how to add a second frontend app.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>graphql</category>
      <category>awsamplify</category>
      <category>awsappsync</category>
    </item>
    <item>
      <title>Getting started with a Angular/NX workspace backed by an AWS Amplify GraphQL API - Part 1</title>
      <dc:creator>Michael Gustmann</dc:creator>
      <pubDate>Tue, 26 Mar 2019 17:01:56 +0000</pubDate>
      <link>https://dev.to/beavearony/getting-started-with-a-angularnx-workspace-backed-by-an-aws-amplify-graphql-api---part-1-24m0</link>
      <guid>https://dev.to/beavearony/getting-started-with-a-angularnx-workspace-backed-by-an-aws-amplify-graphql-api---part-1-24m0</guid>
      <description>&lt;p&gt;We are going to create a Todo app using &lt;a href="https://angular.io" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; with the help of&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nx.dev" rel="noopener noreferrer"&gt;Nx&lt;/a&gt; to help with a multi-app, multi-team project style&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws-amplify.github.io/docs" rel="noopener noreferrer"&gt;AWS Amplify CLI&lt;/a&gt; to create a fully featured backend, completely managed in code (infrastructure as code)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/awslabs/aws-mobile-appsync-sdk-js" rel="noopener noreferrer"&gt;AWS AppSync JavaScript SDK&lt;/a&gt; An AppSync client using Apollo and featuring offline and persistence capabilities&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws-amplify.github.io/docs/js/angular" rel="noopener noreferrer"&gt;AWS Amplify Angular&lt;/a&gt; Angular components and an Angular provider which helps integrate with the AWS-Amplify library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are creating an offline-ready Todo-App backed by a GraphQL API and Authentication. We can expand on this app by adding further categories and features. But let's start simple.&lt;/p&gt;

&lt;p&gt;You can take a look at the code &lt;a href="https://github.com/BeaveArony/amplify-angular-nx-todo" rel="noopener noreferrer"&gt;in this repo&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;I'm assuming, that you have &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;NodeJS&lt;/a&gt; version 8 or higher installed and that you have access to an AWS Account. The free tier is very generous during the first year and will probably cost you nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a workspace
&lt;/h2&gt;

&lt;p&gt;NX is a schematic on top of Angular CLI, that gives us tools to setup a workspace, where it is easy to share code between apps and to create boundaries to help working within a team or even multiple teams. &lt;/p&gt;

&lt;p&gt;By separating out logic into modules these can be reused easily by just importing them like we are importing other npm packages. We don't need to publish any of those packages to some public or private repository. This saves us some headaches about running tests across different versions of each library and provides us a snapshot in our repo of all the parts working together.&lt;/p&gt;

&lt;p&gt;With the AWS Amplify CLI we can even connect one or more cloud stacks to our workspace, so that we get 'Infrastructure as Code' of our backend going along with our frontend apps. It's not in the sense of having completely different products all put into the same repo. Rather thinking in terms of having a customer web- and separate mobile app, an admin portal, an electron desktop app for the support team, a manager reports app and so on... They all work with the same data in a way and would immensely profit from shared SDKs and public interfaces across libraries.&lt;/p&gt;

&lt;p&gt;Even if you end up using just one app, where you include different domains or Lines of Businesses (LOB), it helps to structure the code base so that teams can work at the same time without worrying too much about the painful integration into production later.&lt;/p&gt;

&lt;p&gt;We start by creating a workspace folder by executing the following command using npx, so that we don't have to globally install it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @nrwl/schematics amplify-graphql-angular-nx-todo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When asked, we answer some questions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Which stylesheet format would you like to use?&lt;/em&gt;&lt;/strong&gt; CSS&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? What is the npm scope you would like to use for your Nx Workspace?&lt;/em&gt;&lt;/strong&gt; my&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? What to create in the new workspace (You can create other applications and libraries at any point using 'ng g')&lt;/em&gt;&lt;/strong&gt; empty&lt;/p&gt;

&lt;p&gt;We named our workspace 'my' and it should be usually named after your organization or project, ie. 'myorg'.&lt;/p&gt;

&lt;p&gt;We create an empty workspace, so that we can name our first application however we want. Otherwise it would get the name 'amplify-graphql-angular-nx-todo'.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create an app
&lt;/h2&gt;

&lt;p&gt;We decide to call our first app 'todo-app' (and it will be the only one for this post).&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g app todo-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;em&gt;? What framework would you like to use for the application?&lt;/em&gt;&lt;/strong&gt; Angular&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? In which directory should the application be generated?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Which stylesheet format would you like to use?&lt;/em&gt;&lt;/strong&gt; CSS&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Which Unit Test Runner would you like to use for the application?&lt;/em&gt;&lt;/strong&gt; Jest&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Which E2E Test Runner would you like to use for the application?&lt;/em&gt;&lt;/strong&gt; Cypress&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Which tags would you like to add to the application? (used for linting)&lt;/em&gt;&lt;/strong&gt; app&lt;/p&gt;

&lt;p&gt;This created two apps&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;todo-app&lt;/li&gt;
&lt;li&gt;todo-app-e2e&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second app is an end-to-end test. The app is our actual app and we will try to only use it for orchestrating other modules as much as possible. Most of our code we will put in libs to reuse it in possible further apps in the future.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create libs
&lt;/h2&gt;

&lt;p&gt;Where do we put our code then? NX makes it easy to create new libs! We compose our app out of possibly many libs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We need a lib for our backend stuff, let's call this lib &lt;code&gt;appsync&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We put our UI components into the lib &lt;code&gt;todo-ui&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let's run the following commands to generate our desired libs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g lib appsync --tags=shared --framework=angular --unitTestRunner=jest --style=css
ng g lib todo-ui --tags=ui --framework=angular --unitTestRunner=jest --style=css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Create the AWS backend
&lt;/h2&gt;

&lt;p&gt;The (AWS Amplify CLI)[&lt;a href="https://github.com/aws-amplify/amplify-cli" rel="noopener noreferrer"&gt;https://github.com/aws-amplify/amplify-cli&lt;/a&gt;] makes it very easy to create a serverless backend in the AWS cloud. Everything is defined in code and can be checked in our source control. It elevates the need to write lengthy CloudFormation templates that describe our cloud resources. A few simple commands will spin up entire cloud stacks using team resources or creating one-of developer sand-boxes to try out new features.&lt;/p&gt;

&lt;p&gt;Install the Amplify CLI globally (if haven't done so previously) by running those commands:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @aws-amplify/cli
amplify configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you are completely new to AWS Amplify, &lt;a href="https://aws-amplify.github.io/docs" rel="noopener noreferrer"&gt;you should check out the docs of how to configure the Amplify CLI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we actually install the AWS Amplify CLI globally instead of using npx! We will use the amplify command quite often.&lt;/p&gt;

&lt;p&gt;Install the Amplify JavaScript dependencies&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i aws-amplify aws-amplify-angular
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Adjusting the angular app to Amplify's SDK
&lt;/h2&gt;

&lt;p&gt;There are a few things we need to change in order for us to use the AWS Amplify SDK in an Angular environment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tell typescript about the &lt;code&gt;window.global&lt;/code&gt; variable&lt;/li&gt;
&lt;li&gt;Add 'node' to tsconfig&lt;/li&gt;
&lt;li&gt;Write a script to rename aws-exports.js to aws-exports.ts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Open &lt;code&gt;apps/todo-app/src/polyfills.ts&lt;/code&gt; and add the following line at the top of the 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="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;node&lt;/code&gt; package needs to be included in &lt;code&gt;apps/todo-app/tsconfig.app.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&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;"types"&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="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;AWS Amplify comes with an UI-Library. We might just as well reuse the styles from those components and build upon them. Add the following lines to &lt;code&gt;apps/todo-app/src/styles.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* You can add global styles to this file, and also import other style files */&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'~aws-amplify-angular/theme.css'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We connect our project or branch to a cloud resource stack by running&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Using NX we need to change quite a few defaults:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Enter a name for the project&lt;/em&gt;&lt;/strong&gt; amplifynx&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Enter a name for the environment&lt;/em&gt;&lt;/strong&gt; master&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Choose your default editor:&lt;/em&gt;&lt;/strong&gt; Visual Studio Code&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Choose the type of app that you're building&lt;/em&gt;&lt;/strong&gt; javascript&lt;br&gt;
Please tell us about your project&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? What javascript framework are you using&lt;/em&gt;&lt;/strong&gt; angular&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Source Directory Path:&lt;/em&gt;&lt;/strong&gt; libs/appsync/src/l&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Distribution Directory Path:&lt;/em&gt;&lt;/strong&gt; dist/apps/todo-app&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Build Command:&lt;/em&gt;&lt;/strong&gt; npm run-script build&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Start Command:&lt;/em&gt;&lt;/strong&gt; npm run-script start&lt;br&gt;
Using default provider awscloudformation&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Do you want to use an AWS profile?&lt;/em&gt;&lt;/strong&gt; Yes&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Please choose the profile you want to use&lt;/em&gt;&lt;/strong&gt; my.aws.profile&lt;br&gt;
⠇ Initializing project in the cloud...&lt;/p&gt;

&lt;p&gt;We named our cloud environment master, corresponding to our current git branch master. We can easily add further branches, like develop and create equally named environments (stacks) in the cloud.&lt;/p&gt;

&lt;p&gt;We set the &lt;strong&gt;&lt;em&gt;Source Directory Path&lt;/em&gt;&lt;/strong&gt; to our previously created lib named &lt;em&gt;appsync&lt;/em&gt; so that the automatically generated file aws_exports.js will land in this folder by default. This way we can share our cloud config with different apps by just importing this lib.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;em&gt;Distribution Directory Path&lt;/em&gt;&lt;/strong&gt; points to the dist output of a single app, which is only important, if we use the amplify CLI commands to build or serve the app or if we connect our app to &lt;a href="https://amplify.aws" rel="noopener noreferrer"&gt;Amplify Console&lt;/a&gt;. The latter makes it very easy to create a CI/CD Pipeline by just connecting a GIT branch to a deployment.&lt;/p&gt;

&lt;p&gt;You will notice a new top-level folder named &lt;code&gt;amplify&lt;/code&gt;. Most of the files created in this folder were added to &lt;code&gt;.gitignore&lt;/code&gt;. The rest of the files can and should be added to version control and can be shared with your team. If you plan to make your repo public you should also add &lt;code&gt;amplify/team-provider-info.json&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's add our first category &lt;code&gt;auth&lt;/code&gt;!
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify add auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I like to change the default configuration to soften the password policy and provide a custom email subject text, but you can choose the defaults if you like and later change any of those settings by running&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify auth update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To create these &lt;code&gt;auth&lt;/code&gt; resources in the cloud run&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You will see&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current Environment: master

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Auth     | cognitonxtodo | Create    | awscloudformation |
? Are you sure you want to continue? (Y/n)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And after a while you will find the newly created Cognito user pool when you log in to your AWS console.&lt;br&gt;&lt;br&gt;
This command will take you there:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify console auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Rename aws-exports.js
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;amplify push&lt;/code&gt; command should have generated a file: &lt;code&gt;libs/appsync/src/lib/aws-exports.js&lt;/code&gt;, which we should rename to &lt;code&gt;aws-exports.ts&lt;/code&gt; to easily import the config in other typescript files. To automate this we use the scripts section in &lt;code&gt;package.json&lt;/code&gt;. Now each time we either serve or build our app the file is renamed.&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;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run rename:aws:config || ng serve; ng serve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run rename:aws:config || ng build --prod; ng build --prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rename:aws:config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd libs/appsync/src/lib &amp;amp;&amp;amp; [ -f aws-exports.js ] &amp;amp;&amp;amp; mv aws-exports.js aws-exports.ts || true"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We should also add this renamed file to &lt;code&gt;.gitignore&lt;/code&gt;, so change &lt;code&gt;aws-export.js&lt;/code&gt; to &lt;code&gt;aws-exports.*s&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding a GraphQL API
&lt;/h2&gt;

&lt;p&gt;If you have ever written a GraphQL API before you will appreciate the simplicity of creating one by just typing a few lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify add api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Please select from one of the below mentioned services&lt;/em&gt;&lt;/strong&gt; GraphQL&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Provide API name:&lt;/em&gt;&lt;/strong&gt; amplifynx&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Choose an authorization type for the API&lt;/em&gt;&lt;/strong&gt; Amazon Cognito User Pool&lt;br&gt;&lt;br&gt;
Use a Cognito user pool configured as a part of this project&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Do you have an annotated GraphQL schema?&lt;/em&gt;&lt;/strong&gt; No&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Do you want a guided schema creation?&lt;/em&gt;&lt;/strong&gt; Yes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? What best describes your project:&lt;/em&gt;&lt;/strong&gt; Single object with fields (e.g., “Todo” with ID, name, description)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Do you want to edit the schema now?&lt;/em&gt;&lt;/strong&gt; Yes&lt;/p&gt;

&lt;p&gt;This will open your previously configured editor with a sample Todo schema:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Todo @model {
  id: ID!
  name: String!
  description: String
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We don't want the description, but would like the &lt;em&gt;completed&lt;/em&gt; status of our todos to be tracked.&lt;br&gt;
At first to not worry too much about offline and sorting in the backend we can add a &lt;em&gt;createdOnClientAt&lt;/em&gt; timestamp to sort the todos on the client. AppSync adds &lt;em&gt;createdAt&lt;/em&gt; and &lt;em&gt;updateAt&lt;/em&gt; timestamps by default, which we could use in the schema, but it will mess with our todo-list when we queue up several todos while we are offline and reconnect.&lt;/p&gt;

&lt;p&gt;To explain this a little:&lt;/p&gt;

&lt;p&gt;Say we only rely on the &lt;em&gt;createdAt&lt;/em&gt; property to sort and we add todo-1 and todo-2 while offline. These get added to our outbox. They are sorted correctly, because we add a timestamp when we create each of those todos. Once we are online, todo-1 will be sent to the server and will receive a new &lt;em&gt;createdAt&lt;/em&gt; timestamp on the server, since it was created there. This timestamp is at a later point in time than todo-2's timestamp. So todo-1 will jump to position 2 until todo-2 was transmitted. After todo-2 was sent to the server, getting a new timestamp, it will jump back to position 2 again. We will see a short reordering happen in our client.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;createdOnClientAt&lt;/em&gt; property is not touched on the server.&lt;/p&gt;

&lt;p&gt;Change the schema to this and save:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Todo @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  name: String!
  completed: Boolean!
  createdOnClientAt: AWSDateTime!
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We added &lt;code&gt;@auth(rules: [{allow: owner}])&lt;/code&gt; to the Todo type to only show the todos of each logged in user. This will automatically add an &lt;em&gt;owner&lt;/em&gt; field to each todo in the underlying todo table in DynamoDB.&lt;/p&gt;
&lt;h2&gt;
  
  
  What happened behind the scenes?
&lt;/h2&gt;

&lt;p&gt;When you look into the &lt;code&gt;amplify/backend/api/{angularnx}/build&lt;/code&gt; directory, you will find an oppiniated and enhanced GraphQL schema by the AWS Amplify team. It generated all types of CRUD operations and Input objects with filtering and pagination automatically! By just writing six lines of annotated graphql we get 116 lines of graphql best practices out of the box! That's just dandy!&lt;/p&gt;
&lt;h2&gt;
  
  
  Upload the API
&lt;/h2&gt;

&lt;p&gt;When we decide to push our API for the first time we get asked a few questions about generating code and where to place it. Choose &lt;em&gt;angular&lt;/em&gt; as language target and leave the rest as default!&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current Environment: master

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Api      | amplifynx     | Create    | awscloudformation |
| Auth     | cognitonxtodo | No Change | awscloudformation |
? Are you sure you want to continue? Yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;em&gt;? Do you want to generate code for your newly created GraphQL API&lt;/em&gt;&lt;/strong&gt; Yes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Choose the code generation language target&lt;/em&gt;&lt;/strong&gt; angular&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;? Enter the file name pattern of graphql queries, mutations and subscriptions&lt;/em&gt;&lt;/strong&gt; (src/graphql/&lt;strong&gt;/_.graphql)&lt;br&gt;&lt;br&gt;
_&lt;/strong&gt;? Enter the file name pattern of graphql queries, mutations and subscriptions*&lt;em&gt;_ src/graphql/API.service.ts&lt;br&gt;&lt;br&gt;
_&lt;/em&gt;*? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions*** Yes&lt;br&gt;&lt;br&gt;
⠋ Updating resources in the cloud. This may take a few minutes...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is a bug when trying to use another directory, like our lib folder, so just leave the default paths &lt;code&gt;src/graphql/*&lt;/code&gt;! Otherwise the service or types will end up in our lib and be pretty much empty and all *.graphql files will be in the default location.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Amplify Code Generator
&lt;/h2&gt;

&lt;p&gt;We get two choices when we decide to generate code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;angular&lt;/li&gt;
&lt;li&gt;typescript&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1. angular
&lt;/h3&gt;

&lt;p&gt;If we choose angular as the target (which we do in our example), we get a single file with all the typescript types and an angular service. This service uses the amplify SDK to connect to the API. It's not really useful to us, since we decided to use the AppSync SDK as our client instead to connect to our cloud resources to get offline and persistance out of the box. We won't use the types nor the service directly, but the operations defined in the service can provide a good starting point to write our own queries and mutations.&lt;/p&gt;

&lt;p&gt;When choosing &lt;em&gt;angular&lt;/em&gt; also generates &lt;code&gt;queries.graphql&lt;/code&gt;, &lt;code&gt;mutations.graphql&lt;/code&gt;, &lt;code&gt;subscriptions.graphql&lt;/code&gt; and an introspection schema &lt;code&gt;schema.json&lt;/code&gt;. This is very useful as we will see later on.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. typescript
&lt;/h3&gt;

&lt;p&gt;This generates *.ts files with all queries, mutations and subscriptions exported as strings.&lt;br&gt;&lt;br&gt;
It also generates a file &lt;code&gt;API.ts&lt;/code&gt; that defines all the types.&lt;/p&gt;

&lt;p&gt;I don't like this option as much as the other generators we will explore later. Here is an example generated by the &lt;em&gt;typescript&lt;/em&gt; option:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CreateTodoMutation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;createdOnClientAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;It's correct to have the nested &lt;code&gt;createTodo&lt;/code&gt; property, but to type our Todo objects in code we need to do this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateTodoMutation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createTodo&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="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/*...*/&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;My code editor will not help with the string part and it might introduce subtle errors in complex schemas.&lt;/p&gt;
&lt;h2&gt;
  
  
  Apollo Code Generator
&lt;/h2&gt;

&lt;p&gt;Another possibility is to use the apollo client's code generator. I like its generated types. They are clean, short and the above code would look like this&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateTodo_createTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/*...*/&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can use it simply by executing&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx apollo-codegen generate src/graphql/*.graphql --schema src/graphql/schema.json --addTypename --target typescript --output libs/appsync/src/lib/api.types.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Installing it locally and running it gives me an error, because of a version mismatch of the dependency &lt;em&gt;graphql&lt;/em&gt; used in &lt;em&gt;aws-appsync&lt;/em&gt;. Using npx works great, though and I didn't worry about finding another solution for this. Using yarn might work better...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  GraphQL Code Generator
&lt;/h2&gt;

&lt;p&gt;One very important feature I am looking for in a code generator is the possibility to customize how the code is generated. &lt;a href="https://graphql-code-generator.com" rel="noopener noreferrer"&gt;{ GraphQL } code generator&lt;/a&gt; is such a tool. It uses plugins to generate code for lots of different languages.&lt;/p&gt;

&lt;p&gt;You could install it by running&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i graphql-code-generator graphql-codegen-typescript-common graphql-codegen-typescript-client
graphql-codegen-fragment-matcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create a codegen.yml file:&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;overwrite&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;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/graphql/schema.json&lt;/span&gt;
&lt;span class="na"&gt;documents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/graphql/*.graphql&lt;/span&gt;
&lt;span class="na"&gt;generates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;./libs/appsync/src/lib/graphql.ts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;plugins&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;typescript-common'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;typescript-client'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fragment-matcher'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and simply run&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run gql-gen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will generate a graphql.ts file in our lib providing all the types organized in namespaces. If we would add another plugin targeting &lt;em&gt;angular&lt;/em&gt; we would also get a service for each operation, that we could simply inject in our components.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note, we will NOT be using this tool in our app&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Choosing the code generator
&lt;/h2&gt;

&lt;p&gt;I think using the apollo code generator is probably the middle choice. It's easy enough to use when you add it the &lt;code&gt;package.json&lt;/code&gt;'s scripts section and hook it up to the amplify push command:&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;"generate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx apollo-codegen generate src/graphql/*.graphql --schema src/graphql/schema.json --addTypename --target typescript --output libs/appsync/src/lib/api.types.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"push"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amplify push &amp;amp;&amp;amp; npm run generate"&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;blockquote&gt;
&lt;p&gt;Make sure to use &lt;code&gt;angular&lt;/code&gt; (not &lt;code&gt;typescript&lt;/code&gt;) as the target in the previous step when configuring amplify's codegen, so that the &lt;code&gt;src/graphql/*.graphql&lt;/code&gt; files are generated.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;src/graphql&lt;/code&gt; folder is only used for code generation and should not be imported anywhere in our app!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since everything in &lt;code&gt;src/graphql&lt;/code&gt; is generated, we can also put it in &lt;code&gt;.gitignore&lt;/code&gt; together with &lt;code&gt;.graphqlconfig.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/graphql
.graphqlconfig.yml;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Writing our GraphQL operations
&lt;/h2&gt;

&lt;p&gt;Our app needs to&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch our list of todos&lt;/li&gt;
&lt;li&gt;Create a new todo&lt;/li&gt;
&lt;li&gt;Update the completed status of a todo&lt;/li&gt;
&lt;li&gt;Delete a todo&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To make our operations reusable in different components or services, we put them in a file &lt;code&gt;libs/appsync/src/lib/gql-operations.ts&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;gql&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;graphql-tag&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;CREATE_TODO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  mutation CreateTodo($input: CreateTodoInput!) {
    createTodo(input: $input) {
      __typename
      id
      name
      completed
      createdOnClientAt
    }
  }
`&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;LIST_TODOS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  query ListTodos($filter: ModelTodoFilterInput, $nextToken: String) {
    listTodos(filter: $filter, limit: 999, nextToken: $nextToken) {
      __typename
      items {
        __typename
        id
        name
        completed
        createdOnClientAt
      }
    }
  }
`&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;UPDATE_TODO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  mutation UpdateTodo($input: UpdateTodoInput!) {
    updateTodo(input: $input) {
      __typename
      id
      name
      completed
      createdOnClientAt
    }
  }
`&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;DELETE_TODO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  mutation DeleteTodo($input: DeleteTodoInput!) {
    deleteTodo(input: $input) {
      __typename
      id
    }
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;To not worry about fetching more todos and simplifying apollo's caching, we hard code the limit in listTodos to 999&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don't forget, that we have auto-generated code in &lt;code&gt;src/graphql/API.service.ts&lt;/code&gt; so we can simply copy some of those statement and use them as our starting point. In a more advanced app with a deeply nested schema the generated operations will need to be adjusted on a per component need.&lt;/p&gt;

&lt;p&gt;From a software architecture's point of view it would be better to create a new library called &lt;code&gt;todo-data&lt;/code&gt; or &lt;code&gt;todo-model&lt;/code&gt; and place the file there naming it &lt;code&gt;todo-operations.ts&lt;/code&gt;. Then we could also add a facade which will be the only thing we would expose to the other libs. This data layer lib could also hold our local state using redux for example. Going this route would make our components simpler. We might expand on this in the future.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup the AppSync Client
&lt;/h2&gt;

&lt;p&gt;Install the client, graphql-tag and optionally localforage&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i aws-appsync graphql-tag localforage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It's time to modify &lt;code&gt;libs/appsync/src/lib/appsync.module.ts&lt;/code&gt; to instantiate and configure the AppSync client.&lt;/p&gt;

&lt;p&gt;Change the content of the file to this:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&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;@angular/core&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;Amplify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AmplifyAngularModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AmplifyService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-amplify-angular&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;AWSAppSyncClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AUTH_TYPE&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-appsync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;localForage&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;localforage&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;config&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./aws-exports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&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;AppSyncClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AWSAppSyncClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_appsync_graphqlEndpoint&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="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_appsync_region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AUTH_TYPE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AMAZON_COGNITO_USER_POOLS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;jwtToken&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentSession&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;getIdToken&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getJwtToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;complexObjectsCredentials&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="nx"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentCredentials&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;cacheOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;addTypename&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;offlineConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localForage&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="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AmplifyAngularModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AmplifyService&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppsyncModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We are creating the AWSAppSyncClient and exporting the instance, so we can use it elsewhere.&lt;br&gt;
AmplifyService needs to be added to the providers array, since it does not use &lt;code&gt;@Injectable({providedIn: 'root'})&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To persist our the AppSync store in IndexedDB rather than LocalStorage, we use the library localforage.&lt;/p&gt;

&lt;p&gt;To hook it up, don't forget to add &lt;code&gt;AppsyncModule&lt;/code&gt; to the imports array of &lt;code&gt;apps/todo-app/src/app/app.module.ts&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: We are not using the &lt;a href="https://github.com/apollographql/apollo-angular" rel="noopener noreferrer"&gt;Apollo-Angular&lt;/a&gt; client here. This would give us an injectable service we could use throughout our app and which wraps the underlying zen-observables with rxjs. This is not necessary though, as we will see building this app in angular.&lt;/p&gt;

&lt;p&gt;AWSAppSyncClient uses quite a few libraries under the hood, which makes it difficult to exchange or add some of them. I haven't found a way to use &lt;a href="https://github.com/apollographql/apollo-link-state" rel="noopener noreferrer"&gt;apollo-link-state&lt;/a&gt; and &lt;a href="https://www.apollographql.com/docs/angular/basics/network-layer.html" rel="noopener noreferrer"&gt;apollo-angular-link-http&lt;/a&gt; together with AWSAppSyncClient while retaining all TypScript typings.&lt;br&gt;
This means NO Angular HttpClient and features like interceptors when working directly with the AWSAppSyncClient!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Offline helpers
&lt;/h2&gt;

&lt;p&gt;The AWS AppSync SDK offers &lt;a href="https://github.com/awslabs/aws-mobile-appsync-sdk-js/blob/master/OFFLINE_HELPERS.md" rel="noopener noreferrer"&gt;helpers&lt;/a&gt; to build mutation objects to use with apollo-client. This makes it quite easy to work with an optimistic UI in bad network conditions and update the local InMemoryCache when changes happen in our app.&lt;/p&gt;

&lt;p&gt;There are higher level helpers available for react, but when using other libraries, we need to use the lower level function &lt;code&gt;buildMutation&lt;/code&gt;. This function needs an apollo-client instance for example, which we can abstract away in our own typescript functions. Since we have generated types for our API we can also use these to give us additional type safety when providing &lt;code&gt;__typename&lt;/code&gt; strings.&lt;/p&gt;

&lt;p&gt;I also find it easier to use a config object instead of using a long parameter list. Here are my TypeScript Offline Mutation Builders which you can put in a file &lt;code&gt;libs/appsync/src/lib/offline.helpers.ts&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OperationVariables&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;apollo-client&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;buildMutation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;CacheOperationTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;CacheUpdatesOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;VariablesInfo&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-appsync&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;DocumentNode&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;graphql&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;AppSyncClient&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;./appsync.module&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TypeNameMutationType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;GraphqlMutationInput&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;TypeNameMutationType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/** DocumentNode for the mutation */&lt;/span&gt;
  &lt;span class="na"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DocumentNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/** An object with the mutation variables */&lt;/span&gt;
  &lt;span class="nl"&gt;variablesInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;VariablesInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/** The queries to update in the cache */&lt;/span&gt;
  &lt;span class="nl"&gt;cacheUpdateQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheUpdatesOptions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/** __typename from your schema */&lt;/span&gt;
  &lt;span class="nl"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__typename&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="cm"&gt;/** The name of the field with the ID (optional) */&lt;/span&gt;
  &lt;span class="nl"&gt;idField&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/** Override for the operation type (optional) */&lt;/span&gt;
  &lt;span class="nl"&gt;operationType&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;CacheOperationTypes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Builds a MutationOptions object ready to be used by the ApolloClient to automatically update the cache according to the cacheUpdateQuery
 * parameter
 */&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;graphqlMutation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OperationVariables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;R&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;TypeNameMutationType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TypeNameMutationType&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;mutation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;variablesInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;cacheUpdateQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;idField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;operationType&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;GraphqlMutationInput&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;R&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buildMutation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;AppSyncClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;variablesInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;cacheUpdateQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;idField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;operationType&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;executeMutation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OperationVariables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;R&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;TypeNameMutationType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TypeNameMutationType&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;mutationInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GraphqlMutationInput&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;R&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;AppSyncClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graphqlMutation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;R&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;mutationInput&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;blockquote&gt;
&lt;p&gt;Note, this might break with future versions of the appsync sdk&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Export everything from our lib
&lt;/h2&gt;

&lt;p&gt;To make it possible to import our types, graphql operations, helpers, appsync-client and amplify from our barrel we need to export them in the &lt;code&gt;libs/appsync/src/index.ts&lt;/code&gt; 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="k"&gt;export&lt;/span&gt; &lt;span class="o"&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;./lib/appsync.module&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="o"&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;./lib/api.types&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="o"&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;./lib/gql-operations&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="o"&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;./lib/offline.helpers&lt;/span&gt;&lt;span class="dl"&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 import them in our components we can simply write&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&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;@my/appsync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;So far, we have setup our Angular multi-app workspace with NX, initialized AWS Amplify to give us a managed authentication service using AWS Cognito and generated a serverless GraphQL API using AWS AppSync. Just with a few CLI commands!&lt;/p&gt;

&lt;p&gt;To make it possible to reuse our backend in more than one app, we imported and configured everything in a separate custom library.&lt;/p&gt;

&lt;p&gt;Since GraphQL works with a schema we could even generate all typings automatically and copy query and mutation operations as a starting point for our own operational needs.&lt;/p&gt;

&lt;p&gt;We also wrote some utility functions to help with the Apollo client-side cache in offline scenarios.&lt;/p&gt;
&lt;h2&gt;
  
  
  What to expect in part 2
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/beavearony/creating-angular-ui-components-and-connecting-them-to-the-graphql-appsync-api-part-2-33n0"&gt;part 2&lt;/a&gt; we will create a UI and use the stuff we created here to connect to our backend.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/beavearony" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F147932%2F49278dd3-133a-4030-9697-801b747d5371.jpg" alt="beavearony"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/beavearony/creating-angular-ui-components-and-connecting-them-to-the-graphql-appsync-api-part-2-33n0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Creating Angular UI components and connecting them to the GraphQL AppSync API - Part 2&lt;/h2&gt;
      &lt;h3&gt;Michael Gustmann ・ Mar 27 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#angular&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#graphql&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#awsamplify&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#awsappsync&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



</description>
      <category>angular</category>
      <category>graphql</category>
      <category>awsamplify</category>
      <category>awsappsync</category>
    </item>
  </channel>
</rss>
