<?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: Evertson Croes</title>
    <description>The latest articles on DEV Community by Evertson Croes (@evertsoncroes).</description>
    <link>https://dev.to/evertsoncroes</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%2F281946%2F22d4cb25-1cd0-4c70-b7b3-868b948e6072.jpg</url>
      <title>DEV Community: Evertson Croes</title>
      <link>https://dev.to/evertsoncroes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/evertsoncroes"/>
    <language>en</language>
    <item>
      <title>Processing long running events on AWS API Gateway</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Fri, 17 Apr 2026 08:19:51 +0000</pubDate>
      <link>https://dev.to/aws-builders/processing-long-running-events-on-aws-api-gateway-bn4</link>
      <guid>https://dev.to/aws-builders/processing-long-running-events-on-aws-api-gateway-bn4</guid>
      <description>&lt;h1&gt;
  
  
  Processing long running events on AWS API Gateway
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;AWS API Gateway&lt;/a&gt; is a managed HTTP/REST service provided by AWS. It provides a relatively simple way to host an API and offers rich functionality when it comes to customizability, security and integration. AWS API Gateway enforces a maximum integration timeout of 29 seconds. For most APIs this is perfectly reasonable.&lt;/p&gt;

&lt;p&gt;However, problems arise when an API must trigger operations that take minutes to complete, such as generating large exports or running complex background jobs. In our case, we needed to generate large database exports that could take several minutes. A synchronous API request was therefore not an option. One example is the generation of large exports from a database. This became a challenge with the default setup we had with API Gateway.&lt;/p&gt;

&lt;p&gt;AWS provides some &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/process-events-asynchronously-with-amazon-api-gateway-and-aws-lambda.html" rel="noopener noreferrer"&gt;guidance&lt;/a&gt; on this in their documentation. However, in this blog article I want to share how we solved this problem in our project in more detail and also provide a working CDK project as an example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;As mentioned in the intro, we have a time &lt;strong&gt;limit of 29 seconds&lt;/strong&gt; on requests on API Gateway. But the problem is also that we want to run functionality that could potentially run for many minutes. In this case, we do not want to run a &lt;strong&gt;synchronous&lt;/strong&gt; call, but an &lt;strong&gt;asynchronous&lt;/strong&gt; call. Meaning that even if API Gateway would allow us to keep a request open for 30 minutes, it would not be beneficial for a frontend application to keep one blocking request open for that long since it takes up resources.&lt;/p&gt;

&lt;p&gt;So we also need a mechanism to handle asynchronous requests while still using API Gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The TaskManager
&lt;/h2&gt;

&lt;p&gt;We ended up calling the solution to this problem the “TaskManager”. It can be seen as one Microservice with the sole responsibility of keeping track of tasks. It does not actually process the tasks. The following diagram provides a high-level overview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyduvfgc98uiyz7yahd52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyduvfgc98uiyz7yahd52.png" alt=" " width="639" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this overview, there are Task Suppliers, Task Processors and the TaskManager itself. It is important to note that in our use cases, we have not yet found a scenario where there are multiple suppliers of the same task and multiple processors for the same type of task. However, the pattern introduced in this blog could be expanded to include this if necessary.&lt;/p&gt;

&lt;p&gt;If we zoom in to the TaskManager, we have the following components:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8668okbvcxcu6wk7qost.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8668okbvcxcu6wk7qost.png" alt=" " width="800" height="330"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://evertson-public.s3.eu-central-1.amazonaws.com/task_manager_diagram.png" rel="noopener noreferrer"&gt;here&lt;/a&gt; to see diagram better)&lt;/p&gt;

&lt;p&gt;This diagram depicts the deployment and the flow at the same time. On the left side, we can see a Task Supplier. For this example, it does not matter what this is. It could be any component that can do a HTTP request. In our example, it was a Backend-for-Frontend API Gateway.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd87rwcuqfyoj5f4lo1ev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd87rwcuqfyoj5f4lo1ev.png" alt=" " width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This Task Supplier calls one of the two endpoints available in the TaskManager API, which itself is an API Gateway, that allows the creation of a Task via a HTTP POST request. This triggers a Lambda function that will create the task in the TaskStatus DynamoDB Table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8e0jvvocgb3d4fb5kgp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8e0jvvocgb3d4fb5kgp4.png" alt=" " width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When an entry is created in this Table, a DynamoDB Stream will trigger the TaskStatusPublisher Lambda. This Lambda will check if this is a new entry (indicated by the INSERT) and in this case will publish a “TaskCreatedEvent”. It is important to note that this event also contains the Task Type. This is important as the type determines which processor needs to process it.&lt;/p&gt;

&lt;p&gt;This is essentially where the first flow of the TaskManager ends. It is the responsibility of the Task Processor to create an EventBridge rule to consume this event and process the event.&lt;/p&gt;

&lt;p&gt;The TaskManager expects to be regularly updated by the TaskProcessor via the TaskUpdatedEvent. The status of the event can be updated to RUNNING, SUCCESSFUL and FAILED. In the case of SUCCESSFUL, a payload can also be added. This could be the result of the task or in our case for the large exports, a signed S3 bucket URL to download it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbi08kelo9m8lon5aglo4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbi08kelo9m8lon5aglo4.png" alt=" " width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Task Supplier can poll for the task regularly and get the status. Based on this status it can decide how to react.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd40mqyg8tpgqjohugcop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd40mqyg8tpgqjohugcop.png" alt=" " width="800" height="122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the TaskStatusPublisher also publishes TaskRunningEvent, TaskSuccessfulEvent and TaskFailedEvent. This could be used in combination with a WebSocket mechanism to receive live updates instead of polling. However, this is out-of-scope for this blog.&lt;/p&gt;

&lt;p&gt;This setup benefits from being completely serverless, meaning we can scale up on higher loads but also scale down to zero if there are no tasks. For this reason, in our case, we have only created one instance of this TaskManager that is shared by all tasks in our system. However, you could create multiple TaskManagers for different bounded contexts or even for each type of task.&lt;/p&gt;

&lt;h2&gt;
  
  
  CDK Project
&lt;/h2&gt;

&lt;p&gt;The example CDK project of this setup can be found in this GitHub &lt;a href="https://github.com/evertson90/aws-task-manager" rel="noopener noreferrer"&gt;repository&lt;/a&gt;. There is a README file which explains how to build and deploy the project to your own AWS environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This pattern is a simple but powerful way to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Work around API Gateway limitations&lt;/li&gt;
&lt;li&gt;Build scalable async workflows&lt;/li&gt;
&lt;li&gt;Keep your frontend responsive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're dealing with long-running operations in AWS, this approach is definitely worth considering.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>apigateway</category>
      <category>architecture</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>The Amplify Series, Part 7: Track app usage with Amplify Analytics</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Sun, 18 Jun 2023 17:25:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-amplify-series-part-7-track-app-usage-with-amplify-analytics-2kli</link>
      <guid>https://dev.to/aws-builders/the-amplify-series-part-7-track-app-usage-with-amplify-analytics-2kli</guid>
      <description>&lt;p&gt;&lt;strong&gt;In the &lt;a href="https://www.luminis.eu/blog/the-amplify-series-part-6-using-the-power-of-ai-and-machine-learning-with-amplify-predictions/"&gt;previous part of this series&lt;/a&gt;, we added the &lt;code&gt;Amplify Predictions&lt;/code&gt; category, which allowed us to use the power of AI and Machine learning to identify text in images, convert text to speech and interpret sentiment of the text. In this part of the blog series, we will add functionality to be able to track the usage of our application in order to learn how our users use the application and be able to improve the application user experience based on this information. We will do this by adding the Amplify category called &lt;code&gt;Amplify Analytics&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will start by adding this category to the project. We will create a new page for the &lt;code&gt;Amplify Analytics&lt;/code&gt; category and add some interactable elements to that page so that we can track the usage of these elements. We will also be updating the previous pages when needed in order to track the usage of those pages. In the end, you will have a better understanding of this category and be able to track the usage of your Amplify application.&lt;/p&gt;

&lt;p&gt;We will continue in the &lt;a href="https://gitlab.com/evertson90/theamplifyapp"&gt;repository&lt;/a&gt; where we left off in the &lt;a href="https://www.luminis.eu/blog/the-amplify-series-part-6-using-the-power-of-ai-and-machine-learning-with-amplify-predictions/"&gt;previous blog post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Analytics category
&lt;/h2&gt;

&lt;p&gt;We will start by generating the backend resources needed for the tracking functionality. &lt;/p&gt;

&lt;p&gt;We will run &lt;strong&gt;amplify add analytics&lt;/strong&gt; with the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analytics provider: &lt;strong&gt;Amazon Pinpoint&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Resource name: &lt;strong&gt;&amp;lt;&amp;gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Unauthenticated users: &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Select an Analytics provider Amazon Pinpoint
✔ Provide your pinpoint resource name: · theamplifyapp
⚠️ Auth configuration is required to allow unauthenticated users, but it is not configured properly.
⚠️ Adding analytics would add the Auth category to the project if not already added.
? Apps need authorization to send analytics events. Do you want to allow guests and unauthenticated users to send analytics events? (we recommend you a
llow this when getting started) No
⚠️ Authorize only authenticated users to send analytics events. Use "amplify update auth" to modify this behavior.
✅ Successfully updated auth resource locally.
✅ Successfully added resource theamplifyapp locally
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will add a new directory called &lt;code&gt;analytics&lt;/code&gt; to our &lt;code&gt;amplify/backend&lt;/code&gt; directory, which will contain information needed to create the resources in AWS to support the new functionality.&lt;/p&gt;

&lt;p&gt;We will run &lt;strong&gt;amplify push&lt;/strong&gt; to create these resources in AWS. The AWS service used here is &lt;a href="https://aws.amazon.com/pinpoint/"&gt;Amazon Pinpoint&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Note that using this service will cost you money. The first 100 million events recorded per month are free. After that, you pay &lt;a href="https://aws.amazon.com/pinpoint/pricing/"&gt;$0.000001 per event you collect&lt;/a&gt;. A piece of general advice:  configure a budget with &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/monitor_estimated_charges_with_cloudwatch.html"&gt;billing alerts&lt;/a&gt; for your AWS account so you don’t get surprised by high costs. &lt;/p&gt;

&lt;p&gt;After running the &lt;strong&gt;amplify push&lt;/strong&gt; command you will not be charged since the application must be configured to send events to AWS Pinpoint. Once we configure this, however, the application will automatically start sending a minimal set of events such as page visits. This can be turned off and will be covered in the next section.&lt;/p&gt;

&lt;p&gt;Once we have created the resources, we can run &lt;strong&gt;amplify console analytics&lt;/strong&gt; to open the Amazon Pinpoint console. Initially, all charts should be empty:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V27tvoJh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c1g6553mbzitddpdkxw6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V27tvoJh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c1g6553mbzitddpdkxw6.png" alt="Analytics Overview" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next step, we will configure the frontend application to start sending data to Amazon Pinpoint to fill these graphs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the frontend for Analytics
&lt;/h2&gt;

&lt;p&gt;To start, we will update our &lt;code&gt;main.ts&lt;/code&gt; to include the following:&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;Predictions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Analytics&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By simply importing this dependency,running the application again and visiting it in the browser, we can see that the charts are being updated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PA1624mB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hjeddjr3jxl0ftzwzuwg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PA1624mB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hjeddjr3jxl0ftzwzuwg.png" alt="First events" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also click on the &lt;strong&gt;demographics&lt;/strong&gt; to see information about the users:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RR6mpS2Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p4ow04o5rxopig0fq3sh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RR6mpS2Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p4ow04o5rxopig0fq3sh.png" alt="Demographics" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this small addition to the frontend application, we can now see how much the application is being used and from what type of devices. If you want to have more information about the users of the application, it is possible to customize the information sent about a user. We will not cover that part in this blog, however, a complete example can be found on this &lt;a href="https://docs.amplify.aws/lib/analytics/update-endpoint/q/platform/js/"&gt;page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session tracking
&lt;/h2&gt;

&lt;p&gt;The session metric in AWS Pinpoint provides information about how often your application has been opened. However, once a session is registered for a device (mobile, browser, etc.) the session counter does not go up if the same device visits the site again within a certain time period. Refreshing the page or closing the browser and opening it again is not a new session. This is because the Amplify SDK creates a unique identifier for your device.&lt;/p&gt;

&lt;p&gt;After the page has not been active on the device for some time or if your application manually calls &lt;code&gt;Analytics.stopSession&lt;/code&gt; the session will terminate. Opening the page again after this will register as a new session.&lt;/p&gt;

&lt;p&gt;The session is tracked by default, however, you can turn it off by adding the following code in the &lt;code&gt;main.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="nx"&gt;Analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;autoTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session&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;enable&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Page view tracking
&lt;/h2&gt;

&lt;p&gt;We can also configure the application to track which pages are visited. We will update the &lt;code&gt;main.ts&lt;/code&gt; with the following:&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="nx"&gt;Analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;autoTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pageView&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;enable&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SPA&lt;/span&gt;&lt;span class="dl"&gt;'&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;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&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;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&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;Now you can run your application and visit some of the pages. If you visit the events page in AWS Pinpoint you can see how much each page is viewed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lbqp0SuD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3shkrp0832k9ktgcvzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lbqp0SuD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3shkrp0832k9ktgcvzb.png" alt="Page view events" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; It can take up to 30 minutes for the events to show up in Pinpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording events
&lt;/h2&gt;

&lt;p&gt;We can record events such as clicks on the pages with Amplify Analytics. This section describes two ways to do that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual event recording
&lt;/h3&gt;

&lt;p&gt;The first way is to do it manually. We will update our &lt;code&gt;text-to-speech.component.ts&lt;/code&gt; to send an event to AWS Pinpoint every time the user uses the text-to-speech functionality. The event will contain the text that the user entered:&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;Predictions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Analytics&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="c1"&gt;// &amp;lt;--- Add import&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;app-text-to-speech&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;./text-to-speech.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;./text-to-speech.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;TextToSpeechComponent&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="c1"&gt;// Other existing code&lt;/span&gt;

  &lt;span class="nx"&gt;convertToAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&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="c1"&gt;// Add this &lt;/span&gt;
    &lt;span class="nx"&gt;Analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&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;convertedTextToSpeech&lt;/span&gt;&lt;span class="dl"&gt;'&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;textInput&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;textInput&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;immediate&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="c1"&gt;// Other existing code&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Other existing code&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 now show up in our events every time a user uses this functionality:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HHQl9Uv8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhmbb9yuueexljy4krjn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HHQl9Uv8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhmbb9yuueexljy4krjn.png" alt="New events" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a real application, you might not want to set an attribute value as we did here for an input field. Since the users of the application are free to enter any value they want, the attribute value list in the filters will explode. However, this example gives an impression of what is possible with the minimal setup we have so far.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic event recording
&lt;/h3&gt;

&lt;p&gt;The other way to record events is to set up an event tracker that will activate when an element contains certain properties. We will update the &lt;code&gt;main.ts&lt;/code&gt; to include the following:&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="nx"&gt;Analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;autoTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event&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;enable&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;events&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;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;selectorPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-amplify-analytics-&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;This will track all click events for HTML elements that have properties with the prefix &lt;strong&gt;data-amplify-analytics-&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will now update our &lt;code&gt;comments.component.html&lt;/code&gt; to make use of this so that an event is sent to AWS Pinpoint every time someone adds a comment to the post.&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;!-- Other existing code --&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;"col-lg-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;"aws-button"&lt;/span&gt;
        &lt;span class="na"&gt;data-amplify-analytics-on=&lt;/span&gt;&lt;span class="s"&gt;"click"&lt;/span&gt;
        &lt;span class="na"&gt;data-amplify-analytics-name=&lt;/span&gt;&lt;span class="s"&gt;"commentAdded"&lt;/span&gt;
        &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"addComment()"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Add Comment
      &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="c"&gt;&amp;lt;!-- Other existing code --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This event will also show up similar to the previous one in our events dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R2WAZS4x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rhv2v9lp0yxxt6ojbnh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R2WAZS4x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rhv2v9lp0yxxt6ojbnh.png" alt="Comment added event" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have enough tools to be able to track the usage in our application.&lt;/p&gt;

&lt;p&gt;The following code changes have been made to our repository in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/f973e8d25faab653bede5591ae3f1422ad086510"&gt;Adding the backend resource&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/a04593542bae7685e8f6a6eaf948910fa0ba7c0a"&gt;Updating the frontend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Amplify Analytics vs Google Analytics
&lt;/h2&gt;

&lt;p&gt;When deciding to track app usage, you can also use &lt;a href="https://analytics.google.com/analytics/web/provision/#/provision"&gt;Google Analytics&lt;/a&gt;, which is unarguably more feature-rich and streamlined than Amplify Analytics. &lt;/p&gt;

&lt;p&gt;Amplify Analytics uses AWS Pinpoint, which has analytics capabilities but is more focused on user interaction, such as sending emails and notifications for campaigns. &lt;/p&gt;

&lt;p&gt;Google Analytics is the industry standard when it comes to Analytics as it provides many features and is very user-friendly compared to AWS Pinpoint in my opinion. So how do you choose between the two?&lt;/p&gt;

&lt;p&gt;My advice would be to choose AWS Pinpoint if you want to get a quick view of the usage of your application. If you already have an Amplify application, you can get up and running with analytics with just a few changes to your application as we have seen in this blog post. &lt;/p&gt;

&lt;p&gt;If you want to go further than seeing the general usage, event usage, and demographics, then you should consider using Google Analytics, especially if you or someone on your team already has experience with it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Up next: Location functionality with Amplify Geo
&lt;/h2&gt;

&lt;p&gt;In this blog we have used &lt;code&gt;Amplify Analytics&lt;/code&gt; to track the app usage of our application. As with the previous categories, there is more that you can do with this category than what is covered in this article, so be sure to check the documentation for more information. In the next article, we will take a look at how to use &lt;code&gt;Amplify Geo&lt;/code&gt; to add location-based functionality.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>amplify</category>
      <category>angular</category>
    </item>
    <item>
      <title>The Amplify Series, Part 6: Using the power of AI and Machine Learning with Amplify Predictions</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Thu, 23 Mar 2023 14:38:29 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-amplify-series-part-6-using-the-power-of-ai-and-machine-learning-with-amplify-predictions-3iel</link>
      <guid>https://dev.to/aws-builders/the-amplify-series-part-6-using-the-power-of-ai-and-machine-learning-with-amplify-predictions-3iel</guid>
      <description>&lt;p&gt;&lt;strong&gt;In the last &lt;a href="https://www.luminis.eu/blog/cloud-en/the-amplify-series-part-5-uploading-and-retrieving-images-with-amplify-storage/" rel="noopener noreferrer"&gt;part of this series&lt;/a&gt;, we added the &lt;code&gt;Amplify Storage&lt;/code&gt; category, which allowed us to retrieve and upload files to S3 directly from our application. In this part of the blog series, we will add even more functionality, such as identifying text from an image, converting text to speech, and interpreting the sentiment of text. We will do this by adding a new Amplify category called &lt;code&gt;Amplify Predictions&lt;/code&gt; which will allow us to use the power of AI and machine learning to access powerful functionality out of the box.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As usual, we will start by adding the category to the project. We will then create a new page for the &lt;code&gt;Amplify Predictions&lt;/code&gt; category and add sections to that page per functionality available. This page will serve as a showcase for all the possibilities within &lt;code&gt;Amplify Predictions&lt;/code&gt;. In the end, you will have a better understanding of this category and be able to appreciate how easy it is to get this functionality working in your applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amplify app repository update
&lt;/h2&gt;

&lt;p&gt;We will continue in the &lt;a href="https://gitlab.com/evertson90/theamplifyapp" rel="noopener noreferrer"&gt;repository&lt;/a&gt; where we left off in the &lt;a href="https://www.luminis.eu/blog/cloud-en/the-amplify-series-part-5-uploading-and-retrieving-images-with-amplify-storage/" rel="noopener noreferrer"&gt;last blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There have been 2 extra commits to this repository since the last blog post. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/259cbb56d924519575cb95c11c348c6915b5d3e6" rel="noopener noreferrer"&gt;Changing the href to routerLink in the header so that the application behaves as a Single Page Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/87db85a1acf0bf057fc86070cf3271d6990540c8" rel="noopener noreferrer"&gt;Upgrading Amplify CLI to the newest version (10.8.1)  as of the point of writing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we are ready to get started and add more functionality to our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identify text from uploaded image
&lt;/h2&gt;

&lt;p&gt;In this section, we will add functionality that will allow us to upload an image and retrieve the identified text inside the image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating the backend resources
&lt;/h3&gt;

&lt;p&gt;We will run &lt;strong&gt;amplify add predictions&lt;/strong&gt; with the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Category: &lt;strong&gt;identify&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;What would you like to identify: &lt;strong&gt;Identify Text&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Friendly name: &lt;strong&gt;&amp;lt;&amp;gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Identify documents: &lt;strong&gt;yes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Access: &lt;strong&gt;Auth users only&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Amplify CLI output will look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MacBook-Pro:theamplifyapp evertsoncroes$ amplify add predictions
✔ Please select from one of the categories below · Identify
✔ What would you like to identify? · Identify Text
✔ Provide a friendly name for your resource · identifyTextd230b04a
✔ Would you also like to identify documents? (y/N) · yes
✔ Who should have access? · Auth users only
Successfully added resource identifyTextd230b04a locally
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with the previous category, we want only to allow access to authenticated users. This command will add a new directory called &lt;code&gt;predictions&lt;/code&gt; to our &lt;code&gt;amplify/backend&lt;/code&gt; directory, which will contain information needed to create the resources in AWS to support the new functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug&lt;/strong&gt;: Before continuing, we need to add some manual changes to the generated output since there is a &lt;a href="https://github.com/aws-amplify/amplify-cli/issues/12159" rel="noopener noreferrer"&gt;bug&lt;/a&gt; in the 10.8.1 version of the Amplify CLI. To fix the issue, open the &lt;code&gt;amplify/backend/predictions/identifyText&amp;lt;&amp;lt;id&amp;gt;&amp;gt;/parameters.json&lt;/code&gt; file and add the following three key-value pairs to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"format": "PLAIN"&lt;/li&gt;
&lt;li&gt;"access": "auth"&lt;/li&gt;
&lt;li&gt;"identifyDoc": "document"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will run &lt;strong&gt;amplify push&lt;/strong&gt; to create these resources in AWS. The AWS services used here are &lt;a href="https://aws.amazon.com/rekognition/" rel="noopener noreferrer"&gt;Amazon Rekognition&lt;/a&gt; for image recognition and &lt;a href="https://aws.amazon.com/textract/" rel="noopener noreferrer"&gt;Amazon Textract&lt;/a&gt; for document analysis. &lt;/p&gt;

&lt;p&gt;Note that using these services will cost money. Amazon Rekognition will cost around &lt;a href="https://aws.amazon.com/rekognition/pricing/" rel="noopener noreferrer"&gt;$0.001 per image processed&lt;/a&gt;, while Amazon Textract costs around &lt;a href="https://aws.amazon.com/textract/pricing/" rel="noopener noreferrer"&gt;$0.0015 per document processed&lt;/a&gt;. As a general rule, be sure to set up a budget with &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/monitor_estimated_charges_with_cloudwatch.html" rel="noopener noreferrer"&gt;billing alerts&lt;/a&gt; for your AWS account so you don’t get surprised by large costs. &lt;/p&gt;

&lt;p&gt;These commands will add the following &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/657293e96986f955dfd57dc9811a8844b6be7767" rel="noopener noreferrer"&gt;changes&lt;/a&gt; to our repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the predictions page
&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is add a new component to our frontend application that will contain all the predictions functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component components/categories/predictions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate the expected files for our component. We will then link up routing to be able to render this component. Refer to this &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/f179ceb01792091fec0cfeabdd9bf416f232183c" rel="noopener noreferrer"&gt;commit&lt;/a&gt; for the details so far. &lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Text identification functionality to the Predictions page
&lt;/h3&gt;

&lt;p&gt;We will add a new frontend component that will contain the functionality to upload an image and identify the text in that image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component components/categories/predictions/identify-text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside our &lt;code&gt;predictions.component.html&lt;/code&gt; we must make sure to add the newly generated identify-text 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;app-identify-text&amp;gt;&amp;lt;/app-identify-text&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;identify-text.component.html&lt;/code&gt;, we will add:&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;input&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;
  &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"imageUpload"&lt;/span&gt;
  &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"imageUpload"&lt;/span&gt;
  &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"image/png, image/jpeg"&lt;/span&gt;
  &lt;span class="na"&gt;(change)=&lt;/span&gt;&lt;span class="s"&gt;"imageSelected($event)"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to the last blog, this will give us an input that we can use to select images from our device. We have to add logic to react to the image that is selected in our &lt;code&gt;identify-text.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="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;app-identify-text&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;./identify-text.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;./identify-text.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;IdentifyTextComponent&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;selectedFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="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;imageSelected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&lt;/span&gt; &lt;span class="o"&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;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="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 will now be able to select a file and it will be stored in the selectedFile variable. Normally it would be best practice to add this logic to a separate component so that we can reuse this code. However, to keep the blog shorter we will allow duplicate code.&lt;/p&gt;

&lt;p&gt;Now that we have the functionality to upload an image, we need to add a button that does something with the selected image. We will also show the identified text and also add some CSS to &lt;code&gt;identify-text.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;"container-fluid card-background"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Identify Text&lt;span class="nt"&gt;&amp;lt;/h2&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;"file"&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"imageUpload"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"imageUpload"&lt;/span&gt;
    &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"image/png, image/jpeg"&lt;/span&gt;
    &lt;span class="na"&gt;(change)=&lt;/span&gt;&lt;span class="s"&gt;"imageSelected($event)"&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;"aws-button"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"identifyText()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Identify Text
  &lt;span class="nt"&gt;&amp;lt;/button&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;"identified-text"&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"identifiedText"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Identified words:
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let word of identifiedText.text.words"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {{ word.text }}
    &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;Once this button is clicked, the &lt;code&gt;identifyText&lt;/code&gt; function is called. We will define this function in the following way in our &lt;code&gt;identify-text.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;Predictions&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="c1"&gt;// &amp;lt;---NEW&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;IdentifyTextOutput&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/predictions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;---NEW&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;app-identify-text&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;./identify-text.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;./identify-text.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;IdentifyTextComponent&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;selectedFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;---NEW&lt;/span&gt;
  &lt;span class="nl"&gt;identifiedText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IdentifyTextOutput&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;---NEW constructor() {} ngOnInit(): void {} identifyText = async () =&amp;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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&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="c1"&gt;//ADD THIS FUNCTION&lt;/span&gt;
    &lt;span class="nx"&gt;Predictions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;file&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;selectedFile&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;identifiedText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;//OTHER CODE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In these changes we import some components we need related to &lt;code&gt;Amplify Predictions&lt;/code&gt;. We add two properties to our components, the &lt;code&gt;selectedFile&lt;/code&gt; which will hold the latest uploaded image and the &lt;code&gt;identifiedText&lt;/code&gt; which will hold the latest results of identified text we received from AWS. We will then call the identify function with the selected image to be sent to AWS. The response will be set to the identifiedText field and the words will show up on the screen.&lt;/p&gt;

&lt;p&gt;There is one more step we need to take. The Predictions component needs to be supplied with a Provider. This can be done in the &lt;code&gt;main.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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;other&lt;/span&gt; &lt;span class="nx"&gt;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;Predictions&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;AmazonAIPredictionsProvider&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/predictions&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;aws_exports&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;Predictions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPluggable&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;AmazonAIPredictionsProvider&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;other&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is all done, we can run our application, upload an image, identify the text and see the results on the screen. When I used this &lt;a href="https://images.saymedia-content.com/.image/ar_1:1%2Cc_fill%2Ccs_srgb%2Cfl_progressive%2Cq_auto:eco%2Cw_1200/MTc0NTAyNjMzMzI5OTkzNzE4/awesome-sounding-words.jpg" rel="noopener noreferrer"&gt;image&lt;/a&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2023%2F03%2Fawesome-sounding-words.jpeg" 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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2023%2F03%2Fawesome-sounding-words.jpeg" alt="awesome-words"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I got the following result:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2023%2F03%2FScreenshot-2023-03-03-at-11.09.36.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2023%2F03%2FScreenshot-2023-03-03-at-11.09.36.png" alt="Identify Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are more options to play around with, including identifying entities and labels in images and many ways to finetune the results. For more information on this, checkout the &lt;a href="https://docs.amplify.aws/lib/predictions/identify-text/q/platform/js/" rel="noopener noreferrer"&gt;Amplify Predictions documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The changes for this section can be found in this &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/c1d7cda0fe91559711717f6cb2387c25c090dd8a" rel="noopener noreferrer"&gt;commit&lt;/a&gt;, including the changes needed for the CSS.&lt;/p&gt;
&lt;h2&gt;
  
  
  Convert text to speech
&lt;/h2&gt;

&lt;p&gt;In this section we are going to add functionality to convert text to speech using &lt;code&gt;Amplify Predictions&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Generating the backend resources
&lt;/h3&gt;

&lt;p&gt;We will first generate the backend resources needed. We will run &lt;strong&gt;amplify add predictions&lt;/strong&gt; with the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Category: &lt;strong&gt;Convert&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;What to convert: &lt;strong&gt;Generate speech audio from text&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Friendly name: &lt;strong&gt;&amp;lt;&amp;gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Source language: &lt;strong&gt;US English&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Speaker: &lt;strong&gt;Kevin – Male&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Access: &lt;strong&gt;Auth users only&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Amplify CLI output will look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MacBook-Pro:theamplifyapp evertsoncroes$ amplify add predictions
✔ Please select from one of the categories below · Convert
✔ What would you like to convert? · Generate speech audio from text
✔ Provide a friendly name for your resource · speechGenerator11c4cfca
? What is the source language? ...  (Use arrow keys or type to filter)
✔ What is the source language? · US English
✔ Select a speaker · Kevin - Male
✔ Who should have access? · Auth users only
Successfully added resource speechGenerator11c4cfca locally
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is very similar to what we previously did for the text identification. We can run &lt;strong&gt;amplify push&lt;/strong&gt; again to create the resources in AWS. The AWS service that will be used for this functionality is &lt;a href="https://aws.amazon.com/polly/" rel="noopener noreferrer"&gt;Amazon Polly&lt;/a&gt;. The costs for using Amazon Polly is around &lt;a href="https://aws.amazon.com/polly/pricing/" rel="noopener noreferrer"&gt;$4.00 per 1 million characters&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;These commands will add the following &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/0ab21ab2ef4665527a8aa2eb09f712dfee2203ff" rel="noopener noreferrer"&gt;changes&lt;/a&gt; to our repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding text-to-speech functionality to the Predictions page
&lt;/h3&gt;

&lt;p&gt;Similar as we did for the text-identification, we will add a component that will handle all of the text-to-speech functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component components/categories/predictions/text-to-speech
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside our &lt;code&gt;predictions.component.html&lt;/code&gt; we will make sure to add the newly generated text-to-speech 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;app-text-to-speech&amp;gt;&amp;lt;/app-text-to-speech&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;text-to-speech.component.html&lt;/code&gt;, we will add a text input and a button to play the text:&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;"container-fluid card-background"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Text to speech&lt;span class="nt"&gt;&amp;lt;/h2&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;id=&lt;/span&gt;&lt;span class="s"&gt;"textInput"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"textInput"&lt;/span&gt;
    &lt;span class="na"&gt;(change)=&lt;/span&gt;&lt;span class="s"&gt;"textInputUpdated($event)"&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;"aws-button"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"convertToAudio()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Play
  &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to hook up these elements to our &lt;code&gt;text-to-speech.component.ts&lt;/code&gt; and call the Predictions component to do the conversion from text to an audio buffer for us. Finally, we play the audio:&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;Predictions&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;TextToSpeechOutput&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/predictions&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;app-text-to-speech&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;./text-to-speech.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;./text-to-speech.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;TextToSpeechComponent&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;textInput&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;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="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;convertToAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&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="nx"&gt;Predictions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;textToSpeech&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;text&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;textInput&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;voiceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Amy&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;result&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="nf"&gt;playAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;playAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TextToSpeechOutput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&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;AudioContext&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;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeAudioData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audioStream&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;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createBufferSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&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;destination&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;textInputUpdated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&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;textInput&lt;/span&gt; &lt;span class="o"&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bug&lt;/strong&gt;: There is currently, at the time of writing, a &lt;a href="https://github.com/aws-amplify/amplify-js/issues/11046" rel="noopener noreferrer"&gt;bug&lt;/a&gt; in Amplify that does not allow us to use the voiceId “Kevin”, which we selected when creating the backend resources. Selecting the voiceId “Amy” works, so we will use that.&lt;/p&gt;

&lt;p&gt;In the code snippet above we create a field that will hold the text in the textInput. We have a method that is called to convert the text to audio using the Predictions component. The convert function will send an http request to Amazon Polly and an audioBuffer will be returned. This can be given to an AudioContext component to play to audio in the browser.&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2023%2F03%2FScreenshot-2023-03-05-at-21.54.34.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2023%2F03%2FScreenshot-2023-03-05-at-21.54.34.png" alt="text to speech"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The changes made in these steps can be found in this &lt;a href="https://www.luminis.eu/wp-content/uploads/2023/03/Screenshot-2023-03-05-at-21.54.34.png" rel="noopener noreferrer"&gt;commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interpret the sentiment of text
&lt;/h2&gt;

&lt;p&gt;The final set of functionality we are going to add is the ability to interpret the sentiment of text. &lt;/p&gt;

&lt;h3&gt;
  
  
  Generating the backend resources
&lt;/h3&gt;

&lt;p&gt;We will add the backend resources by again running amplify &lt;strong&gt;add predictions&lt;/strong&gt; with the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Category: &lt;strong&gt;Interpret&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Friendly name: &lt;strong&gt;&amp;lt;&amp;gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Kind of interpretation: &lt;strong&gt;ALL&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Access: &lt;strong&gt;Auth users only&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Amplify CLI output will look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MacBook-Pro:theamplifyapp evertsoncroes$ amplify add predictions
✔ Please select from one of the categories below · Interpret
Only one option for [What would you like to interpret?]. Selecting [Interpret Text].
✔ Provide a friendly name for your resource · interpretText9001208a
✔ What kind of interpretation would you like? · All
✔ Who should have access? · Auth users only
Successfully added resource interpretText9001208a locally
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we can run &lt;strong&gt;amplify push&lt;/strong&gt; to create the resources in AWS. The AWS service that will be used for this functionality is &lt;a href="https://aws.amazon.com/comprehend/" rel="noopener noreferrer"&gt;Amazon Comprehend&lt;/a&gt;. The pricing for this service can be found &lt;a href="https://aws.amazon.com/comprehend/pricing/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These commands will add the following &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/e2c04a0bd1816665bc5e235a279a7d995abefe21" rel="noopener noreferrer"&gt;changes&lt;/a&gt; to our repository. &lt;/p&gt;

&lt;h3&gt;
  
  
  Adding text-interpret functionality to the Predictions page
&lt;/h3&gt;

&lt;p&gt;We will first create a component that will handle the text-interpret functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component components/categories/predictions/text-interpret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside our &lt;code&gt;predictions.component.html&lt;/code&gt; we will make sure to add the newly generated text-interpret 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;app-text-interpret&amp;gt;&amp;lt;/app-text-interpret&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;text-interpret.component.html&lt;/code&gt;, we will add a text area input and a button to trigger the interpretation of the text and a text to show what the sentiment is:&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;"container-fluid card-background"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Interpret text&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt;
        &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"textAreaInput"&lt;/span&gt;
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"textAreaInput"&lt;/span&gt;
        &lt;span class="na"&gt;rows=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;
        &lt;span class="na"&gt;cols=&lt;/span&gt;&lt;span class="s"&gt;"66"&lt;/span&gt;
        &lt;span class="na"&gt;(change)=&lt;/span&gt;&lt;span class="s"&gt;"textInputUpdated($event)"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&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;"aws-button interpret-button"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"interpretText()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Interpret
    &lt;span class="nt"&gt;&amp;lt;/button&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;"interpretation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Interpretation = {{ interpretation.textInterpretation.sentiment?.predominant }}
    &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;Now we will update the &lt;code&gt;text-interpret.component.ts&lt;/code&gt; to hook up the functions defined here and call the Predictions component to interpret the text:&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;Predictions&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;InterpretTextCategories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InterpretTextOutput&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/predictions&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;app-text-interpret&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;./text-interpret.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;./text-interpret.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;TextInterpretComponent&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;textInput&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;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;interpretation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InterpretTextOutput&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="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="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;interpretText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textInput&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="nx"&gt;Predictions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;interpret&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;text&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;textInput&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;InterpretTextCategories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALL&lt;/span&gt;
      &lt;span class="p"&gt;}&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;result&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;interpretation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


  &lt;span class="nx"&gt;textInputUpdated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&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;textInput&lt;/span&gt; &lt;span class="o"&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now try our entering text in our text area and doing a sentiment check. I Googled “&lt;a href="https://interestingliterature.com/2020/02/inspirational-poems-motivational/" rel="noopener noreferrer"&gt;happy poems&lt;/a&gt;”  and entered the first one I found:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2023%2F03%2FScreenshot-2023-03-10-at-15.52.57.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2023%2F03%2FScreenshot-2023-03-10-at-15.52.57.png" alt="poem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try adding different types of text to check the interpretation. Furthermore, the response to the &lt;strong&gt;interpret&lt;/strong&gt; function also contains more information related to the interpretation of the text. Check the &lt;a href="https://docs.amplify.aws/lib/predictions/interpret/q/platform/js/#setup-the-backend" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more information and possibilities. &lt;/p&gt;

&lt;p&gt;The changes made in these steps can be found in this &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/24fd80d7c8d79de530de8e94884ebf3d5e83f476" rel="noopener noreferrer"&gt;commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up next: Tracking app usage with Amplify Analytics
&lt;/h2&gt;

&lt;p&gt;In this blog we have used Amplify Predictions to identify text in images, convert text to speech and interpret the sentiment of text. There are more possibilities in this category, however, these examples should give you an idea. In the next article, we will look at using Amplify Analytics to collect analytics data for your application.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>amplify</category>
      <category>angular</category>
    </item>
    <item>
      <title>The Amplify Series, Part 5: Uploading and retrieving images with Amplify Storage</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Sun, 19 Feb 2023 19:38:17 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-amplify-series-part-5-uploading-and-retrieving-images-with-amplify-storage-2hja</link>
      <guid>https://dev.to/aws-builders/the-amplify-series-part-5-uploading-and-retrieving-images-with-amplify-storage-2hja</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.luminis.eu/blog/the-amplify-series-part-4-developing-and-deploying-a-cloud-native-application-with-aws-amplify/" rel="noopener noreferrer"&gt;With our application in place&lt;/a&gt;, it is now time to start adding more functionality using some of the other Amplify categories. In this article, we will be adding &lt;code&gt;Amplify Storage&lt;/code&gt; to upload and retrieve images from AWS in just a few steps.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will start off by adding the &lt;code&gt;Amplify Storage&lt;/code&gt; category to our project. We will follow up by using the Storage component to be able to upload and list all of our images. Finally, we will create some UI that will make use of this functionality. At the end of this article, you will have a better understanding of &lt;code&gt;Amplify Storage&lt;/code&gt; and can use it in any scenario regarding file uploads and retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Amplify Storage to our project
&lt;/h2&gt;

&lt;p&gt;We will continue in the &lt;a href="https://gitlab.com/evertson90/theamplifyapp" rel="noopener noreferrer"&gt;repository&lt;/a&gt; where we left off in the &lt;a href="https://www.luminis.eu/blog/the-amplify-series-part-4-developing-and-deploying-a-cloud-native-application-with-aws-amplify/" rel="noopener noreferrer"&gt;last blog post&lt;/a&gt;. From this point, we will run &lt;strong&gt;amplify add storage&lt;/strong&gt; with the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service: &lt;strong&gt;content&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Friendly name: &lt;strong&gt;amplifyappimages&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Bucket name: &lt;strong&gt;amplifyappimages&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Access: &lt;strong&gt;create, update, read,&lt;/strong&gt; and &lt;strong&gt;delete&lt;/strong&gt;, &lt;strong&gt;for authorized users only&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Amplify CLI output will look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MBP:theamplifyapp evertsoncroes$ amplify add storage

? Select from one of the below mentioned services: Content (Images, audio, video, etc.)

✔ Provide a friendly name for your resource that will be used to label this category in the project: · amplifyappimages

✔ Provide bucket name: · amplifyappimages

✔ Who should have access: · Auth users only

✔ What kind of access do you want for Authenticated users? · create/update, read, delete

✔ Do you want to add a Lambda Trigger for your S3 Bucket? (y/N) · no

✅ Successfully added resource amplifyappimages locally

⚠️ If a user is part of a user pool group, run "amplify update storage" to enable IAM group policies for CRUD operations
✅ Some next steps:
"amplify push" builds all of your local backend resources and provisions them in the cloud

"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want users to be able to only see their own uploads. Therefore it is important that we select that only authenticated users can make use of this resource and that we give them all permissions so that they can upload, view and delete images. This is yet another useful example of how the &lt;code&gt;Amplify Auth&lt;/code&gt; category automatically links with other categories to provide authentication and authorization functionality. &lt;/p&gt;

&lt;p&gt;This command adds some &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/d60fda3e6593bb3db60cf6e3a32f1f7f55c3be3a" rel="noopener noreferrer"&gt;changes&lt;/a&gt; to our repo. In the next step, we will start using the new category.&lt;/p&gt;

&lt;p&gt;We will separate this functionality into three components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image uploader&lt;/strong&gt;: A component responsible for uploading the image and giving the user feedback about the upload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image album&lt;/strong&gt;: A component responsible for listing all uploaded images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image viewer&lt;/strong&gt;: A component that will show only one image in a modal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding an image overview page
&lt;/h2&gt;

&lt;p&gt;We will first start by adding a new Angular component that will be used to show all images our users have uploaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component components/categories/storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate the expected files for our component. We will then link up routing to be able to render this component. This is all pretty standard Angular stuff that we have done in the previous blog, please refer to this &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/f179ceb01792091fec0cfeabdd9bf416f232183c" rel="noopener noreferrer"&gt;commit&lt;/a&gt; for details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding image upload functionality
&lt;/h2&gt;

&lt;p&gt;We will now add a component that will contain the functionality to upload an image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component components/categories/storage/image-upload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate the expected Angular files. Inside our &lt;code&gt;storage.component.html&lt;/code&gt; we will make sure to add the newly generated image-upload:&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;app-image-upload&amp;gt;&amp;lt;/app-image-upload&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;image-upload.component.html&lt;/code&gt;, we will add:&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;input&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;
  &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"imageUpload"&lt;/span&gt;
  &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"imageUpload"&lt;/span&gt;
  &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"image/png, image/jpeg"&lt;/span&gt;
  &lt;span class="na"&gt;(change)=&lt;/span&gt;&lt;span class="s"&gt;"imageSelected($event)"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give us an input that we can use to select images from our device. We have to add logic to react to whenever a user selects an image in our &lt;code&gt;image-upload.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="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;app-image-upload&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;./image-upload.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;./image-upload.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;ImageUploadComponent&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;selectedFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="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;imageSelected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&lt;/span&gt; &lt;span class="o"&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;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this change we set the variable selectedFile to contain the contents of the file we selected. Now that we have this, we can add an upload button that uploads the selected image to the AWS cloud, which we previously configured. &lt;/p&gt;

&lt;p&gt;We will start off by adding a button in &lt;code&gt;image-upload.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;input&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;
  &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"imageUpload"&lt;/span&gt;
  &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"imageUpload"&lt;/span&gt;
  &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"image/png, image/jpeg"&lt;/span&gt;
  &lt;span class="na"&gt;(change)=&lt;/span&gt;&lt;span class="s"&gt;"imageSelected($event)"&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;"aws-button"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"uploadImage()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!---NEW!--&amp;gt;&lt;/span&gt;
  Upload
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this button is clicked the uploadImage function is called. We will define this function in the following way in our &lt;code&gt;image-upload.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;Storage&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="c1"&gt;// &amp;lt;--- add this&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;app-image-upload&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;./image-upload.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;./image-upload.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;ImageUploadComponent&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;selectedFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="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="c1"&gt;//Add this function!&lt;/span&gt;
  &lt;span class="nx"&gt;uploadImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&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="k"&gt;try&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;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&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;selectedFile&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error uploading file: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;//other code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We first import the Storage component from &lt;code&gt;aws-amplify&lt;/code&gt;. We then define the &lt;code&gt;uploadImage&lt;/code&gt; function that is used by our new button. This function uses the Amplify Storage component and calls the put method, which gets the file name, the file contents, and some properties.&lt;/p&gt;

&lt;p&gt;In this case, the options include the type of file we want to accept and the authorization level. Setting the level to &lt;code&gt;private&lt;/code&gt; means that only the user that uploaded this image will have access to it. &lt;/p&gt;

&lt;p&gt;Once we have this in place we can select an image with our input and upload it using the new button. While we don’t see any feedback yet, if we go into our AWS console to S3 and search for our bucket, we should see that there is a new directory called “private” which contains a directory with the cognitoID of the user we used to upload the image. Inside this directory we can see the image that was uploaded:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wmezxdvx25rtf1h42ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wmezxdvx25rtf1h42ly.png" alt="Image description" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding file upload progress feedback
&lt;/h3&gt;

&lt;p&gt;In this step we want to show the user some feedback about the image upload process. We will start by adding an enum for the possible upload states and will set a variable in our component to have the correct state given the situation of the upload process. On our UI we will then show some text based on this state.&lt;/p&gt;

&lt;p&gt;We will first make these changes to our &lt;code&gt;image-upload.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;Storage&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="c1"&gt;//Add this!&lt;/span&gt;
&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;IDLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;UPLOADING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;UPLOAD_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ERROR&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;app-image-upload&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;./image-upload.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;./image-upload.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;ImageUploadComponent&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;selectedFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;//Add these 3:&lt;/span&gt;
  &lt;span class="nx"&gt;uploadStates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;uploadState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IDLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;progressText&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="dl"&gt;''&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="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;uploadImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uploadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UPLOADING&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- Add this&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&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="k"&gt;try&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;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&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;selectedFile&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;progressCallback&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;progressCallback&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;---- Add this&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;uploadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UPLOAD_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- Add this&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;error&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;uploadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;---- Add this&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;Error uploading file: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;//Add this function!&lt;/span&gt;
  &lt;span class="nx"&gt;progressCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;progress&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="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;progressText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Uploaded: &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
      &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; %`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;imageSelected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uploadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IDLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;---- Add this&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&lt;/span&gt; &lt;span class="o"&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;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="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 upload state will start in &lt;code&gt;IDLE&lt;/code&gt;, switch to &lt;code&gt;UPLOADING&lt;/code&gt; and will either go into &lt;code&gt;UPLOADING_COMPLETE&lt;/code&gt; or &lt;code&gt;ERROR&lt;/code&gt; depending on how the call to Amplify Storage goes. The Storage.put function also allows for the definition of a &lt;code&gt;progressCallback&lt;/code&gt; function in order to track the progress of the upload. &lt;/p&gt;

&lt;p&gt;Once these are set in place, we add the following to our &lt;code&gt;image-upload.component.html&lt;/code&gt; to be able to see the feedback:&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;!---Existing code--&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt;
  &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"
    uploadState === uploadStates.UPLOADING ||
    uploadState === uploadStates.UPLOAD_COMPLETE
  "&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{ progressText }}
&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;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"uploadState === uploadStates.UPLOAD_COMPLETE"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Upload complete!
&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;"error-text"&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"uploadState === uploadStates.ERROR"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Something went wrong with the file upload
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will now get some feedback when uploading images.&lt;/p&gt;

&lt;p&gt;The complete set of changes for this step can be found in this &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/2599014b62b4a589cd6dcb343b242da0a50cf861" rel="noopener noreferrer"&gt;commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding an image overview
&lt;/h2&gt;

&lt;p&gt;Now that we can upload images, we want to view all our uploaded images. We start by creating an image-album component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component components/categories/storage/image-album
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are going to update the &lt;code&gt;image-album.component.ts&lt;/code&gt; to contain logic to get all of the images for our user:&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;Storage&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;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;key&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;url&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="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;app-image-album&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;./image-album.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;./image-album.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;ImageAlbumComponent&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;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Image&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="nf"&gt;constructor&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAllImages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;getAllImages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&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="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;imageObject&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objectKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&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;objectKey&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;signedURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;objectKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;download&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;objectKey&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;signedURL&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;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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some important things to note here is to set the levels to private in both the &lt;code&gt;Storage.list&lt;/code&gt; and &lt;code&gt;Storage.get&lt;/code&gt; functions. The reason we need to use two functions is because the &lt;code&gt;Storage.list&lt;/code&gt; only gives us some information about the objects, such as the unique key in &lt;code&gt;S3&lt;/code&gt;. However it does not give use a URL with which we can actually view the object.&lt;/p&gt;

&lt;p&gt;For this we need a signed URL. Because we are logged in and we call the &lt;code&gt;Storage.get&lt;/code&gt; function with the &lt;code&gt;private&lt;/code&gt; level, the &lt;code&gt;Amplify Storage&lt;/code&gt; component will check which user is logged in and verify that this user may view this object and create a signed url.&lt;/p&gt;

&lt;p&gt;We also created an &lt;code&gt;Image&lt;/code&gt; interface which we will use to contain the image key and signed url retrieved from &lt;code&gt;S3&lt;/code&gt;. We will store these in an array on the component and loop through these to show the images in &lt;code&gt;image-album.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;"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;"row"&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let image of images"&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;"col-lg-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;"col-lg-8"&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;"album-image"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ image.url }}"&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;"col-lg-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;This simply loops through the imageUrls and shows an image for each. We also add a bit of styling to add the width and a bit of margin in our &lt;code&gt;image-album.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="nc"&gt;.album-image&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;8px&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 results in the images being viewed on the page like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2nttf3g5pdicz523mmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2nttf3g5pdicz523mmi.png" alt="Image description" width="800" height="793"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For all the code changes, including the wiring the new component, see this &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/b050c9d02217b35f84993f92593a44659ed6562c" rel="noopener noreferrer"&gt;commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding image removal functionality
&lt;/h2&gt;

&lt;p&gt;In this section we are going to add a button to remove images that we have uploaded. We will first add a &lt;code&gt;removeImage&lt;/code&gt; function in our &lt;code&gt;image-album.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="nx"&gt;removeImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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;=&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;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&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;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAllImages&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 reassign the images array here to force Angular to rerender the image-album component so that we can see that our image as indeed been removed. We then update our &lt;code&gt;image-album.component.html&lt;/code&gt; to contain an “x” button for removal next to each image:&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;"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;"row"&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let image of images"&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;"col-lg-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;"col-lg-8"&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;"album-image"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ image.url }}"&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;"col-lg-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!--NEW! --&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;"remove-image-button"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"removeImage(image.key)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        x
      &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we add a bit of styling to the button:&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="nc"&gt;.remove-image-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;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&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;And now we have a button that allows us to remove images, which will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rexufmagl76s5kwh1mg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rexufmagl76s5kwh1mg.png" alt="Image description" width="800" height="931"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As always, the changes for this step can be found in this &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/8b3884c34a94672a2851f58ae1a8c68c07c99411" rel="noopener noreferrer"&gt;commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting the maximum number of uploads per user
&lt;/h2&gt;

&lt;p&gt;The final step we want to add for our image upload is to add a maximum number of images per user so that a user does not flood our &lt;code&gt;S3&lt;/code&gt; buckets. We don’t have a property for this in the &lt;code&gt;Amplify Storage&lt;/code&gt; component, so we need to build this ourselves. Also, note that the solution here is only a frontend check, there is no actual check on the bucket itself per user. &lt;/p&gt;

&lt;p&gt;We make the following changes to our &lt;code&gt;image-upload.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;Storage&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_NUMBER_OF_IMAGES_PER_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;IDLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;UPLOADING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;UPLOAD_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;MAX_REACHED&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- add this!&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;app-image-upload&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;./image-upload.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;./image-upload.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;ImageUploadComponent&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="c1"&gt;//Existing code...&lt;/span&gt;

  &lt;span class="nx"&gt;uploadImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uploadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UPLOADING&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&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="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//Add this part!&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userImages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&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="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&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;userImages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;MAX_NUMBER_OF_IMAGES_PER_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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uploadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UploadState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MAX_REACHED&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="c1"&gt;//Existing code...&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//Existing code ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;//Existing code...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We first add a new state called &lt;code&gt;MAX_REACHED&lt;/code&gt; which we will set when this is the case. Inside the &lt;code&gt;uploadImage&lt;/code&gt; function we will add a new check where we will first list the objects of the user and check if the length is equal or greater than the max. If this is the case we set the upload state to &lt;code&gt;MAX_REACHED&lt;/code&gt; and stop the upload.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;image-upload.component.html&lt;/code&gt;, we add a new error message that is shown in this upload state:&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;!---Existing code...--&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;"error-text"&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"uploadState === uploadStates.MAX_REACHED"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Reached maximum amount of uploads. Please remove an image before trying again.
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we try to add the 11th image, we get the following message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F206rikmz5n82y2beyjny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F206rikmz5n82y2beyjny.png" alt="Image description" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Changes for this step can be found &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/1e1f7876d85d5c87e2ede32ae02039957d336338" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up next: AI and ML with Amplify Predictions
&lt;/h2&gt;

&lt;p&gt;In this blog, we have used the &lt;code&gt;Amplify Storage&lt;/code&gt; category to update our existing application to contain image upload, viewing, and removal functionality per user. In the next article, we will look at using the power of AI and Machine Learning with &lt;code&gt;Amplify Predictions&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>discuss</category>
      <category>gamechallenge</category>
    </item>
    <item>
      <title>Top 8 tips for visiting re:Invent 2023</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Fri, 20 Jan 2023 11:11:58 +0000</pubDate>
      <link>https://dev.to/aws-builders/top-8-tips-for-visiting-reinvent-2023-257b</link>
      <guid>https://dev.to/aws-builders/top-8-tips-for-visiting-reinvent-2023-257b</guid>
      <description>&lt;p&gt;AWS re:Invent 2022 will be an event I will never forget. From the sheer size of the event to the &lt;a href="//youtube.com/playlist?list=PL2yQDdvlhXf_hIzmfHCdbcXj2hS52oP9r"&gt;high-quality sessions&lt;/a&gt;, conversations, and swag, re:Invent has a lot to offer if you are at all working with AWS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktrby6tkq3gm1g8lgvgl.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktrby6tkq3gm1g8lgvgl.jpeg" alt="Re:Invent entrace" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The event took place in Las Vegas, spanning multiple venues on the strip. AWS did their best to ensure that the event’s magnitude was manageable for the visitoran app that shows everything that is happening that week and  by introducing helps you navigate between the venues. &lt;br&gt;
Even with this app handy and all of the re:Invent employees ready to help you at any moment, there are still some things that are good to know before going to the event. In this blog, I will share 8 tips for re:Invent 2023 that I learned from my visit this year.&lt;/p&gt;

&lt;h2&gt;
  
  
  #1: Reserve your sessions
&lt;/h2&gt;

&lt;p&gt;The sessions scheduled for re:Invent will be made available much earlier than the event, and you will have the option to reserve your seat at these sessions. I highly recommend you take the time to do this and reserve the sessions that you want to go to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2qc5lh0w5sniy51y1kx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2qc5lh0w5sniy51y1kx.jpeg" alt="Lines" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you finally visit re:Invent, all sessions will have two lines. One for the reserved visitors and the other for the unreserved (walk-up) visitors. Having a reserved seat will not only guarantee you a spot at the session, but it also allows you to do other things and show up 15 minutes before the session and still enter. The people in the walk-up line usually start to gather about 45 minutes before the session and have to stand, and sometimes sit, in line until 10 minutes before the session. Don’t let this happen to you. Reserve your seat!&lt;/p&gt;

&lt;h2&gt;
  
  
  #2: Get Certified
&lt;/h2&gt;

&lt;p&gt;There are sessions, swag, events, and even a special lounge for visitors that are AWS certified.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6abbcdswmhqys6awwbhf.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6abbcdswmhqys6awwbhf.jpeg" alt="Certification event" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had the most interesting interactions and the most fun at these events. I had conversations with AWS builders around the world. We had discussions about the certification process, how they use AWS at the companies they work, what kind of costs they have, which AWS services they use, and a lot more. We also played games and had some drinks, and eventually added each other on LinkedIn to keep in touch. Don’t miss out on this. Get certified! Any official AWS certification will do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozpcckgkh1d6c2agexz7.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozpcckgkh1d6c2agexz7.jpeg" alt="Bowling alley" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  #3: Join the AWS Community builders
&lt;/h2&gt;

&lt;p&gt;There are also special perks for visitors that are part of the &lt;a href="https://aws.amazon.com/developer/community/community-builders/" rel="noopener noreferrer"&gt;AWS Community Builders&lt;/a&gt; group. This gives you access to events and special SWAG. There is an open application twice a year to join. To get into the AWS community builders, you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write blogs about AWS&lt;/li&gt;
&lt;li&gt;Speak at conferences about AWS&lt;/li&gt;
&lt;li&gt;Give workshops on AWS technology&lt;/li&gt;
&lt;li&gt;Answer questions online about AWS technology&lt;/li&gt;
&lt;li&gt;Contribute to AWS open source projects&lt;/li&gt;
&lt;li&gt;…anything else that spreads the word and helps others learn about AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By joining, you get several benefits next to all of the special access and SWAG at re:Invent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;500$ AWS credit&lt;/li&gt;
&lt;li&gt;1 year subscription at &lt;a href="https://cloudacademy.com/" rel="noopener noreferrer"&gt;CloudAcademy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;1 AWS certified exam voucher&lt;/li&gt;
&lt;li&gt;Access to Slack which gives you access to AWS community builders and Hero’s&lt;/li&gt;
&lt;li&gt;re:Invent discount of 50%&lt;/li&gt;
&lt;li&gt;More SWAG&lt;/li&gt;
&lt;li&gt;Lots of exclusive access to resources (webinars, beta previews)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8w46jpdowe8xf4g6l1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8w46jpdowe8xf4g6l1f.png" alt="Community Builders" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  #4: Leave room for SWAG
&lt;/h2&gt;

&lt;p&gt;AWS re:Invent has SWAG, a lot of it! Once you visit the Expo, where all the stands are of the event sponsors, you will be overwhelmed with the number of companies that have shown up and are eager to talk to you about their products. Most, if not all stands have some sort of SWAG to give to you. These come in all shapes and sizes. Leave some room in your suitcase!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fac2nasqwpvltl6cuzb90.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fac2nasqwpvltl6cuzb90.jpeg" alt="SWAG" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  #5: Vary the type of session you visit
&lt;/h2&gt;

&lt;p&gt;There are several types of sessions to choose from at re:Invent, ranging from typical breakout sessions to workshops. One fact to note is that a breakout session is recorded and can be viewed later on, while sessions such as Chalk or builders sessions do not. If you are struggling to decide which sessions to go to and some of them overlap, consider the session type and choose the non-breakout sessions. You can always watch the breakout sessions later.&lt;/p&gt;

&lt;p&gt;For example, at one of the “builder sessions” I was sitting at a table with a Senior AWS architect and other re:Invent visitors to discuss how we can monitor costs on AWS. It was very insightful to see which tools were being used, either AWS native or third party, and also to see the massive number of costs some people are dealing with. One of the tools they discussed in this session is the &lt;a href="https://aws.amazon.com/blogs/mt/visualize-and-gain-insights-into-your-aws-cost-and-usage-with-cloud-intelligence-dashboards-using-amazon-quicksight/" rel="noopener noreferrer"&gt;CUDOS&lt;/a&gt; dashboard, which you can deploy via a CloudFormation template that can monitor various aspects of your costs and give you recommendations. &lt;/p&gt;

&lt;p&gt;Another type of session was the “Chalk” session, which was very interactive. In one of the sessions, we designed a solution that included using Lambda extensions to debug them from your local development environment, decreasing the feedback loop during Lambda development.&lt;/p&gt;

&lt;p&gt;I also visited some breakout sessions, which are available now on Youtube. Some of my favorites include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=RmwKBPCo7o4&amp;amp;ab_channel=AWSEvents" rel="noopener noreferrer"&gt;Unleash developer productivity with infrastructure from code&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;An introduction to Infrastructure FROM code from the CEO of &lt;a href="https://getampt.com/" rel="noopener noreferrer"&gt;Ampt&lt;/a&gt; AWS services.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://www.youtube.com/watch?v=MZBbhqt6bQs&amp;amp;ab_channel=AWSEvents" rel="noopener noreferrer"&gt;A close look at AWS Fargate and AWS App Runner&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A walkthrough of the evolution from EC2 instances to AWS App Runner, given by a principal engineer at AWS that has been there from the start.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://www.youtube.com/watch?v=tPr1AgGkvc4&amp;amp;ab_channel=AWSEvents" rel="noopener noreferrer"&gt;A day in the life of a billion requests&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A look into how AWS optimized their authentication mechanism to handle half a billion requests per second to IAM. (spoiler: they use a lot of HMAC’s)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://www.youtube.com/watch?v=Zrj7RD7G24Q&amp;amp;t=551s&amp;amp;ab_channel=AWSEvents" rel="noopener noreferrer"&gt;Are you integrating or building distributed applications?&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;An overview of choices you have to make when building distributed applications and makes clear that the words you use are important. Recommended for anyone in an Architect role.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://www.youtube.com/watch?v=goYiaIGebFo&amp;amp;ab_channel=AWSEvents" rel="noopener noreferrer"&gt;The architect elevator: Connecting the boardroom and IT&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This talk describes the impact you can have as an architect within a company and gives you tips on how to do this. Also highly recommended for anyone in an Architect role.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  #6: Take the venue’s locations into account
&lt;/h2&gt;

&lt;p&gt;As mentioned previously, re:Invent takes place at multiple venues on the Las Vegas strip. Take a moment to check where all the venues are and how long it takes to walk to each venue. AWS offers shuttles between venues, and in my experience, these work well. However, even with the shuttles, if you have to get from the Wynn to the Mandalay Bay, then you are probably going to miss the mark.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbz7wy8vk0xqvquaqa65z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbz7wy8vk0xqvquaqa65z.png" alt="Location" width="670" height="1072"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  #7: Book a hotel close to the Expo
&lt;/h2&gt;

&lt;p&gt;The expo was held at the Venetian. This is the place you want to be when you are not in any other sessions or events. There is a lot to do and see there, such as speaking to people at the several stands about their AWS-related products, collect SWAG, play several types of games, win prizes, attend lightning talks, view product demo’s and get some snacks.&lt;/p&gt;

&lt;p&gt;Booking a hotel close to the Expo gives you more opportunities to experience everything it offers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctaydzwavpoa22oeylny.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctaydzwavpoa22oeylny.jpeg" alt="Expo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  #8: Register and attend receptions
&lt;/h2&gt;

&lt;p&gt;There is breakfast and lunch included on most days of re:Invent. However, try registering for as many receptions as possible for dinner. These will be announced in the AWS re:Invent app and will be hosted by several of the sponsors. This gives you a good chance to network with different kinds of people. Some of these will offer buffet dinners which is a nice plus. &lt;/p&gt;

&lt;p&gt;For some of these events you have to register on time or you won’t get in, and in some cases, the registration starts well before re:Invent, so be sure to keep an eye out online and in your email inbox. If you get an invitation, register immediately as they are quickly full.&lt;/p&gt;

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

&lt;p&gt;I had a great time at re:Invent, and I hope these tips will help improve your re:Invent experience if you choose to attend in person. Of course, you can attend re:Invent virtually as well, for free. Some of these tips, such as getting certified or joining a community, will require you to take action now. It might seem like a lot of work. However, you will be happy once you reap the benefits at re:Invent next year.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>The Amplify Series, Part 4: Developing and deploying a cloud-native application with AWS Amplify</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Fri, 10 Jun 2022 12:31:08 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-amplify-series-part-4-developing-and-deploying-a-cloud-native-application-with-aws-amplify-4ai5</link>
      <guid>https://dev.to/aws-builders/the-amplify-series-part-4-developing-and-deploying-a-cloud-native-application-with-aws-amplify-4ai5</guid>
      <description>&lt;p&gt;&lt;strong&gt;In this article, we will be using AWS Amplify to create a cloud-native application. We will cover the Authentication, GraphQL API, and Hosting categories of AWS Amplify. We will be using Angular for the frontend. However, the steps taken here should be similar for other frameworks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have learned &lt;a href="https://www.luminis.eu/blog/cloud-en/the-amplify-series-part-1-what-is-aws-amplify/"&gt;what Amplify is&lt;/a&gt;, &lt;a href="https://www.luminis.eu/blog/the-amplify-series-part-2-how-does-aws-amplify-work/"&gt;how Amplify works behind the scenes&lt;/a&gt;, and &lt;a href="https://www.luminis.eu/blog/the-amplify-series-part-3-why-should-you-use-aws-amplify/"&gt;Amplify use cases&lt;/a&gt; throughout this series. It is time to see this in action and get hands-on experience using  Amplify. At the end of this article, you will have a cloud-native application.&lt;/p&gt;

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

&lt;p&gt;Before getting started, we need to make sure we have some things set. You need to have the following to be able to follow these instructions and build your application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An active AWS account.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt;: I used version 14.17.5.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://angular.io/cli"&gt;Angular CLI&lt;/a&gt;: I used version 13.1.4.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/downloads"&gt;Git&lt;/a&gt;: Necessary to check out the example project.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.amplify.aws/cli/start/install/"&gt;Amplify CLI&lt;/a&gt;: I used version 7.6.22.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configuring Amplify CLI
&lt;/h3&gt;

&lt;p&gt;Before we can start using the CLI, we need to &lt;a href="https://docs.amplify.aws/cli/start/install/#option-2-follow-the-instructions"&gt;configure the AWS Amplify CLI&lt;/a&gt; to use our AWS account. Once you have configured your CLI, we can get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing dependencies &amp;amp; launching the app
&lt;/h2&gt;

&lt;p&gt;We have created an &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/tree/master"&gt;example AWS Amplify Angular app&lt;/a&gt; that you can clone. Check out the &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/tree/start_here"&gt;start_here branch&lt;/a&gt; and start from there. To ensure everything is working, run &lt;code&gt;npm run start&lt;/code&gt;. You should see a simple application called “The Amplify App”:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5DhRmGHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2urc0qt0pwkj3z4ke4s1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5DhRmGHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2urc0qt0pwkj3z4ke4s1.png" alt="The amplify app" width="880" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This app is currently a simple Angular app with no associated backend yet. From this point, we will start using the CLI to initialise our project, generate backend resources, and connect them to the frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialising the backend
&lt;/h2&gt;

&lt;p&gt;The first command we will run is &lt;code&gt;amplify init&lt;/code&gt;. You should see output similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MBP:theamplifyapp evertsoncroes$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project theamplifyapp
The following configuration will be applied:

Project information
| Name: theamplifyapp
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: angular
| Source Directory Path: src
| Distribution Directory Path: dist/theamplifyapp
| Build Command: npm run-script build
| Start Command: ng serve

? Initialize the project with the above configuration? Yes
Using default provider awscloudformation
? Select the authentication method you want to use: AWS profile

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

? Please choose the profile you want to use default
⠴ Initializing project in the cloud...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is done, our Amplify project is created, and we can see several new files in our repository. If you need a refresher of what these files are and how Amplify works, check out part 2 of this blog series: &lt;a href="https://www.luminis.eu/blog/the-amplify-series-part-2-how-does-aws-amplify-work/"&gt;how does AWS amplify work&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;If you are creating a public project, it is recommended to add the &lt;code&gt;amplify/team-provider-info.json&lt;/code&gt; to the &lt;code&gt;.gitignore&lt;/code&gt;, as it contains identifiers to the resources we are using for Amplify. This does not create a security risk since your project is secured with your credentials. If multiple developers are working on the same project, they all need this file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/fe366ac47fed1e8ea1830cf5d3ea19d977de069f"&gt;If everything is working, you should see about 7 changed files.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the frontend
&lt;/h2&gt;

&lt;p&gt;Now we need to install the relevant Amplify frontend libraries. In this case, we 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 install --save aws-amplify @aws-amplify/ui-angular
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install the amplify library and Angular specific UI elements that we will be using later. After this, we want to configure the Amplify library with our &lt;code&gt;aws-exports.js&lt;/code&gt; by adding the following to our &lt;code&gt;src/main.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;Amplify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;aws_exports&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="nx"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws_exports&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your IDE might complain about the &lt;code&gt;./aws-exports&lt;/code&gt; not having a type definition. An easy fix for now is to add &lt;code&gt;allowJs:true&lt;/code&gt; to our &lt;code&gt;tsconfig.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we also need to add the following to &lt;code&gt;src/polyfill.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="nx"&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;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="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to add these since they are used by Amplify but are not present by default since Angular 6.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/a6526bd2752546588c5383c32a80a36ba32e0b3a"&gt;You should see around 5 file changes after this step.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Amplify Authentication
&lt;/h2&gt;

&lt;p&gt;We are now going to add user authentication functionality to our application. &lt;/p&gt;

&lt;h3&gt;
  
  
  Generating backend resources for authentication
&lt;/h3&gt;

&lt;p&gt;The first thing we will do is generate backend resources that will help us with authentication by running &lt;code&gt;amplify add auth&lt;/code&gt;. You should see output similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MBP:theamplifyapp evertsoncroes$ amplify add auth
Using service: Cognito, provided by: awscloudformation

The current configured provider is Amazon Cognito.

Do you want to use the default authentication and security configuration? Default configuration
Warning: you will not be able to edit these selections.
How do you want users to be able to sign in? Username
Do you want to configure advanced settings? No, I am done.
✅ Successfully added auth resource theamplifyappfdeaa7e5 locally

✅ Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should now see some new files added to our project, such as files under &lt;code&gt;amplify/backend/auth&lt;/code&gt;, which represent our authentication choices. However, nothing has happened in the backend yet. We need to run &lt;code&gt;amplify push&lt;/code&gt; to push our changes to AWS and have CloudFormation create the resources for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MBP:theamplifyapp evertsoncroes$ amplify push

✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

┌──────────┬───────────────────────┬───────────┬───────────────────┐
│ Category │ Resource name │ Operation │ Provider plugin │
├──────────┼───────────────────────┼───────────┼───────────────────┤
│ Auth │ theamplifyappfdeaa7e5 │ Create │ awscloudformation │
└──────────┴───────────────────────┴───────────┴───────────────────┘
? Are you sure you want to continue? Yes
⠋ Updating resources in the cloud. This may take a few minutes...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding the authentication component to the frontend
&lt;/h3&gt;

&lt;p&gt;We will be using &lt;a href="https://ui.docs.amplify.aws/react/components/authenticator"&gt;the Amplify Authenticator UI&lt;/a&gt; component to implement authentication in the frontend. We already installed this previously however now, we still need to add it to &lt;code&gt;src/app/app.module.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="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;AmplifyAuthenticatorModule&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/ui-angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Add this&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="c1"&gt;//Existing declarations&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="c1"&gt;//other imports&lt;/span&gt;
&lt;span class="nx"&gt;AmplifyAuthenticatorModule&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Add this&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="c1"&gt;// Other stuff&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are going to start off by simply protecting our entire application with the Amplify authenticator. To do this, add the following to &lt;code&gt;src/app/app.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;amplify-authenticator&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- &amp;lt;--Add this --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;app-header&amp;gt;&amp;lt;/app-header&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-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&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;app-footer&amp;gt;&amp;lt;/app-footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/amplify-authenticator&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- &amp;lt;--Add this --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we also need to import the default styling in our &lt;code&gt;src/app/styles.css&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-amplify/ui-angular/theme.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* other styles */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run your application now, you should see the Amplify authenticator:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4G5zZQXu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6wzi806ik0akkhnte9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4G5zZQXu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6wzi806ik0akkhnte9z.png" alt="Amplify authenticator" width="880" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use this to create an account and log in to see the application. It is a fully-featured authenticator with registration, sign-in, forgot password, and email activation. It also gives feedback when errors occur. &lt;/p&gt;

&lt;p&gt;There are several options to customize this UI. You can &lt;a href="https://ui.docs.amplify.aws/angular/components/authenticator?platform=angular"&gt;find out more about the Amplify Authenticator&lt;/a&gt; in the documentation.&lt;/p&gt;

&lt;p&gt;Once you have created an account, you should be able to log in to the AWS console, navigate to the Cognito service and see a user pool with the name of your project. Inside that user pool, you should see your new account:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--08ut3XyN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gn3qxg346vu6mxvlwg5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--08ut3XyN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gn3qxg346vu6mxvlwg5w.png" alt="Amplify cognito" width="880" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding signout functionality
&lt;/h3&gt;

&lt;p&gt;We also want to be able to sign out of our application. First we add the actual button in header.component.html:&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="c"&gt;&amp;lt;!-- other existing code --&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;"aws-button"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"signOut()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign out&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!--  &amp;lt;-- Add this --&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;And then, we add our sign out logic, which we get directly from Amplify, in our &lt;code&gt;header.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;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="c1"&gt;// &amp;lt;-- Add this&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;HeaderComponent&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="c1"&gt;// Other existing code&lt;/span&gt;

    &lt;span class="nx"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Add this&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;signOut&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;And now, we have a sign-out button which we can use to sign out and go back to the Amplify Authenticator.&lt;/p&gt;

&lt;h3&gt;
  
  
  User data
&lt;/h3&gt;

&lt;p&gt;We will add a &lt;code&gt;Welcome &amp;lt;&amp;lt;username&amp;gt;&amp;gt;&lt;/code&gt; text in the header. We can use the Amplify Auth library to get this data. We will begin by creating an Angular service which we will call &lt;code&gt;UserService&lt;/code&gt;, which will handle all of our logged-in user’s needs. &lt;/p&gt;

&lt;p&gt;In the terminal, run the following at the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate service services/shared/user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will update the new service in the following way:&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;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="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;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UserInfo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&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="err"&gt; &lt;/span&gt; &lt;span class="nx"&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="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="err"&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;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&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="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserInfo&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userInfoResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentUserInfo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userInfoResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributes&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;userInfoResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="err"&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;It makes use of the Amplify Auth library to get information about the current user. We then make some changes in our &lt;code&gt;header.component.ts&lt;/code&gt; file to call this function:&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;UserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserInfo&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;../../services/shared/user.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//&amp;lt;--- Add this&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;HeaderComponent&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="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//other code&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;currentUserName&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="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//&amp;lt;--- Add this&lt;/span&gt;

&lt;span class="err"&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;public&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Router&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;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;//&amp;lt;--- Add userService&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//other code&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&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="c1"&gt;//&amp;lt;--- Add this     this.userService       .getCurrentUserInfo()       .then((userInfo: UserInfo) =&amp;gt; {&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;currentUserName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userInfo&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;Error while obtaining user: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&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;And now that we have variable in the header component with the current username, we can display this in the &lt;code&gt;header.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="c"&gt;&amp;lt;!-- other code --&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;"welcome-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Welcome {{ currentUserName }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- &amp;lt;--- add this --&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;"aws-button"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"signOut()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign out&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;And finally we add some styling in &lt;code&gt;header.component.css&lt;/code&gt; so that we can actually see the text on the dark background:&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="nc"&gt;.welcome-text&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;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&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;And now we have a text that shows us the username of the user that is logged in:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BzB94r-6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu1dx6pc6bhkgfb3qm5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BzB94r-6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu1dx6pc6bhkgfb3qm5o.png" alt="Logged in" width="540" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://aws-amplify.github.io/amplify-js/api/classes/authclass.html"&gt;Amplify Auth documentation&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;We’ve changed several things now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/da4768a12af1e0f9e2d6956f4a7b4b7c384028c6"&gt;We added the Amplify Authenticator UI.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/c08ff7e297d9c7c27baefad68c7bd71156a99548"&gt;We added a signout button.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/e807577f3a1a114ff79e4554c57ad676b09ca903"&gt;We added user details to the header.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Connecting the backend with GraphQL
&lt;/h2&gt;

&lt;p&gt;In this section, we will add a GraphQL API for our application, which will replace the current in-memory mock data being used for the API page in our application. If you click through the API tab, you will see that we have a list of Posts that can all have a list of Likes and Comments. We will be modeling this in GraphQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating backend resources for the API
&lt;/h3&gt;

&lt;p&gt;To start, we need to add a resource endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MBP:theamplifyapp evertsoncroes$ amplify add api
? Select from one of the below-mentioned services: GraphQL
? Here is the GraphQL API that we will create. Select a setting to edit or continue Authorization modes: API key (default, expiration time: 7 days from now)
? Choose the default authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project.
? Configure additional auth types? No
? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue
? Choose a schema template: One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”)

⚠️ WARNING: Some types do not have authorization rules configured. That means all create, read, update, and delete operations are denied on these types:
- Blog
- Post
- Comment
Learn more about "@auth" authorization rules here: https://docs.amplify.aws/cli/graphql/authorization-rules
GraphQL schema compiled successfully.

Edit your schema at /Users/evertsoncroes/Documents/development/private/theamplifyapp/amplify/backend/api/theamplifyapp/schema.graphql or place .graphql files in a directory at /Users
/evertsoncroes/Documents/development/private/theamplifyapp/amplify/backend/api/theamplifyapp/schema
✔ Do you want to edit the schema now? (Y/n) · yes
✅ Successfully added resource theamplifyapp locally

✅ Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to change the authentication method from &lt;strong&gt;API key&lt;/strong&gt; to &lt;strong&gt;Amazon Cognito User Pool&lt;/strong&gt; to use the Auth resources we created in the last section.&lt;/p&gt;

&lt;p&gt;We now have some new files generated for us by the Amplify CLI. We are going to start by editing the &lt;code&gt;amplify/backend/api/schema.graphql&lt;/code&gt; to look as follows:&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;type&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;model&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;auth&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="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;read&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="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
&lt;span class="nx"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Like&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;hasMany&lt;/span&gt;
&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;hasMany&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Like&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;model&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;auth&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="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;read&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="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;belongsTo&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Comment&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;model&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;auth&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="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;read&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="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;belongsTo&lt;/span&gt;
&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&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 this example, we have defined 3 GraphQL Object types for Post, Likes, and Comments. We don’t specify a date field because we get a createdAt and updatedAt field for each type by default. &lt;/p&gt;

&lt;p&gt;The Amplify specific parts of this schema are the directives, which you can notice by the @ prefix. The directives used here are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@model: This creates a DynamoDB table to back this model. In this case, 3 tables will be created.&lt;/li&gt;
&lt;li&gt;
&lt;a class="mentioned-user" href="https://dev.to/auth"&gt;@auth&lt;/a&gt;: These are authorization rules for the type. The rules described here are:

&lt;ul&gt;
&lt;li&gt;The owner (creator) of a record is allowed to perform all operations on that record&lt;/li&gt;
&lt;li&gt;An “owner” field will be added to each item in the DB, which contains the username of the logged-in user that created the item&lt;/li&gt;
&lt;li&gt;Only logged in users (private) can read the records for this type&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;@hasMany: This creates a one-to-many relationship with another type. In our example above, Posts have many comments and likes.&lt;/li&gt;
&lt;li&gt;@belongsTo: This creates a many-to-one relationship with another type. In our example above, comments belong to one post, and likes belong to one post&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can learn more about &lt;a href="https://docs.amplify.aws/cli-legacy/graphql-transformer/directives/"&gt;directives&lt;/a&gt; or &lt;a href="https://docs.amplify.aws/cli/graphql/authorization-rules/"&gt;authorization rules&lt;/a&gt; in the Amplify documentation.&lt;/p&gt;

&lt;p&gt;After customizing our schema we run &lt;code&gt;amplify push&lt;/code&gt; and select &lt;strong&gt;yes&lt;/strong&gt;. We are then asked a few questions related to graphql:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target angular
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.graphql
? Do you want to generate/update all possible GraphQL operations - queries, mutations, and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
? Enter the file name for the generated code src/app/API.service.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate a lot of front-end code specific to our schema so thatuse we can easily  our GraphQL backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting the frontend to the API
&lt;/h3&gt;

&lt;p&gt;A few files have been generated that are important to cover. First of all, there is the API.service.ts. This file contains all generated types and statements that can be used with the GraphQL API and injected as an Angular Service into Angular components and services.&lt;/p&gt;

&lt;p&gt;Secondly, we also have a directory now called &lt;strong&gt;graphql&lt;/strong&gt; in the root of our project. This file contains generated GraphQL Queries, Mutations and Subscription, and a JSON representation of our GraphQL schema. These files are used as input to generate &lt;code&gt;API.service.ts&lt;/code&gt;. We will look at these files later in the article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filling the data source
&lt;/h3&gt;

&lt;p&gt;We will start by updating our &lt;code&gt;post.service.ts&lt;/code&gt; to save new posts to our Amplify backend instead of in the &lt;code&gt;mockObject&lt;/code&gt;. We will make the following 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="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;APIService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/app/API.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- Add this&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="err"&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;PostService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//other code&lt;/span&gt;

&lt;span class="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APIService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- Add api to constructor&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;addPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&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="nx"&gt;description&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="c1"&gt;// &amp;lt;--- replace current addPost with new code&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CreatePost&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//other code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we import the generated API service and use one of the generated functions, &lt;code&gt;CreatePost&lt;/code&gt;, to create a new post item to our backend. If you search for the definition of the &lt;code&gt;CreatePost&lt;/code&gt; function in &lt;code&gt;src/app/API.service&lt;/code&gt; you can see which parameters it expects. In this case adding a title and description is enough. &lt;/p&gt;

&lt;p&gt;Now when we add posts, they will be persisted. &lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieving data from the backend
&lt;/h3&gt;

&lt;p&gt;The next step is to retrieve all our posts from the backend. We will make the following changes to our &lt;code&gt;post.service.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="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;APIService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;AmplifyPost&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/app/API.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- Add this new import&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="err"&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;PostService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&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;Post&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&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;Post&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- remove mockposts and its usage completely&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;postsData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Post&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="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APIService&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;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;setOnPostCreateSubscription&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// Other code&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; 
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//Update this function&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;getAllPosts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&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="err"&gt; &lt;/span&gt; &lt;span class="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListPosts&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="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;responsePosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Post&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;response&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="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&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;convertToPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;AmplifyPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;responsePosts&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="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;posts&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;responsePosts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;posts&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="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//Add this function&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;convertToPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amplifyPost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AmplifyPost&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amplifyPost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;likesItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;likes&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;likes&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="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commentsItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;comments&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="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;description&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;owner&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;likesItems&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;likesItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;commentsItems&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;commentsItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="err"&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 doing a few things here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Importing the generated &lt;code&gt;Post&lt;/code&gt; interface and giving it an alias since &lt;code&gt;Post&lt;/code&gt; already exists and is used for our Post component UI model&lt;/li&gt;
&lt;li&gt;Removing the mock data completely since this service will now be serving data from the Amplify backend&lt;/li&gt;
&lt;li&gt;Using the &lt;code&gt;listPosts&lt;/code&gt; generated function to get all posts in our backend.&lt;/li&gt;
&lt;li&gt;Converting the posts retrieved from the amplify backend to the Post model we want to use in our UI. A significant difference here is that we want to count the number of likes and comments and return numbers. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we run our app now, we should now see new data retrieved from the backend. If we add a new post and refresh the page ,we should see the latest post in the list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscribing to data
&lt;/h3&gt;

&lt;p&gt;It would be nice to have the new post show up in the list immediately after it is added. One way to achieve this is to call &lt;code&gt;getAllPosts&lt;/code&gt; after the &lt;code&gt;addPosts&lt;/code&gt; was successful. There is a better way, though: Amplify GraphQL also allows us to subscribe to certain events. &lt;/p&gt;

&lt;p&gt;In this case, we can subscribe to the event of a post being created by making the following changes to our &lt;code&gt;post.service.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;UserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserInfo&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;../shared/user.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Add this import&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="err"&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;PostService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;postsData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Post&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="c1"&gt;// &amp;lt;-- add this&lt;/span&gt;

&lt;span class="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APIService&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;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;setOnPostCreateSubscription&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- add this&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//Add this function&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;setOnPostCreateSubscription&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCurrentUserInfo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;userInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnCreatePostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInfo&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;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;responseData&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;responseData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onCreatePost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&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;convertToPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;responseData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onCreatePost&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;AmplifyPost&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;postsData&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="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;posts&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postsData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;getAllPosts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&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="err"&gt; &lt;/span&gt; &lt;span class="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CustomListPosts&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="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;responsePosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Post&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;response&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="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&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;convertToPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;AmplifyPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;responsePosts&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="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;postsData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;responsePosts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- add this&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;posts&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;responsePosts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;posts&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="err"&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 made these changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Import the &lt;code&gt;UserService&lt;/code&gt; we created in the last section. This is needed because of a limitation in Amplify GraphQL subscription (see below).&lt;/li&gt;
&lt;li&gt;Created an array that holds the state of the latest data retrieved. We need this since the subscription only returns the new post added. We need to add the newly created post to this list and return it to the component.&lt;/li&gt;
&lt;li&gt;We call the &lt;code&gt;onCreatePostListener&lt;/code&gt; with the username of the currently logged in user and we convert the response of this subscription to our &lt;code&gt;Post&lt;/code&gt; model object, similarly to how we did this for the query.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Amplify GraphQL limitation
&lt;/h3&gt;

&lt;p&gt;Currently it is not possible to have the &lt;strong&gt;owner&lt;/strong&gt; auth rule in your GraphQL schema and have a subscription on an object without supplying the owner username. If you want to be able to have a subscription on all objects of a type, regardless of who created it, you need to remove the &lt;strong&gt;owner&lt;/strong&gt; auth rule in your schema. However, this means that you will no longer get the &lt;strong&gt;owner&lt;/strong&gt; field in all of your items in the database and that any logged in user can also update and delete posts that don’t belong to them. &lt;/p&gt;

&lt;p&gt;For the sake of having a working subscription example, we have created a subscription for the current user, meaning you will only get updated for posts that you post yourself. Hopefully in the future we will be able to have more flexibility regarding subscriptions. Nevertheless, this example should still show how easy it is to setup subscriptions and there are still several use cases where this might be useful. &lt;/p&gt;

&lt;h3&gt;
  
  
  Likes and comments
&lt;/h3&gt;

&lt;p&gt;For the &lt;code&gt;Like&lt;/code&gt; and &lt;code&gt;Comment&lt;/code&gt; services we followed steps similar to those followed for the &lt;code&gt;Post&lt;/code&gt;. You can view all of these steps in the Git commits we will list at the end of this section. However, there are some key points we still want to cover that we encounter when migrating likes and comments to the Amplify backend. &lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL query with filters and pagination
&lt;/h3&gt;

&lt;p&gt;When we want to query the likes or comments, we only want to query them if they belong to the Post that we are currently looking at in the frontend. Luckily, the generated functions we use to query our backend come with built-in filtering and pagination functionality. &lt;/p&gt;

&lt;p&gt;If we look at the definition of the &lt;code&gt;ListLikes&lt;/code&gt; function in the &lt;code&gt;API.service.ts&lt;/code&gt;, we can see the following signature:&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;async&lt;/span&gt; &lt;span class="nx"&gt;ListLikes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;ModelLikeFilterInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;nextToken&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ListLikesQuery&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;//code&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;ModelLikeFilterInput&lt;/code&gt; contains options to filter the results based on several conditions. Click through them to see all of the possibilities. We can also see a limit and a nextToken parameters which are used for pagination. Since we did not build pagination in this application yet, we will refer to the &lt;a href="https://docs.amplify.aws/guides/api-graphql/graphql-pagination/q/platform/js/"&gt;pagination documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To query only the likes that belong to a certain post, we use the following code:&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="nx"&gt;getLikesForPostId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&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="err"&gt; &lt;/span&gt; &lt;span class="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListLikes&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;postLikesId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;postId&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="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//other code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom GraphQL Queries, Mutations and Subscriptions
&lt;/h3&gt;

&lt;p&gt;If you follow the patterns we have shown up to now and have posts, likes and comments using the Amplify backend, you will notice that all posts still show 0 likes and comments on the post overview. This is a bug, and has to do with the values that are retrieved in the generated GraphQL query used in the listPosts function. &lt;/p&gt;

&lt;p&gt;If we check the definition of the query in the &lt;code&gt;src/graphql/queries.graphl&lt;/code&gt; we will see the following:&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="nx"&gt;query&lt;/span&gt; &lt;span class="nx"&gt;ListPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&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;ModelPostFilterInput&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;$limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;$nextToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;listPosts&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;$filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$nextToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;likes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;nextToken&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;nextToken&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;updatedAt&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;nextToken&lt;/span&gt;
&lt;span class="err"&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 can see that for the likes and comments, only the &lt;code&gt;nextToken&lt;/code&gt; field is being retrieved. The &lt;code&gt;items&lt;/code&gt; field is not being retrieved at all, which is why our frontend is defaulting to 0. Thankfully, we can define our own queries.&lt;/p&gt;

&lt;p&gt;We can create a new file called &lt;code&gt;src/app/custom-queries.graphql&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="nx"&gt;CustomListPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&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;ModelPostFilterInput&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;$limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;$nextToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;listPosts&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;$filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$nextToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;likes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;updatedAt&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;nextToken&lt;/span&gt;
&lt;span class="err"&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 now query the items for likes and comments and only retrieve the ids, since we only want to count them. Once we add this file, we can run the following in the terminal:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This will generate new code that you can use in your frontend without having to run amplify push. Now we can update our &lt;code&gt;post.service.ts&lt;/code&gt; to make use of this new query:&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="nx"&gt;getAllPosts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&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="err"&gt; &lt;/span&gt; &lt;span class="err"&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CustomListPosts&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="nx"&gt;response&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;// &amp;lt;-- updated here&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;//code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run the application now we should see the correct counters for likes and comments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom GraphQL Resolvers
&lt;/h3&gt;

&lt;p&gt;In our current example we retrieve likes and comments and we count them in the frontend to show the counters. We could also update our GraphQL schema for &lt;code&gt;Post&lt;/code&gt; to include a &lt;code&gt;likesCount&lt;/code&gt; and &lt;code&gt;commentsCount&lt;/code&gt; fields and define custom logic for these fields. &lt;/p&gt;

&lt;p&gt;This would let the backend calculate these numbers for us and return them to the frontend. A demo of this would be too long for this article, so we will refer to the &lt;a href="https://docs.amplify.aws/cli-legacy/graphql-transformer/function/"&gt;documentation for AWS Lambda resolver&lt;/a&gt; configuration, which explains how to set this up.&lt;/p&gt;

&lt;p&gt;We’ve introduced quite some changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/970660cd06469f1d725537eee7f205181c635470"&gt;We added an API endpoint.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;We &lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/a2caa50bb746d41c35a2573c509f32c55c44a427"&gt;updated the GraphQL schema&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/114373e4e7a71ca7c76f81056e272a827de62938"&gt;We pushed the changes to the backend.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/79660ba580152abb7754a4e1736e6e78afcf1961"&gt;We added Posts to the GraphQL backend.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/5e046d428d6f544dad281f51f760c9abfa634890"&gt;We retrieved Posts from the GraphQL backend.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/ece24c0b648e41bd2fd8fc93313fe6ea22caf856"&gt;We added subscriptions for Posts.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/7445d70afd14e99bfe23d5f139b10ecd36cc0314"&gt;We added Likes  to the GraphQL backend.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/91199e15c8a8a242505944512e9e22177c2fcd1a"&gt;We retrieved Likes from the GraphQL backend.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/4a54ec820249fa85c452bc8b943fec5790669bab"&gt;We  fixed Posts subscriptions.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/69a0a0cccd3d13113e4c2639486a0a8e6d5a6648"&gt;We created a custom query to list Posts.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/f1b284efbbdfc19e7339e66d7a17125b8d65f555"&gt;We added a subscription for likes.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/ec5bc2331da6df3b79a08a26c6961c74e4095fc5"&gt;We added Comments to the GraphQL backend.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/3343d16e9d6215aef5c25d9c49f3b2d5c50e489a"&gt;We retrieve Comments from the GraphQL backend.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/9018050a9d57546eef5da837959032205da34844"&gt;We added subscriptions for Comments.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hosting our application
&lt;/h2&gt;

&lt;p&gt;Now that we have our application working locally, it is time to host it on AWS so that it is available online. We are going to do this by adding the Amplify hosting category. Before we do that, we need to do these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in to the AWS console with your default browser.&lt;/li&gt;
&lt;li&gt;Push all your code to Git.&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;initial&lt;/code&gt; budget in &lt;code&gt;angular.json&lt;/code&gt; to &lt;code&gt;2mb&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will run the following command to add hosting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MBP:theamplifyapp evertsoncroes$ amplify add hosting
? Select the plugin module to execute Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment)
? Choose a type Continuous deployment (Git-based deployments)
? Continuous deployment is configured in the Amplify Console. Please hit enter once you connect your repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open the AWS console that will enable us to connect our repository to the CI/CD pipeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SkD5g2pu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7k2n5d27c4jpjmd8npcr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SkD5g2pu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7k2n5d27c4jpjmd8npcr.png" alt="CI/CD" width="880" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Hosting environments&lt;/strong&gt; tab and select your Git repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R7kGMLNC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tph177mamzy3gfpw2knf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R7kGMLNC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tph177mamzy3gfpw2knf.png" alt="Git Repo" width="880" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;connect branch&lt;/strong&gt;. Once you are authenticated with your Git repository, select the repository and branch where the latest version of your code exists and click on &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On the next page it is important that you select an environment to use for this deployment. In this case we have only created a dev environment, so we will use that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---NFh4M0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q8txipyj942s94n72f3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---NFh4M0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q8txipyj942s94n72f3.png" alt="Environment" width="880" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you don’t have an existing service role for Amplify projects, click on &lt;strong&gt;Create new role&lt;/strong&gt; to generate one and click on the refresh button to see it appear as an option.&lt;/p&gt;

&lt;p&gt;If you scroll to the bottom you will see the default build settings. For now these will work, however if in the future you want to extend the build to include more phases or steps you can always change this.&lt;/p&gt;

&lt;p&gt;When we click on next we will get a summary of the options we selected. Review them and click on &lt;strong&gt;Save and deploy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From this point on, everytime you push to the develop branch a build will start and the live application will get updated. This is what it looks like when it is building:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1oeMFcMI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mma7560lpnoky1xhi55s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1oeMFcMI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mma7560lpnoky1xhi55s.png" alt="building" width="880" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can click on any of the phases, such as &lt;strong&gt;provision&lt;/strong&gt; to see the detailed logging of what is happening:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZSZkcTGO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/srse5v3iuhyc1htkbp96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZSZkcTGO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/srse5v3iuhyc1htkbp96.png" alt="Provision log " width="880" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When it is done building and deploying, we can see that all steps have passed and that there is a link to test out our app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wBSU8xEN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/opjg10rnh4b4d7vt3o8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wBSU8xEN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/opjg10rnh4b4d7vt3o8m.png" alt="Link to test app" width="880" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring redirects for our SPA
&lt;/h3&gt;

&lt;p&gt;If you click on the link you should see your application where you can log in and should see the main page. However, if you click on &lt;strong&gt;API&lt;/strong&gt; you will get an access denied error. For single page applications we need to add an extra setting in the Amplify console in our project related to rewrites and redirects:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m5CzK8ZJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pq6azfa71ai4x2mra7j7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m5CzK8ZJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pq6azfa71ai4x2mra7j7.png" alt="Rewrites and Redirects" width="880" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The settings are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source address: &lt;code&gt;&amp;lt;/^[^.]+$|\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|woff2|ttf|map|json)$)&lt;/code&gt;([^.]+$)/&amp;gt;.&lt;/li&gt;
&lt;li&gt;Target address: &lt;code&gt;/index.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Type: 200 (Rewrite)&lt;/li&gt;
&lt;li&gt;Country: (leave empty).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For details, refer to &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/redirects.html#redirects-for-single-page-web-apps-spa"&gt;the Amplify documentation for single page application redirects&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  (Extra) Linking a domain name to your deployment
&lt;/h3&gt;

&lt;p&gt;One extra step you could take is to link a domain name to one of your deployments. In this case we are going to create a new environment for our project, link it to the master branch and then link the domain name “theamplifyapp.com” to that deployment.&lt;/p&gt;

&lt;p&gt;To start off, I will run the following command in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evertsons-MBP:theamplifyapp evertsoncroes$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Do you want to use an existing environment? No
? Enter a name for the environment prod
? Choose your default editor: Visual Studio Code
Using default provider  awscloudformation
? Select the authentication method you want to use: AWS profile

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

? Please choose the profile you want to use default
Adding backend environment prod to AWS Amplify app: d1ekyrd95b627y
⠴ Initializing project in the cloud...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that is done we can run &lt;code&gt;amplify push&lt;/code&gt; and follow the steps we made for the dev environment.&lt;/p&gt;

&lt;p&gt;After that, we can go into the Amplify console, click on &lt;strong&gt;General&lt;/strong&gt;, scroll to the bottom and click on &lt;strong&gt;connect branch&lt;/strong&gt;. There we will select the master branch and the prod environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DjqM3bGu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dy4dvjej22vlknnq7uma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DjqM3bGu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dy4dvjej22vlknnq7uma.png" alt="prod" width="880" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we click on &lt;strong&gt;next&lt;/strong&gt; and &lt;strong&gt;save and deploy&lt;/strong&gt; and wait for the build to be successful.&lt;/p&gt;

&lt;p&gt;After that is done, we can go to &lt;a href="https://aws.amazon.com/route53/"&gt;Route53&lt;/a&gt; in the AWS console and register a domain name. Note that this will cost money! In this case I have registered the domain &lt;a href="https://www.theamplifyapp.com/home"&gt;theamplifyapp.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Back in the Amplify console, click on the &lt;strong&gt;Domain management tab&lt;/strong&gt; and on &lt;strong&gt;Add domain&lt;/strong&gt;. We should find our registered domain in the available domains and select it. Then we click on &lt;strong&gt;Configure domain&lt;/strong&gt; and set it to the master branch build:&lt;/p&gt;

&lt;p&gt;Once we click on &lt;strong&gt;Save&lt;/strong&gt; the process should get started. When it is complete, we can visit our application on our brand new domain!&lt;/p&gt;

&lt;p&gt;We made two major changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/e654ab751d8ed1d5e61fcfd1ce64b9a3ee1502c6"&gt;We’ve added Amplify hosting.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/evertson90/theamplifyapp/-/commit/5294c2bfce4ce9086d34ccd862b24d4326102f93"&gt;We’ve updated our Angular budget to 2Mb.Updated Angular budget to 2mb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Up next…
&lt;/h2&gt;

&lt;p&gt;With just a few Amplify commands and code changes we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created a cloud-native application…&lt;/li&gt;
&lt;li&gt;Protected with authentication… &lt;/li&gt;
&lt;li&gt;Backed by a GraphQL backend…&lt;/li&gt;
&lt;li&gt;With a fully configured CI/CD pipeline…&lt;/li&gt;
&lt;li&gt;Hosted on the AWS cloud…&lt;/li&gt;
&lt;li&gt;Connected through a registered domain name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each of the categories taken there are still plenty of customization options to explore. However, hopefully it has become clear how powerful Amplify can be. &lt;/p&gt;

&lt;p&gt;In the next installment of this series we will continue working on our application and we will add the Amplify Storage category. We will use this to create an image library where we can upload images.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>amplify</category>
      <category>angular</category>
    </item>
    <item>
      <title>The Amplify Series, Part 3: Why should you use AWS Amplify?</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Fri, 21 Jan 2022 13:36:11 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-amplify-series-part-3-why-should-you-use-aws-amplify-kla</link>
      <guid>https://dev.to/aws-builders/the-amplify-series-part-3-why-should-you-use-aws-amplify-kla</guid>
      <description>&lt;p&gt;&lt;strong&gt;Now that you know what AWS Amplify is and have a broad overview of how it works, we will look at several reasons you might consider using it. We will also look at scenarios where Amplify might not be the best choice. AWS Amplify is a powerful tool. However, you should always try to use the right tool for the job! Whether the right choice is Amplify or another tool.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Amplify has evolved into a useful and mature toolset for creating cloud-native web and mobile applications. Having Amplify in your developer toolbelt will increase your productivity in different scenarios. Be it developing your next application, creating a quick prototype, or learning/teaching AWS. Let’s look at why you might want to use AWS Amplify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Increase speed, reduce risk: write less hand-crafted code
&lt;/h2&gt;

&lt;p&gt;The primary reason to use AWS Amplify is that it increases your business speed and agility. This is achieved by greatly reducing the amount of code you need to write and manage to achieve cloud-native functionality. &lt;a href="https://www.luminis.eu/blog/cloud-en/the-amplify-series-part-1-what-is-aws-amplify/" rel="noopener noreferrer"&gt;In part 1&lt;/a&gt;, we already listed all of the functionality available in AWS Amplify. However, that was just a brief overview. In this section, we want to zoom in on some of the functionality and advantages that impress us the most and end up using the most in our projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;One of the most valuable categories in the Amplify suite is the &lt;a href="https://docs.amplify.aws/lib/auth/getting-started/q/platform/js/" rel="noopener noreferrer"&gt;Auth category&lt;/a&gt;. It provides us with rich authentication functionality backed by &lt;a href="https://aws.amazon.com/cognito/" rel="noopener noreferrer"&gt;AWS Cognito&lt;/a&gt;. After you use this for the first time, you will never want to write your own authentication mechanisms ever again. This category even comes with an authentication UI that you can optionally use to speed up development even more, and it is completely customizable:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FScreenshot-2020-10-21-at-15.51.19-3.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FScreenshot-2020-10-21-at-15.51.19-3.png" alt="authUI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the auth category is set up, it will create an AWS Cognito user pool to register your users. The &lt;a href="https://aws.amazon.com/cognito/" rel="noopener noreferrer"&gt;Amplify SDK&lt;/a&gt; will provide several helper functions to register, sign in and sign out your users. There is also forgot-password and email activation functionality out-of-the-box. &lt;/p&gt;

&lt;p&gt;Besides creating a user using an email and password combination, you can easily set up federated sign-in with Google, Facebook, Apple, or any provider that supports Open-ID connect and SAML.&lt;/p&gt;

&lt;p&gt;One other nice feature to notice in this category is that it will handle authentication for the other categories such as API and Storage once you have it set up. This means that you do not need to handle any token management yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL
&lt;/h3&gt;

&lt;p&gt;While the &lt;a href="https://docs.amplify.aws/lib/restapi/getting-started/q/platform/js/" rel="noopener noreferrer"&gt;REST version of the Amplify API&lt;/a&gt; category also provides a lot of functionality, including a REST API, &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt; function, and &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;DynamoDB&lt;/a&gt;, we have decided to highlight the GraphQL version as it is a prime example how much AWS Amplify can help you with.&lt;/p&gt;

&lt;p&gt;If you are familiar with GraphQL, you know that you can define your API in a GraphQL schema. What Amplify adds to this is several annotations called &lt;a href="https://docs.amplify.aws/cli-legacy/graphql-transformer/directives/" rel="noopener noreferrer"&gt;GraphQL directives&lt;/a&gt; that generate functionality. The following is an example of such a schema:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FScreenshot-2022-01-09-at-13.14.34.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FScreenshot-2022-01-09-at-13.14.34.png" alt="GraphQL_Schema"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just by creating this schema, we will get the following generated for us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;@model&lt;/strong&gt;: DynamoDB tables for Show and Review are generated.
GraphQL Query, Mutation, and Subscription client stub code for Show and Review &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/auth"&gt;@auth&lt;/a&gt;&lt;/strong&gt;: 403 http status codes when the auth rules are broken. In this example, only users that are logged in (“private”) are allowed to create, read, update and delete shows. There is also the possibility of making more complex authorization rules, such as allowing only users of a particular group to perform actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@connection&lt;/strong&gt;: References from Show to Review and vice versa&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@function&lt;/strong&gt;: GraphQL queries for “reviewScore” for “show” routes to a separate Lambda function instead of going directly to the database. In this way, you can still customize the logic in the backend instead of just building CRUD.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visit the AWS Amplify docs to find out more about &lt;a href="https://docs.amplify.aws/cli-legacy/graphql-transformer/directives/" rel="noopener noreferrer"&gt;Amplify GraphQL directives&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hosting and CI/CD
&lt;/h3&gt;

&lt;p&gt;The final part we want to highlight is the entire hosting and CI/CD that you can set up for your project in just a few commands. By simply running “amplify add hosting” and following a few instructions, you can set up an entire CI/CD pipeline for your repo that deploys the backend and hosts the frontend on a public URL.&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FScreenshot-2022-01-14-at-14.47.45.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FScreenshot-2022-01-14-at-14.47.45.png" alt="CICD"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every part of the pipeline is customizable, and setting up redirects and HTTPS with a domain name is a piece of cake. You can even spin up different environments per branch to test them. &lt;/p&gt;

&lt;h2&gt;
  
  
  Empower front-end engineers: JavaScript front- and back-ends
&lt;/h2&gt;

&lt;p&gt;We have noticed while using Amplify that it has allowed our front-end engineers to expand their reach in development by being able to contribute to the backend since it is also written in JavaScript. &lt;/p&gt;

&lt;p&gt;While Amplify supports Lambda functions in other languages such as Java, choosing for JavaScript while also building your frontend in JavaScript means that you develop your entire application with the one language all frontend engineers know. This will allow Frontend engineers to pick up whole vertical issues in your project to handle changes to the entire stack.&lt;/p&gt;

&lt;p&gt;Note that you can choose to ignore JavaScript completely and build an Android mobile app with Java Lambda’s in the backend in Amplify. The advantage mentioned here does not hold in this case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Innovate at speed: prototype to reduce time-to-value
&lt;/h2&gt;

&lt;p&gt;Prototyping is one of the best ways to determine if your product/solution will solve the problems you are trying to solve. If you can test your ideas and get feedback as fast as possible, you will innovate quickly.&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FProduct-feedback-productboard-e1621926658567.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FProduct-feedback-productboard-e1621926658567.png" alt="innovate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While using paper prototypes or interactive mockups is a fast way to get user feedback, you can only reach the number of people you have time to test with. If you want to get an actual prototype up and running in no time so that it can be shared via social media and tested by several users, then Amplify can be used to skip all of the setup and let you focus on building the prototype. &lt;/p&gt;

&lt;p&gt;If you combine this with analytics and surveys, you will get information from a more extensive user base should that be necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accelerate cloud proficiency: leverage AWS best practices and patterns
&lt;/h2&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FScreenshot-2022-01-20-at-15.24.35.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2FScreenshot-2022-01-20-at-15.24.35.png" alt="academy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS has almost 200 services available at the moment. This can be very intimidating for someone trying to start their cloud journey. AWS Amplify only uses a subset of these services and guides you in creating and configuring the services. &lt;/p&gt;

&lt;p&gt;One of the strengths of Amplify is that you can create an entire cloud-native application without having to know what is happening under the hood. However, learning what happens under the hood of AWS Amplify is an excellent way to start learning about cloud computing since the scope of services is much smaller, and you can take it step-by-step based on the categories.&lt;/p&gt;

&lt;p&gt;When you are done building your first cloud-native Amplify application and know what is happening in the background, you will have a sound base of knowledge and experience to build upon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep options open: extend Amplify with CDK
&lt;/h2&gt;

&lt;p&gt;One of the most common questions is when we should use Amplify or CDK when starting up a cloud-native AWS project. By now, you have an idea of what AWS Amplify is. If you have never heard of CDK before, our colleague wrote an introduction &lt;a href="https://www.luminis.eu/blog/creating-your-serverless-web-application-using-aws-cdk-part-1/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; about it. &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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2Fcdk-logo6-1260x476-1.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2022%2F01%2Fcdk-logo6-1260x476-1.png" alt="cdk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The primary objective of CDK is to allow developers to write infrastructure-as-code in a programmer-friendly language instead of CloudFormation. Amplify wants to abstract as much as possible from the backend infrastructure and Cloudformation, while CDK has nothing to do with the frontend. Since they have different goals, they have different scenarios where you should pick one over the other. &lt;/p&gt;

&lt;p&gt;However, since the introduction of Amplify Extensibility, your options have become somewhat simpler. You can build your frontend using Amplify, extend it with CDK and even export your Amplify project as a CloudFormation templates to use in your existing CDK projects. ​​Picking one over the other is no longer an binary exercise. Instead, we should explore when to leverage which option.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you not use AWS Amplify?
&lt;/h2&gt;

&lt;p&gt;Before Amplify Extensibility, which allows you to use AWS CDK to define AWS services you want to use in your Amplify application that is not present in the several Amplify categories, my advice was always:&lt;/p&gt;

&lt;p&gt;“If you can map your main success use cases to the Amplify categories, then you should use Amplify. Otherwise, you should avoid using it.”&lt;/p&gt;

&lt;p&gt;However, this advice has become obsolete since we can now use any AWS service in our Amplify applications thanks to Extensibility. There are still some scenarios where you shouldn’t use Amplify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Non-AWS or multi-cloud projects&lt;/strong&gt;: AWS Amplify can only target the AWS cloud platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-fullstack projects&lt;/strong&gt;: if you creating pure front-end or back-end projects, Amplify might not be the most effective choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misfit with existing architecture&lt;/strong&gt;: if fitting an AWS Amplify project into your existing landscape would require massive effort or does not align with existing architectural principles, it might not be the most logical choice. Maybe you should start with convincing your enterprise architect of Amplify's value first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No clear advantage&lt;/strong&gt;: if large parts of Amplify's functionality are already in place (e.g., slick build and deploy pipelines, or a library of similar building blocks), then the added value of Amplify might not be so great. But that's good news, right?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Up next…
&lt;/h2&gt;

&lt;p&gt;Hopefully, this blog has given you even more reason to start working with AWS Amplify. Now that we have the high-overview and background information out of the way, it is time to get our hands dirty! In the following blog posts, we will be creating an AWS Amplify application, and we will take a closer look at several Amplify categories.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>amplify</category>
    </item>
    <item>
      <title>The Amplify Series, Part 2: How does AWS Amplify work?</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Wed, 22 Dec 2021 10:42:59 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-amplify-series-part-2-how-does-aws-amplify-work-40bf</link>
      <guid>https://dev.to/aws-builders/the-amplify-series-part-2-how-does-aws-amplify-work-40bf</guid>
      <description>&lt;p&gt;&lt;strong&gt;In the previous blog, we got a high-level overview of AWS Amplify. In this article I will examine how AWS Amplify works and explain the "magic" behind the scenes. While one of the advantages of Amplify is that you don’t need to know any of this to develop a cloud-native application, it is essential to understand its inner working if you want to do more than just quick scaffolding.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have worked on several Amplify projects without digging deep to get effective. So if you want to get to just building a cloud-native application, you can skip this one. However, for those of you who stay, we will be covering the foundation on which Amplify is built upon, the concept and technologies used, and describe what actions happen in the background when you run specific commands in the Amplify CLI. It is important to note that the examples we give in these articles are very web app-oriented to keep the articles shorter. However, Amplify also supports mobile development with Android, iOS, and hybrid frameworks, and for the most part, the information here is still relevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure as Code (IaC)
&lt;/h2&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Finfrastructureascode_600x300-3.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Finfrastructureascode_600x300-3.png" alt="IaC"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To explain how AWS Amplify works, we first need to discuss the concept of "Infrastructure as Code".&lt;/p&gt;

&lt;p&gt;Before IaC, we would probably log in on a cloud console or web portal of some sort and start configuring our infrastructure by clicking around. Additionally, the cloud provider might have a CLI or another tool to help us provision our infrastructure. After many clicking and cli commands, we finally have our first version up and running.&lt;/p&gt;

&lt;p&gt;After testing this setup, we are ready to go to production. Here is where we encounter one of the first problems that IaC solves: How do we replicate the test environment we provisioned and tested? One option is to follow all the steps we did to create a replica. However, as you can already tell, this is very error-prone, and missing even one step might make a big difference.&lt;/p&gt;

&lt;p&gt;With IaC, we solve this issue by describing these steps we made into some form of code. Once we have this in code, we can easily create a copy of the infrastructure using a provider that understands the code and can provide us with resources. There are also other benefits to IaC. To learn more about it, check out this blog &lt;a href="https://martinfowler.com/bliki/InfrastructureAsCode.html" rel="noopener noreferrer"&gt;post&lt;/a&gt; or this short &lt;a href="https://www.youtube.com/watch?v=zWw2wuiKd5o" rel="noopener noreferrer"&gt;video&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Many tools provide IaC functionality, such as &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;, &lt;a href="https://puppet.com/" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt;, &lt;a href="https://cloud.google.com/deployment-manager/docs" rel="noopener noreferrer"&gt;Google Cloud Deployment Manager&lt;/a&gt;, and Azure resource manager, to name a few. When it comes to AWS, we use AWS Cloudformation.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS CloudFormation
&lt;/h3&gt;

&lt;p&gt;AWS Cloudformation is the Infrastructure as Code solution provided by Amplify. We can define our cloud environment, meaning the resources and their dependencies, in so-called "CloudFormation templates". These templates are written in JSON or YAML. CloudFormation can take such a template and create a CloudFormation Stack, a set of related resources. For CloudFormation to find the template, it must be uploaded to &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;S3&lt;/a&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Fcreate-stack-diagram.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Fcreate-stack-diagram.png" alt="CloudFormation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the good news is that Amplify will do all of the steps above for you. You just need to answer a few questions about what kind of functionality you need for your cloud-native app. Amplify will generate CloudFormation templates based on these answers, deploy them to S3 and use CloudFormation to create, update or delete the stack. We can log in to AWS and go to CloudFormations to look at our stacks at any time. Here is an example of a CloudFormation template generated by Amplify:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-10.48.00.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-10.48.00.png" alt="json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some items in the example above are minimized, but we can see that we are declaring an API resource with a path "/players" and "/achievements". The entire file is 2200 lines long, only for the Amplify API category. Learning to do this by hand is a lot of work and error-prone. That is why several tools generate CloudFormation, such as Amplify, &lt;a href="https://aws.amazon.com/cdk/#:~:text=The%20AWS%20Cloud%20Development%20Kit,resources%20using%20familiar%20programming%20languages.&amp;amp;text=AWS%20CDK%20uses%20the%20familiarity,languages%20for%20modeling%20your%20applications." rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt;, and &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html" rel="noopener noreferrer"&gt;AWS SAM&lt;/a&gt;. Which tool you should use will be covered in the next blog. For now, it is essential to understand the basics of CloudFormation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring AWS Amplify CLI
&lt;/h2&gt;

&lt;p&gt;Before you start using the Amplify CLI, you need to configure it to know which account it can use to create AWS resources using S3 and CloudFormation. You can do this by running "amplify configure" in the command-line terminal. This will open a browser window for you to log in to the AWS console and walk you through the steps of creating an IAM account that has enough access to do everything that the Amplify CLI needs to do.  At the end of all of the steps, you must give the profile a name. This name will be used in the CLI moving forward with Amplify, and at some points, the CLI will ask you which profile you want to use for which commands.&lt;/p&gt;

&lt;p&gt;Once this is done, the CLI will use these credentials to communicate with AWS to generate cloud resources via CloudFormation. Here is a diagram showing the actions that happen when you first configure your CLI and how the credentials are used to access CloudFormation and S3:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Famplify-add-auth.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Famplify-add-auth.png" alt="configuring"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialising an Amplify project
&lt;/h2&gt;

&lt;p&gt;When your CLI is configured, you can start creating your Amplify project. This is done by navigating to the root of your project and running "amplify init". The CLI will ask several questions, one of which is related to the credentials we configured. Once we select the correct profile, Amplify will work and perform several actions to create the Amplify project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changes in AWS
&lt;/h3&gt;

&lt;p&gt;After running the amplify init command, the CLI will create an S3 bucket to store the CloudFormation templates, and it will create a CloudFormation stack that will point to the S3 bucket. Here is an example of a stack built after running amplify init:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-11.47.48.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-11.47.48.png" alt="cfinit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we can see, the DeploymentBucket is present, which will hold our CloudFormation templates. There are also two IAM roles created for the project. One for authorized users and one for unauthorized users. Once we begin adding more Amplify functionality and setting up authorization rules, these IAM roles will be updated to match what we configure via Amplify.&lt;/p&gt;

&lt;p&gt;Another action that is taken by the CLI when running Amplify init is the creation of an Amplify project in the Amplify console:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-11.52.52.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-11.52.52.png" alt="consoleinit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will become relevant once we start looking into hosting and adding CI/CD for our project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changes in the project repository
&lt;/h3&gt;

&lt;p&gt;The CLI also generates and updates several files in our repository, reflecting our choices made in the Amplify CLI. An extensive list of files generated by Amplify can be found &lt;a href="https://docs.amplify.aws/cli/reference/files/" rel="noopener noreferrer"&gt;here&lt;/a&gt;; however, here is a summary of the files changed if you init for an Angular application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;amplify/cli.json:&lt;/strong&gt; Feature flags for the CLI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/README.MD:&lt;/strong&gt; A list of links to helpful resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/team-provider-info.json:&lt;/strong&gt; Information needed so that other team members can use Amplify. This file can be added to git if running a private application. However, it is advised to remove it or add it to the gitignore if it is a public project. Note: Even if someone gets the information in this file, they would still need the correct credentials to use the information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/.config/..:&lt;/strong&gt; Configuration options set during the "amplify configure" command&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/backend/backend-config.json:&lt;/strong&gt; Information about resources and how they connect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/hooks/...:&lt;/strong&gt; Command hooks that can be used during the Amplify lifecycle, such as "pre-push", "post-add-function" etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;src/aws-exports.js:&lt;/strong&gt; This file only exists for JS projects and is used by the JS libraries to know which resources they should communicate with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.gitignore:&lt;/strong&gt; This file is updated by the Amplify CLI to ignore certain generated files and should not be stored in Git.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These changes should all be stored in Git to have a good starting point for your project. In general, changes made by the Amplify CLI should be committed to Git because those are the changes in your Infrastructure as Code setup that Amplify is updating for you based on your CLI choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding backend functionality to project
&lt;/h2&gt;

&lt;p&gt;Once the project is initialized with Amplify, we can add backend functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend functionality
&lt;/h3&gt;

&lt;p&gt;When we talk about backend functionality, we mean AWS Cloud services that will be generated by the CLI and consumed by our frontend application. For example, adding the Amplify (REST) API category will make use of &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;AWS API Gateway&lt;/a&gt;. Adding the Amplify Auth category will make use &lt;a href="https://aws.amazon.com/cognito/" rel="noopener noreferrer"&gt;AWS Cognito&lt;/a&gt;. Here is a diagram that shows a deployment of a REST backend created with AWS Amplify:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Fstack.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Fstack.png" alt="backendfunc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can see that the CLI generates a set of AWS resources. Each resource is part of an Amplify category. On the right side we can see that our frontend application will make use of the Amplify SDK to use these resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the CLI
&lt;/h3&gt;

&lt;p&gt;We can add backend functionality by running "amplify add &amp;lt;&amp;gt;" in the CLI. In this case, if I run "amplify add auth" I will get the following questions:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-14.26.45.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-14.26.45.png" alt="backendfunccli"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that this is one of the more straightforward functionalities to add. When adding API, for example, you will have to answer many more questions. Once we are done running this command, there will be changes in our project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;amplify/team-provider-info.json:&lt;/strong&gt; It will now contain a "categories" object containing the "auth" category with identifiers of the resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/backend/backend-config.json:&lt;/strong&gt; It will now contain an object called "auth" with information related to the AWS service and how it needs to be configured, in this case, AWS Cognito.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/backend/auth/&amp;lt;&amp;gt;/cli-inputs.json:&lt;/strong&gt; This file contains the choices you made via the CLI for the auth category. This is the file that will be committed in Git with the state of the auth category for this project. This is much better than adding the CloudFormation templates themselves to Git, as that would make fixing merge conflicts harder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/backend/auth/&amp;lt;&amp;gt;/build/&amp;lt;&amp;gt;-cloudformation-template.json:&lt;/strong&gt; This is the actual CloudFormation template which will be used. This file is already added to the .gitignore by Amplify and will be regenerated when there are relevant changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amplify/backend/auth/&amp;lt;&amp;gt;/build/parameters.json:&lt;/strong&gt; Contains environment specific parameters for the CloudFormation template. It should also be ignored in Git.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is important to note that we have not yet done anything in AWS. These changes all remain local until the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pushing backend changes to AWS
&lt;/h2&gt;

&lt;p&gt;After adding and updating functionality that we need in our application via the CLI, we can push the local changes to AWS running "amplify push". This is where we will be making changes in AWS and generating the resources that will be needed to support the functionality we want. With this command, we will be generating several resources into our CloudFormation stack by just answering questions in a CLI.&lt;/p&gt;

&lt;p&gt;When running the command, the CLI will compare the state of the infrastructure in the S3 deployment bucket and the local changes and give a preview of the changes that will happen if the push continues. In the case of the auth category we added in the last section; the preview would look as follows:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-14.43.40.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-14.43.40.png" alt="create"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that new resources will be created for the auth category. If we choose "yes" then the CLI will update the S3 bucket with the new CloudFormation templates and use CloudFormation to create/update the stack for our Amplify project. One other important thing to note is that once the CLI starts pushing the changes to AWS, it will also update the aws-exports.js file with the new references so that the frontend libraries can make use of this.&lt;/p&gt;

&lt;p&gt;At any point, we can run amplify status to see the state of your current changes. After pushing the changes and running amplify status, we can see that we no longer have any changes:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-14.53.36.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-14.53.36.png" alt="nochange"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also check out our CloudFormation stack to see all the new resources created.&lt;/p&gt;

&lt;p&gt;This is the primary development cycle for working with AWS Amplify using the CLI. In short, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make local changes by running "amplify add &amp;lt;&amp;gt;" or "amplify update &amp;lt;&amp;gt;"&lt;/li&gt;
&lt;li&gt;We run "amplify push" and check if the changes make sense&lt;/li&gt;
&lt;li&gt;We click on "Yes" to let the push continue&lt;/li&gt;
&lt;li&gt;(Optional) We check CloudFormation to see if the stack is in the "CREATE_COMPLETE" or "UPDATE_COMPLETE" status&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Connecting backend and frontend
&lt;/h2&gt;

&lt;p&gt;Besides the CLI, Amplify also provides libraries so that your frontend application can make use of the generated resources in an easy way. We need to inform the frontend libraries of the backend resources we have generated and how to find them.&lt;/p&gt;

&lt;p&gt;To get this working, we first need to install the libraries. You can find tutorials for all of the integrations &lt;a href="https://docs.amplify.aws/start/q/integration/js/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For Angular, we can run:&lt;/p&gt;

&lt;p&gt;"npm install --save aws-amplify @aws-amplify/&lt;a href="mailto:ui-angular@1.x.x"&gt;ui-angular@1.x.x&lt;/a&gt; "&lt;/p&gt;

&lt;p&gt;Furthermore, we need to configure the Amplify library with the aws-exports.js to know which resources it can use for which category of Amplify functionality. This is usually done somewhere in your frontend code in the earliest part of the app lifecycle. For Angular, we add it in the src/main.ts:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-15.04.11.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-17-at-15.04.11.png" alt="maints"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this is done, we can run functionality such as "Amplify.Auth.register(userInfo)" to register a new user. We will look into more concrete examples in the following blogs.&lt;/p&gt;

&lt;p&gt;The complete flow of adding Amplify functionality and using it can be illustrated in the following way:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Famplify-add-auth-2.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Famplify-add-auth-2.png" alt="fullfunc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Up next...
&lt;/h2&gt;

&lt;p&gt;In this blog we looked at how AWS Amplify works behind the scenes. If you are considering using Amplify for real life projects, this information will help you have the confidence to make changes and fix issues. In the next blog, we will be looking at why you should consider using AWS Amplify, when to use it, when not to use it and when you should look at alternatives. See you in the next one!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>amplify</category>
    </item>
    <item>
      <title>The Amplify Series, Part 1: What is AWS Amplify?</title>
      <dc:creator>Evertson Croes</dc:creator>
      <pubDate>Sun, 12 Dec 2021 20:26:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-amplify-series-part-1-what-is-aws-amplify-5eko</link>
      <guid>https://dev.to/aws-builders/the-amplify-series-part-1-what-is-aws-amplify-5eko</guid>
      <description>&lt;p&gt;&lt;strong&gt;The way we develop software is rapidly and increasingly changing with the world around us. One of the more exciting developments is the advent of serverless technology, which, when applied correctly, opens doors to organizational agility and speed — and thus innovation and happy users.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the quickest ways to get familiar with this way of working is using AWS Amplify, a set of powerful tools and features that can help you quickly and easily build cloud-powered, extensible, and scalable full-stack applications.&lt;/p&gt;

&lt;p&gt;Amplify has selected a subset of all AWS services and default settings to help you quickly get started with building your Cloud Native application and thus provide value at a higher pace. This might seem like you are losing control over your application and that you will be limited somehow; however, with AWS Amplify, you can still override any defaults and access any AWS service if you need to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blog series
&lt;/h2&gt;

&lt;p&gt;In this blog series I will introduce AWS Amplify, explain why you would want to use it, how it works, and give detailed examples of the several categories of functionality available out-of-the-box with AWS Amplify.&lt;/p&gt;

&lt;p&gt;The entire series will cover the following topics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is AWS Amplify?&lt;/li&gt;
&lt;li&gt;How does AWS Amplify work?&lt;/li&gt;
&lt;li&gt;Why should you use AWS Amplify?&lt;/li&gt;
&lt;li&gt;Developing and deploying a Cloud Native application with AWS Amplify&lt;/li&gt;
&lt;li&gt;Uploading and retrieving images with Amplify Storage&lt;/li&gt;
&lt;li&gt;Using the power of AI and Machine Learning with Amplify Predictions&lt;/li&gt;
&lt;li&gt;Track app usage with Amplify Analytics&lt;/li&gt;
&lt;li&gt;Location functionality with Amplify Geo&lt;/li&gt;
&lt;li&gt;Chatbots with Amplify Interactions&lt;/li&gt;
&lt;li&gt;Do everything with AWS Amplify! (Extensibility)&lt;/li&gt;
&lt;li&gt;Low Code with Amplify Studio&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We hope you will enjoy this series and extend your AWS Amplify knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AWS Amplify?
&lt;/h2&gt;

&lt;p&gt;In this blog, we will give a high-level overview of the functionalities and tools provided by Amplify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Functionality categories
&lt;/h3&gt;

&lt;p&gt;With Amplify, we are more concerned with what we want to build instead of how it will work with AWS. Amplify offers the possibility to generate AWS services based on a set of functionality categories. At the moment of writing, these categories are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; Fast and easy authentication and authorization out-of-the-box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API:&lt;/strong&gt; Create and use a REST or GraphQL API as a gateway to other services in AWS, such as Lambda functions or databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; Used for storing and retrieving user-generated content on AWS such as photos, videos, or other files&lt;/li&gt;
&lt;li&gt;Geo: Location-based functionality such as maps and location search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; Hosting your app on AWS and setting up a CI/CD pipeline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactions:&lt;/strong&gt; Create chatbots for your application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PubSub:&lt;/strong&gt; Connectivity with cloud-based message-oriented middleware&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DataStore:&lt;/strong&gt; Use on-device persistent storage to be able to use your application when offline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functions:&lt;/strong&gt; Create Lambda functions that are linked to an API or create CRON jobs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics:&lt;/strong&gt; Add tracking of user behavior of your apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI/ML Predictions:&lt;/strong&gt; Use AI/ML to add functionality such as text translation, speech-to-text, and entity recognition in images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push Notifications:&lt;/strong&gt; Send messages to your customers to improve engagement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility:&lt;/strong&gt; Leverage any other AWS service that is not covered by the categories above by using AWS CDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, the functionality categories already cover many everyday use cases for web and mobile apps while still offering flexibility by leveraging CDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Fhal-gatewood-v7WyjiyXNr4-unsplash.jpg" 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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2Fhal-gatewood-v7WyjiyXNr4-unsplash.jpg" alt="Tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS Amplify consists of 4 tools that help you develop cloud-native applications. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLI:&lt;/strong&gt; The Command-line interface that you can use to initialize your Amplify project and generate AWS resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Libraries/SDK:&lt;/strong&gt; The set of libraries used by your frontend of any kind to more easily be able to communicate with the AWS resources created with Amplify&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Console:&lt;/strong&gt; The AWS service where you can keep track of your Amplify projects and configure many settings related to the deployment and hosting of your AWS Amplify app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amplify Studio:&lt;/strong&gt; Amplify Studio allows you to configure your AWS resources using the console and visual modeling. Everything you change here is still compatible with the CLI, and you can even use the CLI to download the resources you designed in the studio.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  CLI
&lt;/h4&gt;

&lt;p&gt;With the command-line interface (CLI), you can run a few commands, you will be asked specific questions about the functionality you want to achieve, and it will generate the AWS resources based on your answers. Here is an example of such an exchange with the Amplify CLI:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F11%2FScreenshot-2021-11-26-at-21.17.45.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F11%2FScreenshot-2021-11-26-at-21.17.45.png" alt="CLI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, we add a REST API to my project using the Amplify CLI. Depending on what functionality you want to add, you will need to answer different questions.&lt;/p&gt;

&lt;p&gt;In the following blogs in this series, we will be using the CLI a lot and show you everything from starting a project to adding several functionalities. For now, it is important that you have a broad idea of how the CLI works.&lt;/p&gt;

&lt;h4&gt;
  
  
  Libraries (SDK)
&lt;/h4&gt;

&lt;p&gt;We can use the Amplify libraries to connect our app to AWS resources we created using the CLI. There are libraries for all of the most popular frameworks to develop web and mobile apps:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-01-at-22.15.44.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-01-at-22.15.44.png" alt="SDK"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The combination of the CLI and libraries makes it possible for your app to use the AWS resources instantly, without knowing the IP or ARN for every resource you created. Here is an example of making a REST call to an endpoint created with the AWS Amplify in JavaScript:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-01-at-22.27.49.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-01-at-22.27.49.png" alt="Adding API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are helper functions for most functionality categories covered by Amplify that help you use the AWS resources you have created via the CLI.&lt;/p&gt;

&lt;h4&gt;
  
  
  Console
&lt;/h4&gt;

&lt;p&gt;Just like services such as API Gateway or AWS Lambda, AWS Amplify is can now also be considered a first-class citizen and can be found as a service in the AWS console. Here is an example of what it looks like when you have active projects:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-02-at-10.23.31.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-02-at-10.23.31.png" alt="Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following is a list of things you can do on the Amplify console:&lt;/p&gt;

&lt;h5&gt;
  
  
  CI/CD Pipeline
&lt;/h5&gt;

&lt;p&gt;You can set up a CI/CD pipeline via the Amplify CLI and track the progress via the Amplify Console. Here is a screenshot of what it looks like:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-02-at-10.29.50.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-02-at-10.29.50.png" alt="Pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default CI/CD pipeline setup has these four steps. However, it is fully configurable either in the Amplify console or by adding a .yml file in the root of your Amplify project. If the project builds and is deployed successfully, you will even see a preview of your app, and you will be provided with a link to the hosted app.&lt;/p&gt;

&lt;h5&gt;
  
  
  Connect Git Branch
&lt;/h5&gt;

&lt;p&gt;You can connect git branches from your projects to an AWS Amplify environment you have created with the Amplify CLI. Once connected, your app can be built every time you push to that branch, and the frontend and backend environment it is related to will be deployed.&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-07-at-14.04.36.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-07-at-14.04.36.png" alt="Connect git repo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Domain management
&lt;/h4&gt;

&lt;p&gt;By default, you will get a cloudfront.net URL on which your application is hosted when you first start. However, in the Amplify Console, linking a domain you have registered to a specific git branch of your project is possible. In the case shown above, every time something is merged to develop, a build is run, and once it is deployed, it is available at &lt;a href="https://cloudthegame.com" rel="noopener noreferrer"&gt;https://cloudthegame.com&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Configure build settings
&lt;/h5&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-07-at-14.08.38.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-07-at-14.08.38.png" alt="Build settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can change the build steps in the console as previously mentioned. You can configure custom webhooks to trigger builds for specific branches, and you can configure the settings for the Amazon Linux container that will run the build. Setting up environment variables is also possible.&lt;/p&gt;

&lt;h5&gt;
  
  
  View metrics
&lt;/h5&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-07-at-14.09.37.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-07-at-14.09.37.png" alt="Metrics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While services created with the Amplify CLI, such as API Gateway and Lambda, have their metrics, the Amplify console shows you metrics related to your hosted application. You can see metrics such as viewer requests, bytes downloaded, and errors.&lt;/p&gt;

&lt;h4&gt;
  
  
  Amplify Studio
&lt;/h4&gt;

&lt;p&gt;The Amplify Studio allows you to achieve the same functionality you could using the CLI. The difference is that instead of answering questions in a CLI, you are clicking in the console. You will see that all functionality categories have tabs that you can configure. Once you are done configuring your backend, you can "pull" the backend to your local Amplify project and start using it in your application in the same way you would if you generated the backend using the CLI.&lt;/p&gt;

&lt;p&gt;Here is a screenshot of the studio where we designed a data model for our pizza restaurant:&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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-03-at-10.37.56.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%2Fwww.luminis.eu%2Fwp-content%2Fuploads%2F2021%2F12%2FScreenshot-2021-12-03-at-10.37.56.png" alt="Amplify studio"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is also a set of functionalities that is exclusive to the Amplify Studio, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI Library:&lt;/strong&gt; You can make component UI designs in Figma and generate ReactJS component code. You can even link the components to backend logic in the studio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data and files:&lt;/strong&gt; You can manage your database (CMS), files, and user/groups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; You can give users access to your Amplify studio environment without creating an AWS account. This can be useful if you have clients who want to manage the data for their application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, Amplify Studio aims to be a visual development environment. While it is low-code, it is still backed by human-readable code and AWS CloudFormation, the infrastructure-as-code solution on AWS. We will talk more about CloudFormation in the next blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up next...
&lt;/h2&gt;

&lt;p&gt;We hope this blog post has given you a clear overview of Amplify and has gotten you excited and interested in learning more. We can achieve a lot with Amplify out-of-the-box to create a cloud-native application using the most popular Javascript and hybrid mobile frameworks and native Android and iOS. In the next blog, we will look at how AWS Amplify works to achieve all of this functionality. In the subsequent blogs, we will be using Amplify to develop and deploy a cloud-native application. See you there!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>amplify</category>
    </item>
  </channel>
</rss>
