<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">

<channel>
	<title>Front-End Web &amp; Mobile</title>
	<atom:link href="https://aws.amazon.com/blogs/mobile/feed/" rel="self" type="application/rss+xml"/>
	<link>https://aws.amazon.com/blogs/mobile/</link>
	<description/>
	<lastBuildDate>Thu, 09 Oct 2025 11:55:14 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Unlocking deeper insights with enhanced monitoring in AWS AppSync</title>
		<link>https://aws.amazon.com/blogs/mobile/unlocking-deeper-insights-with-enhanced-monitoring-in-aws-appsync/</link>
					
		
		<dc:creator><![CDATA[Sagar Suri]]></dc:creator>
		<pubDate>Thu, 09 Oct 2025 11:55:13 +0000</pubDate>
				<category><![CDATA[Amazon CloudWatch]]></category>
		<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Management Tools]]></category>
		<category><![CDATA[AppSync]]></category>
		<category><![CDATA[CloudWatch]]></category>
		<guid isPermaLink="false">cd10bef3fc1614ece88985eeef22770e63c36a43</guid>

					<description>In today’s digital landscape, how well your APIs work is important for a good user experience. Monitoring your APIs isn’t just smart—it’s needed to keep your apps running well. AWS AppSync, a managed GraphQL service, is a popular choice for developers building scalable, real-time applications. With the introduction of Enhanced Monitoring, AppSync now provides deeper […]</description>
										<content:encoded>&lt;p&gt;In today’s digital landscape, how well your APIs work is important for a good user experience. Monitoring your APIs isn’t just smart—it’s needed to keep your apps running well.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/appsync/" target="_blank" rel="noopener"&gt;AWS AppSync&lt;/a&gt;, a managed &lt;a href="https://graphql.org/" target="_blank" rel="noopener"&gt;GraphQL&lt;/a&gt; service, is a popular choice for developers building scalable, real-time applications. With the introduction of &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/02/aws-appsync-amazon-cloudwatch-metrics-enhanced-monitoring/" target="_blank" rel="noopener"&gt;Enhanced Monitoring&lt;/a&gt;, AppSync now provides deeper insights and more detailed metrics. In this blog post, we’ll show how this feature can help you monitor and optimise your APIs more effectively. We’ll also compare it to current monitoring tools, guide you through a sample API implementation, and break down the associated costs.&lt;/p&gt; 
&lt;h2&gt;AWS AppSync overview&lt;/h2&gt; 
&lt;p&gt;AWS AppSync simplifies the process of building and deploying secure, high-performance APIs. It’s a managed service for GraphQL and Pub/Sub APIs that abstracts away the complexities of infrastructure, allowing you to focus on what matters—building innovative applications.&lt;/p&gt; 
&lt;p&gt;With AppSync, you can create a unified data graph that brings together data from multiple sources. Whether you’re synchronizing real-time data, enabling offline access, or managing conflict resolution, AppSync provides the tools to make it happen. Its built-in security features and serverless architecture mean you can deploy APIs that are both robust and secure, without worrying about the underlying infrastructure.&lt;/p&gt; 
&lt;h2&gt;The importance of monitoring in API management&lt;/h2&gt; 
&lt;p&gt;Monitoring is a critical aspect of API management. It ensures that APIs are performing optimally, meeting service level objectives, and maintaining security standards. By &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/operational-excellence-pillar/ops_evolve_ops_metrics_review.html" target="_blank" rel="noopener"&gt;regularly keeping a close eye on key metrics&lt;/a&gt;, you ensure that your APIs meet your service-level objectives (SLOs) and comply with security standards, ensuring a smooth and reliable experience. When you monitor metrics like latency, error rates, and request counts, you can detect anomalies early, troubleshoot root causes, and prevent small issues from escalating into user-facing problems. This approach of using telemetry from metrics to take proactive action aligns with best practices like &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/operational-excellence-pillar/ops_operations_health_review_ops_metrics_prioritize_improvement.html" target="_blank" rel="noopener"&gt;OPS09-BP03&lt;/a&gt; and is essential for the long-term reliability and scalability of your APIs.&lt;/p&gt; 
&lt;h2&gt;Understanding existing monitoring in AWS AppSync&lt;/h2&gt; 
&lt;h3&gt;Basic monitoring with AWS CloudWatch&lt;/h3&gt; 
&lt;p&gt;AWS AppSync integrates with &lt;a href="https://docs.aws.amazon.com/cloudwatch/" target="_blank" rel="noopener"&gt;Amazon CloudWatch&lt;/a&gt;, providing essential metrics that offer a high-level overview of your API’s performance. These include request latency, counts and error rates and more.&lt;/p&gt; 
&lt;p&gt;AppSync’s real-time subscriptions are a powerful feature, enabling push notifications and live updates. To monitor these, Amazon CloudWatch offers metrics such as the number of active real-time subscriptions, number of messages sent via subscription and more.&lt;/p&gt; 
&lt;p&gt;Check the &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/monitoring.html" target="_blank" rel="noopener"&gt;full list of built-in metrics&lt;/a&gt; to identify overall trends and spikes at a glance.&lt;/p&gt; 
&lt;h2&gt;Introducing enhanced monitoring in AWS AppSync&lt;/h2&gt; 
&lt;p&gt;As applications become more complex and user demands grow, there is a growing need for more granular insights. Developers increasingly require additional metrics to pinpoint the precise stages of request processing that might contribute to latency or errors.&lt;/p&gt; 
&lt;p&gt;For instance, understanding the performance at the resolver level or tracking how individual data sources perform under load can be critical for fine-tuning and optimizing API operations. These detailed metrics empower developers to not only detect issues but also to understand the root causes, enabling more targeted and effective optimizations.&lt;/p&gt; 
&lt;p&gt;On February 12, 2024, AWS announced &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/02/aws-appsync-amazon-cloudwatch-metrics-enhanced-monitoring/" target="_blank" rel="noopener"&gt;Enhanced Monitoring for AWS AppSync&lt;/a&gt;, Enhanced Monitoring in AWS AppSync gives developers access to a more detailed view of their API’s performance. This feature provides real-time insights and granular metrics, allowing you to drill down into specific operations, resolvers, and data sources.&lt;/p&gt; 
&lt;h3&gt;Key features of enhanced monitoring&lt;/h3&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Resolver-Level Metrics:&lt;/strong&gt; Track the performance of individual resolvers to pinpoint latency and errors. This allows for targeted optimizations, ensuring your API performs efficiently at every stage.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Data Source Monitoring:&lt;/strong&gt; Gain detailed insights into how your data sources—whether &lt;a href="https://aws.amazon.com/dynamodb/" target="_blank" rel="noopener"&gt;Amazon DynamoDB&lt;/a&gt;, &lt;a href="https://aws.amazon.com/rds/" target="_blank" rel="noopener"&gt;Amazon RDS&lt;/a&gt;, or &lt;a href="https://aws.amazon.com/lambda/" target="_blank" rel="noopener"&gt;AWS Lambda&lt;/a&gt;—are performing. Identifying performance bottlenecks at the source level becomes easier, allowing you to take corrective action quickly.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Operational metrics:&lt;/strong&gt; Get detailed metrics on GraphQL operation-level (Mutations &amp;amp; Queries) metrics.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;In total, 10 new CloudWatch metrics were added under the &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/monitoring.html#enhanced-metrics" target="_blank" rel="noopener"&gt;“Enhanced metrics” section&lt;/a&gt; of the official documentation.&lt;/p&gt; 
&lt;h2&gt;Sample API implementation with enhanced monitoring&lt;/h2&gt; 
&lt;p&gt;To demonstrate the power of Enhanced Monitoring, let’s walk through a sample AWS AppSync API implementation. This API will include three queries and two mutations, connected to four different data sources: Amazon DynamoDB, AWS Lambda, and Amazon OpenSearch Service.&lt;/p&gt; 
&lt;p&gt;We will create an API that supports the following operations:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Queries:&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code class="inline"&gt;getUser&lt;/code&gt;: Retrieve user details by ID.&lt;/li&gt; 
 &lt;li&gt;&lt;code class="inline"&gt;listUsers&lt;/code&gt;: List all users.&lt;/li&gt; 
 &lt;li&gt;&lt;code class="inline"&gt;searchUsers&lt;/code&gt;: Search users by a specific attribute.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;Mutations:&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code class="inline"&gt;createUser&lt;/code&gt;: Add a new user.&lt;/li&gt; 
 &lt;li&gt;&lt;code class="inline"&gt;updateUser&lt;/code&gt;: Update existing user details.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;Data Sources:&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt;: To store user data.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS Lambda&lt;/strong&gt;: For complex business logic processing (used for &lt;code&gt;createUser&lt;/code&gt; and &lt;code&gt;updateUser&lt;/code&gt; mutations).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon OpenSearch Service&lt;/strong&gt;: To enable search functionality.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;Step-by-step setup&lt;/h3&gt; 
&lt;h4&gt;Prerequisites:&lt;/h4&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;An active AWS account&lt;/strong&gt;: If you don’t have one, you can &lt;a href="https://portal.aws.amazon.com/billing/signup" target="_blank" rel="noopener"&gt;create an AWS account&lt;/a&gt; to get started.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Valid AWS credentials&lt;/strong&gt;: You need an &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html" target="_blank" rel="noopener"&gt;IAM user&lt;/a&gt; with permissions to manage AWS AppSync APIs (create, update, delete, view) and to view related CloudWatch metrics and alarms. This can be granted by attaching the AWS managed policies &lt;code&gt;AWSAppSyncAdministrator&lt;/code&gt; and &lt;code&gt;CloudWatchReadOnlyAccess&lt;/code&gt;, or equivalent custom permissions. You will use these credentials to sign in to &lt;a href="https://console.aws.amazon.com/" target="_blank" rel="noopener"&gt;AWS Management Console&lt;/a&gt;.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Here’s how you can enable enhanced monitoring in AWS AppSync using different methods:&lt;/p&gt; 
&lt;h4&gt;Method 1: Enabling enhanced monitoring via the AWS Management Console:&lt;/h4&gt; 
&lt;ol&gt; 
 &lt;li&gt;&lt;strong&gt;Navigate to the AppSync Console:&lt;/strong&gt; 
  &lt;ol&gt; 
   &lt;li&gt;Sign in to the &lt;a href="https://console.aws.amazon.com/" target="_blank" rel="noopener"&gt;AWS Management Console&lt;/a&gt;.&lt;/li&gt; 
   &lt;li&gt;Navigate to the &lt;strong&gt;AppSync&lt;/strong&gt; service by searching for “AppSync” in the AWS services search bar.&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Select Your GraphQL API:&lt;/strong&gt; 
  &lt;ol&gt; 
   &lt;li&gt;In the AppSync console, click on the API for which you want to enable enhanced monitoring.&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Go to the Settings:&lt;/strong&gt; 
  &lt;ol&gt; 
   &lt;li&gt;Select “Settings” from the left-hand menu.&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;Enable Enhanced Monitoring:&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt; 
  &lt;div id="attachment_14278" style="width: 1847px" class="wp-caption alignnone"&gt;
   &lt;img aria-describedby="caption-attachment-14278" loading="lazy" class="wp-image-14278 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/10/07/picture1-1.png" alt="Screenshot of the AWS AppSync Console ‘Settings’ tab showing the toggles for Enhanced Monitoring. Three checkboxes are enabled: Per-Resolver Metrics, Per-Data Source Metrics, and Operation-Level Metrics." width="1837" height="384"&gt;
   &lt;p id="caption-attachment-14278" class="wp-caption-text"&gt;Figure 1: AWS AppSync Console ‘Settings’ tab showing the toggles for Enhanced Monitoring&lt;/p&gt;
  &lt;/div&gt; 
  &lt;ol&gt; 
   &lt;li&gt;In the API Configuration section, you’ll see an option to enable &lt;strong&gt;Enhanced Monitoring&lt;/strong&gt;.&lt;/li&gt; 
   &lt;li&gt;Here you can either enable metrics for all resolvers and data sources or can choose to manually select them by choosing “&lt;code&gt;PER_RESOLVER_METRICS&lt;/code&gt;” and “&lt;code&gt;PER_DATA_SOURCE_METRICS&lt;/code&gt;”, respectively, as stated in the documentation.&lt;/li&gt; 
   &lt;li&gt;For per-resolver and per-data source metrics, metrics need to be enabled individually on each resolver and data source.&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;h4&gt;Method 2: Enabling enhanced monitoring via the AWS CLI&lt;/h4&gt; 
&lt;p&gt;&lt;code&gt;aws appsync update-graphql-api \&lt;br&gt; &lt;/code&gt;&lt;code&gt;--api-id &amp;lt;API_ID&amp;gt; \&lt;br&gt; &lt;/code&gt;&lt;code&gt;--name "&amp;lt;API_NAME&amp;gt;" \&lt;br&gt; &lt;/code&gt;&lt;code&gt;--authentication-type API_KEY \&lt;br&gt; &lt;/code&gt;&lt;code&gt;--enhanced-metrics-config resolverLevelMetricsBehavior=FULL_REQUEST_RESOLVER_METRICS,dataSourceLevelMetricsBehavior=FULL_REQUEST_DATA_SOURCE_METRICS,operationLevelMetricsConfig=ENABLE&lt;/code&gt;&lt;/p&gt; 
&lt;h4&gt;Method 3: Enabling enhanced monitoring via Terraform&lt;/h4&gt; 
&lt;p&gt;Following the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appsync_graphql_api#enhanced_metrics_config-block" target="_blank" rel="noopener"&gt;AWS Provider documentation&lt;/a&gt;, adding the block &lt;code&gt;enhanced_metrics_config&lt;/code&gt;to an existing resource will enable enhanced monitoring, eg:&lt;/p&gt; 
&lt;pre class="hl"&gt;&lt;code&gt;resource "aws_appsync_graphql_api" "api" {
  authentication_type = "API_KEY"
  name                = "&amp;lt;API_NAME&amp;gt;"
  enhanced_metrics_config {
    resolver_level_metrics_behavior     = "FULL_REQUEST_RESOLVER_METRICS"
    data_source_level_metrics_behavior  = "FULL_REQUEST_DATA_SOURCE_METRICS"
    operation_level_metrics_config      = "ENABLED"
  }
}
&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Metrics dashboard&lt;/h2&gt; 
&lt;p&gt;With Enhanced Monitoring enabled, you can now observe detailed metrics in the Amazon CloudWatch dashboard. For example, if the SearchUsers query is experiencing high latency, you can quickly identify whether the issue lies with the Amazon OpenSearch Service data source or the Lambda resolver. The Amazon CloudWatch dashboard will display metrics such as resolver execution time and data source response time, allowing you to pinpoint the exact cause of any performance issues.&lt;/p&gt; 
&lt;h3&gt;Additional API metrics in Amazon CloudWatch metrics&lt;/h3&gt; 
&lt;p&gt;Useful dimensions and metrics include:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;DataSource, GraphQLAPIId&lt;/li&gt; 
 &lt;li&gt;API Metrics by Operation&lt;/li&gt; 
 &lt;li&gt;GraphQLAPIId, Resolver&lt;/li&gt; 
&lt;/ul&gt; 
&lt;div id="attachment_14279" style="width: 1891px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-14279" loading="lazy" class="wp-image-14279 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/10/07/picture2.png" alt="A screenshot of AWS AppSync API Metrics in Amazon CloudWatch" width="1881" height="714"&gt;
 &lt;p id="caption-attachment-14279" class="wp-caption-text"&gt;Figure 2: A screenshot of AWS AppSync API Metrics in Amazon CloudWatch&lt;/p&gt;
&lt;/div&gt; 
&lt;h4&gt;API Metrics per resolver&lt;/h4&gt; 
&lt;div class="figure"&gt; 
 &lt;div class="figcaption"&gt; 
  &lt;div id="attachment_14281" style="width: 1891px" class="wp-caption aligncenter"&gt;
   &lt;img aria-describedby="caption-attachment-14281" loading="lazy" class="wp-image-14281 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/10/07/image003.jpg" alt="A screenshot of AWS AppSync API Metrics per-Resolver" width="1881" height="719"&gt;
   &lt;p id="caption-attachment-14281" class="wp-caption-text"&gt;Figure 3: AWS AppSync API Metrics by Resolver&lt;/p&gt;
  &lt;/div&gt; 
 &lt;/div&gt; 
&lt;/div&gt; 
&lt;div class="figure"&gt; 
 &lt;h4&gt;API Metrics per DataSource&lt;/h4&gt; 
 &lt;div class="figcaption"&gt; 
  &lt;div id="attachment_14280" style="width: 1885px" class="wp-caption alignnone"&gt;
   &lt;img aria-describedby="caption-attachment-14280" loading="lazy" class="wp-image-14280 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/10/07/image004.jpg" alt="A screenshot of AWS AppSync API Metrics per data source." width="1875" height="717"&gt;
   &lt;p id="caption-attachment-14280" class="wp-caption-text"&gt;Figure 4: AWS AppSync API Metrics by Datasource&lt;/p&gt;
  &lt;/div&gt; 
 &lt;/div&gt; 
 &lt;h4&gt;API Metrics per Operation&lt;/h4&gt; 
 &lt;div class="figcaption"&gt; 
  &lt;div id="attachment_14282" style="width: 1891px" class="wp-caption alignnone"&gt;
   &lt;img aria-describedby="caption-attachment-14282" loading="lazy" class="wp-image-14282 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/10/07/image005.jpg" alt="A screenshot of AWS AppSync API Metrics per operation." width="1881" height="718"&gt;
   &lt;p id="caption-attachment-14282" class="wp-caption-text"&gt;Figure 5: AWS AppSync API Metrics by Operation&lt;/p&gt;
  &lt;/div&gt; 
 &lt;/div&gt; 
&lt;/div&gt; 
&lt;div class="figure"&gt; 
 &lt;div class="figcaption"&gt;&lt;/div&gt; 
&lt;/div&gt; 
&lt;h3&gt;API metrics custom dashboard with enhanced metrics&lt;/h3&gt; 
&lt;p&gt;With AWS AppSync’s enhanced monitoring metrics, you can create a custom CloudWatch dashboard to get a real-time view of your API’s performance and display key KPIs like resolver latency, error rates, throttling, and data source performance. This helps quickly identify bottlenecks, track usage patterns, and optimise API efficiency.&lt;/p&gt; 
&lt;p&gt;For example, you can set up widgets on your Amazon CloudWatch dashboard to monitor:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Resolver Request Count:&lt;/strong&gt; This will provide insights into usage patterns, guiding the team to optimise the most frequently used data sources and resolvers.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Resolver Latency:&lt;/strong&gt; Track the time taken by each resolver to process requests, helping you pinpoint areas that may require optimisation.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Data Source Performance:&lt;/strong&gt; Visualise the interaction between your API and its data sources, such as DynamoDB or Lambda, to ensure they are performing within acceptable thresholds.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Error Rates:&lt;/strong&gt; Display the number of errors encountered by each resolver or data source, enabling you to identify and address issues before they impact users.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;By aggregating these metrics into a single dashboard, you gain a holistic view of your API’s health, enabling proactive management and optimisation to deliver a seamless user experience.&lt;/p&gt; 
&lt;div id="attachment_14283" style="width: 1867px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-14283" loading="lazy" class="wp-image-14283 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/10/07/image006.jpg" alt="A custom dashboard to show API Metrics by aggregating total request count, total Data source requests, average latencies per resolver and data source. Operation level metrics and overall API metrics. " width="1857" height="744"&gt;
 &lt;p id="caption-attachment-14283" class="wp-caption-text"&gt;Figure 6: Custom Amazon CloudWatch dashboard to show API Metrics&lt;/p&gt;
&lt;/div&gt; 
&lt;article class="post"&gt; 
 &lt;h2&gt;Actionable insights from API monitoring&lt;/h2&gt; 
 &lt;p&gt;Using the insights from an API metrics dashboard with enhanced metrics like those provided by AWS AppSync, you can take several actions to improve your API’s performance and reliability. Here are some specific actions you can take based on the metrics you monitor:&lt;/p&gt; 
 &lt;h3&gt;High resolver request count&lt;/h3&gt; 
 &lt;ul&gt; 
  &lt;li&gt;&lt;strong&gt;Enable AppSync Caching:&lt;/strong&gt; Use AppSync’s built-in caching to store results of frequently requested resolvers, reducing load on data sources. Set an appropriate TTL (Time to Live) for optimal balance between freshness and performance.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Smaller Functional Units:&lt;/strong&gt; Break down complex resolvers into smaller units. This improves visibility and makes it easier to identify and optimse specific tasks within your API.&lt;/li&gt; 
 &lt;/ul&gt; 
 &lt;h3&gt;High resolver latency&lt;/h3&gt; 
 &lt;ul&gt; 
  &lt;li&gt;&lt;strong&gt;Implement AppSync Caching:&lt;/strong&gt; Cache responses for expensive queries to reduce latency for repeated requests.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Optimise Resolvers:&lt;/strong&gt; Refactor resolver logic to minimise processing time, such as reducing unnecessary computations or data transformations.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Optimise Data Fetching:&lt;/strong&gt; Ensure efficient database queries. Use indexes in DynamoDB and optimise SQL queries with proper indexing.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Batch Operations:&lt;/strong&gt; Use batch operations to minimise the number of data source calls. For example, use DynamoDB’s BatchGetItem for fetching multiple items in one request.&lt;/li&gt; 
 &lt;/ul&gt; 
 &lt;h3&gt;Poor data source performance&lt;/h3&gt; 
 &lt;ul&gt; 
  &lt;li&gt;&lt;strong&gt;Optimise AWS Lambda Functions:&lt;/strong&gt; Increase memory allocation or refactor code for more efficient execution. Use Lambda’s metrics to identify performance issues.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Use DynamoDB Accelerator (DAX):&lt;/strong&gt; Integrate DAX to reduce read latency for high-traffic DynamoDB data sources by caching responses in-memory.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;RDS Optimisation:&lt;/strong&gt; Utilise RDS read replicas for distributing read load and optimise queries with proper indexing.&lt;/li&gt; 
 &lt;/ul&gt; 
 &lt;h3&gt;Increased error rates&lt;/h3&gt; 
 &lt;ul&gt; 
  &lt;li&gt;&lt;strong&gt;Investigate and Resolve Issues:&lt;/strong&gt; Use CloudWatch and AppSync logs to pinpoint and fix resolver or data source errors.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Implement Retry Mechanisms:&lt;/strong&gt; Use retry logic for transient errors or AWS SDK’s automatic retries to handle network interruptions.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Improve Input Validation:&lt;/strong&gt; Add validation to catch invalid or malformed requests early, preventing errors and providing informative messages to clients.&lt;/li&gt; 
 &lt;/ul&gt; 
 &lt;h3&gt;Frequent throttling occurrences&lt;/h3&gt; 
 &lt;ul&gt; 
  &lt;li&gt;&lt;strong&gt;Increase Throttling Limits:&lt;/strong&gt; Adjust throttling limits in AppSync (using AWS WAF) or data sources like DynamoDB for legitimate high traffic.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Optimise API Usage:&lt;/strong&gt; Guide clients to reduce redundant requests and use efficient query structures.&lt;/li&gt; 
  &lt;li&gt;&lt;strong&gt;Implement Rate Limiting and Backoff:&lt;/strong&gt; Apply rate limiting and exponential backoff on the client side to manage request rates and prevent hitting throttling limits.&lt;/li&gt; 
 &lt;/ul&gt; 
 &lt;h2&gt;Cost considerations&lt;/h2&gt; 
 &lt;p&gt;Enhanced Monitoring emits additional metrics that incur charges in Amazon CloudWatch. Here’s a breakdown based on the sample API:&lt;/p&gt; 
 &lt;h3&gt;Calculating costs — Example estimation using the sample API&lt;/h3&gt; 
 &lt;p&gt;&lt;strong&gt;Example calculation:&lt;/strong&gt;&lt;/p&gt; 
 &lt;p&gt;Total no. of new metrics = &lt;code class="inline"&gt;5 (3 queries + 2 mutations) * 5 (additional enhanced metrics)&lt;/code&gt; + &lt;code class="inline"&gt;4 (No. of Data sources) * 3 (additional enhanced metrics)&lt;/code&gt; = &lt;strong&gt;37&lt;/strong&gt;&lt;/p&gt; 
 &lt;p&gt;Monthly Amazon CloudWatch Metrics Charges @ &lt;code class="inline"&gt;$0.30&lt;/code&gt; per custom metric = &lt;code class="inline"&gt;37 * $0.30 = $11.10&lt;/code&gt;&lt;/p&gt; 
 &lt;p&gt;Note: Once you exceed 10,000 total metrics then volume pricing tiers will apply – To see the detailed pricing table, click the Metrics tab on the &lt;a href="https://aws.amazon.com/cloudwatch/pricing/" target="_blank" rel="noopener"&gt;Amazon CloudWatch pricing page&lt;/a&gt;.&lt;/p&gt; 
 &lt;h3&gt;Reviewing costs&lt;/h3&gt; 
 &lt;p&gt;Using &lt;a href="https://docs.aws.amazon.com/cur/latest/userguide/what-is-data-exports.html" target="_blank" rel="noopener"&gt;AWS Data Exports&lt;/a&gt; you can get an approximate cost breakdown of an existing AWS AppSync API by following these steps:&lt;/p&gt; 
 &lt;ol&gt; 
  &lt;li&gt;Enable the enhanced monitoring temporarily on an AppSync API as seen above.&lt;/li&gt; 
  &lt;li&gt;Execute queries or mutations against the AppSync API.&lt;/li&gt; 
  &lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cur/latest/userguide/dataexports-create-legacy.html" target="_blank" rel="noopener"&gt;Setup Cost Data Export&lt;/a&gt;&lt;/li&gt; 
  &lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cur/latest/userguide/dataexports-processing.html#dataexports-athena" target="_blank" rel="noopener"&gt;Use Amazon Athena to process Data Exports using the Crawlers&lt;/a&gt;.&lt;/li&gt; 
  &lt;li&gt;Run the Crawler (see &lt;a href="https://docs.aws.amazon.com/athena/latest/ug/schema-crawlers.html" target="_blank" rel="noopener"&gt;Crawler docs&lt;/a&gt;), and wait until it completes its run.&lt;/li&gt; 
  &lt;li&gt;Replace with the Crawler’s target table.&lt;/li&gt; 
  &lt;li&gt;Replace placeholders in the query below: 
   &lt;ol&gt; 
    &lt;li&gt;&lt;code&gt;&amp;lt;glue table&amp;gt;&lt;/code&gt;: Crawler’s target Glue table&lt;/li&gt; 
    &lt;li&gt;&lt;code&gt;&amp;lt;account&amp;gt;&lt;/code&gt;: AWS account where AppSync is set up&lt;/li&gt; 
    &lt;li&gt;&lt;code&gt;&amp;lt;region&amp;gt;&lt;/code&gt;: Region where AppSync is set up&lt;/li&gt; 
   &lt;/ol&gt; &lt;/li&gt; 
  &lt;li&gt;Query Athena with the SQL query below by adjusting &lt;code&gt;line_item_usage_start_date&lt;/code&gt;, &lt;code&gt;line_item_usage_end_date&lt;/code&gt; to compare the following datasets: 
   &lt;ol&gt; 
    &lt;li&gt;During a timeframe when enhanced metrics were not yet used.&lt;/li&gt; 
    &lt;li&gt;During a timeframe when enhanced metrics were already active.&lt;/li&gt; 
   &lt;/ol&gt; &lt;/li&gt; 
 &lt;/ol&gt; 
 &lt;p&gt;Sample Athena SQL (adjust placeholders):&lt;/p&gt; 
 &lt;pre class="hl"&gt;&lt;code&gt;SELECT
line_item_operation,
SUM(CAST(line_item_unblended_cost AS decimal(16,8))) AS TotalSpend
FROM ''
WHERE
product_product_name = 'AmazonCloudWatch'
AND line_item_usage_account_id = ''
AND product_region = ''
AND line_item_operation in ('MetricStorage:AWS/AppSync', 'GetMetricData', 'ListMetrics', 'GetMetricStatistics', 'PutMetricData')
AND line_item_line_item_type NOT IN ('Tax','Credit','Refund','EdpDiscount','Fee','RIFee')
AND line_item_usage_start_date &amp;gt; DATE('2024-07-20')
AND line_item_usage_end_date &amp;lt; DATE('2024-07-30')
GROUP BY line_item_operation
ORDER BY TotalSpend DESC;
&lt;/code&gt;&lt;/pre&gt; 
 &lt;p&gt;Example output:&lt;/p&gt; 
 &lt;pre class="hl"&gt;&lt;code&gt;# line_item_operation TotalSpend
1 GetMetricData 59.11682000
2 MetricStorage:AWS/AppSync 0.56626344
3 PutMetricData 0.42279000
4 ListMetrics 0.00248000
5 GetMetricStatistics 0.00028000
&lt;/code&gt;&lt;/pre&gt; 
 &lt;p&gt;You can extract other details out of the data export by reviewing the &lt;a href="https://docs.aws.amazon.com/cur/latest/userguide/Lineitem-columns.html" target="_blank" rel="noopener"&gt;Line item details&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_billing.html" target="_blank" rel="noopener"&gt;Usage types&lt;/a&gt;.&lt;/p&gt; 
 &lt;h3&gt;Metrics retention&lt;/h3&gt; 
 &lt;p&gt;&lt;strong&gt;CloudWatch retains metrics data as follows:&lt;/strong&gt;&lt;/p&gt; 
 &lt;p&gt;Data points with a period of less than 60 seconds are available for 3 hours. These data points are high-resolution custom metrics.&lt;/p&gt; 
 &lt;ul&gt; 
  &lt;li&gt;Data points with a period of 60 seconds (1 minute) are available for 15 days&lt;/li&gt; 
  &lt;li&gt;Data points with a period of 300 seconds (5 minutes) are available for 63 days&lt;/li&gt; 
  &lt;li&gt;Data points with a period of 3600 seconds (1 hour) are available for 455 days (15 months)&lt;/li&gt; 
 &lt;/ul&gt; 
 &lt;p&gt;Data points that are initially published with a shorter period are aggregated together for long-term storage. For example, if you collect data using a period of 1 minute, the data remains available for 15 days with 1-minute resolution. After 15 days this data is still available, but is aggregated and is retrievable only with a resolution of 5 minutes. After 63 days, the data is further aggregated and is available with a resolution of 1 hour.&lt;/p&gt; 
 &lt;h2&gt;Conclusion&lt;/h2&gt; 
 &lt;p&gt;AWS AppSync’s Enhanced Monitoring is a powerful tool that provides deeper insights into your API’s performance, delivering granular visibility to accelerate issue detection and remediation and reduce mean time to resolution. While there is an associated cost, the value it brings in terms of operational efficiency and improved user experience is substantial. By leveraging Enhanced Monitoring, you can ensure that your AWS AppSync APIs are optimised, reliable, and scalable.&lt;/p&gt; 
 &lt;p&gt;Monitoring provides valuable data that empowers you to make informed decisions and drive your business forward. Ready to take your API performance to the next level? &lt;strong&gt;Activate Enhanced Monitoring in AWS AppSync&lt;/strong&gt; for the detailed insights you need to keep your APIs running smoothly.&lt;/p&gt; 
&lt;/article&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/10/06/1620923127007.jpeg" alt="Sagar Suri" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Sagar Suri – Senior Delivery Consultant, AWS&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;Sagar is a Senior Delivery Consultant at AWS, specialising in large-scale Cloud Architecture and governance. He leads strategic initiatives to help enterprise teams establish best practices, enabling the delivery of resilient, cost-optimised, and high-impact digital solutions across their application landscape.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/10/09/vbertram-1.jpg" alt="Bertram Varga" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Bertram Varga – Senior Delivery Consultant, AWS&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;Bertram Varga is a Senior Cloud Application Architect at AWS Professional Services. He supports customers in building and modernizing applications in AWS in the Financial services, Automotive and Healthcare &amp;amp; Life sciences domains.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
					
			
		
		
			</item>
		<item>
		<title>Offline caching with AWS Amplify, Tanstack, AppSync and MongoDB Atlas</title>
		<link>https://aws.amazon.com/blogs/mobile/offline-caching-with-aws-amplify-tanstack-appsync-and-mongodb-atlas/</link>
		
		<dc:creator><![CDATA[Dan Kiuna]]></dc:creator>
		<pubDate>Thu, 19 Jun 2025 20:59:54 +0000</pubDate>
				<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<category><![CDATA[Amplify]]></category>
		<category><![CDATA[AppSync]]></category>
		<category><![CDATA[offline]]></category>
		<guid isPermaLink="false">61321dad901d24202425c6c381e2612b02f7008c</guid>

					<description>In this blog we demonstrate how to create an offline-first application with optimistic UI using AWS Amplify, AWS AppSync, and MongoDB Atlas. Developers design offline first applications to work without requiring an active internet connection. Optimistic UI then builds on top of the offline first approach by updating the UI with expected data changes, without […]</description>
										<content:encoded>&lt;p&gt;In this blog we demonstrate how to create an offline-first application with optimistic UI using &lt;a href="https://aws.amazon.com/amplify/"&gt;AWS Amplify&lt;/a&gt;, &lt;a href="https://aws.amazon.com/appsync/"&gt;AWS AppSync&lt;/a&gt;, and &lt;a href="https://www.mongodb.com/products/platform/atlas-cloud-providers/aws"&gt;MongoDB Atlas&lt;/a&gt;. Developers design offline first applications to work without requiring an active internet connection. Optimistic UI then builds on top of the offline first approach by updating the UI with expected data changes, without depending on a response from the server. This approach typically utilizes a local cache strategy.&lt;/p&gt; 
&lt;p&gt;Applications that use offline first with optimistic UI provide a number of improvements for users. These include reducing the need to implement loading screens, better performance due to faster data access, reliability of data when an application is offline, and cost efficiency. While implementing offline capabilities manually can take sizable effort, you can use tools that simplify the process.&lt;/p&gt; 
&lt;p&gt;We provide a &lt;a href="https://github.com/mongodb-partners/amplify-mongodb-tanstack-offline"&gt;sample to-do application&lt;/a&gt; that renders results of MongoDB Atlas CRUD operations immediately on the UI before the request roundtrip has completed, improving the user experience. In other words, we implement optimistic UI that makes it easy to render loading and error states, while allowing developers to rollback changes on the UI when API calls are unsuccessful. The implementation leverages &lt;a href="https://tanstack.com/query/latest/docs/react/overview"&gt;TanStack Query&lt;/a&gt; to handle the optimistic UI updates along with &lt;a href="https://docs.amplify.aws/react/build-a-backend/data/optimistic-ui/"&gt;AWS Amplify&lt;/a&gt;. The diagram in Figure 1 illustrates the interaction between the UI and the backend.&lt;/p&gt; 
&lt;p&gt;TanStack Query is an asynchronous state management library for TypeScript/JavaScript, React, Solid, Vue, Svelte, and Angular. It simplifies fetching, caching, synchronizing, and updating server state in web applications. By leveraging TanStack Query’s caching mechanisms, the app ensures data availability even without an active network connection. AWS Amplify streamlines the development process, while AWS AppSync provides a robust GraphQL API layer, and MongoDB Atlas offers a scalable database solution. This integration showcases how TanStack Query’s offline caching can be effectively utilized within a full-stack application architecture.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-14195 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/TanstackWithAtlas-793x1024.png" alt="Amplify Tanstack Optimistic UI - Interaction Diagram" width="793" height="1024"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Figure 1. Interaction Diagram&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;The sample application implements a classic to-do functionality and the exact app architecture is shown in &lt;strong&gt;Figure 2&lt;/strong&gt;. The stack consists of:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;MongoDB Atlas for database services.&lt;/li&gt; 
 &lt;li&gt;AWS Amplify the full-stack application framework.&lt;/li&gt; 
 &lt;li&gt;AWS AppSync for GraphQL API management.&lt;/li&gt; 
 &lt;li&gt;AWS Lambda Resolver for serverless computing.&lt;/li&gt; 
 &lt;li&gt;Amazon Cognito for user management and authentication.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-14197 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/architecture-1024x557.png" alt="Amplify Tanstack Optimistic UI - Architecture Diagram" width="1024" height="557"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Figure 2. Architecture&lt;/strong&gt;&lt;/p&gt; 
&lt;h2&gt;Deploy the Application&lt;/h2&gt; 
&lt;p&gt;To deploy the app in your AWS account, follow the steps below. Once deployed you can create a user, authenticate yourself, and create to-do entries – see Figure 8.&lt;/p&gt; 
&lt;h3&gt;Set up the MongoDB Atlas cluster&lt;/h3&gt; 
&lt;ol&gt; 
 &lt;li&gt;Follow the &lt;a href="https://www.mongodb.com/docs/atlas/tutorial/create-atlas-account/"&gt;link&lt;/a&gt; to the setup the &lt;a href="https://www.mongodb.com/docs/atlas/tutorial/deploy-free-tier-cluster/"&gt;MongoDB Atlas cluster&lt;/a&gt;, Database , &lt;a href="https://www.mongodb.com/docs/atlas/tutorial/create-mongodb-user-for-cluster/"&gt;User&lt;/a&gt; and &lt;a href="https://www.mongodb.com/docs/atlas/security/add-ip-address-to-list/"&gt;Network access&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;Set up the user 
  &lt;ol&gt; 
   &lt;li&gt;&lt;a href="https://www.mongodb.com/docs/atlas/security-add-mongodb-users/"&gt;Configure User&lt;/a&gt;&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3&gt;Clone the GitHub Repository&lt;/h3&gt; 
&lt;ol&gt; 
 &lt;li&gt;Clone the sample application with the following command&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;&lt;code&gt; git clone https://github.com/mongodb-partners/amplify-mongodb-tanstack-offline&lt;/code&gt;&lt;/p&gt; 
&lt;h3&gt;Setup the AWS CLI credentials (optional if you need to debug your application locally)&lt;/h3&gt; 
&lt;ol&gt; 
 &lt;li&gt;If you would like to test the application locally using a &lt;a href="https://docs.amplify.aws/react/deploy-and-host/sandbox-environments/setup/"&gt;sandbox environment&lt;/a&gt;, you can setup temporary AWS credentials locally:&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
export AWS_SESSION_TOKEN=&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;Deploy the Todo Application in AWS Amplify&lt;/h3&gt; 
&lt;ol&gt; 
 &lt;li&gt;Open the AWS Amplify console and Select the Github Option&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-14201 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/fig3-github-1024x317.png" alt="Amplify Tanstack Optimistic UI - Select Github Option" width="1024" height="317"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Figure 3. Select Github option&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;2. Configure the GitHub Repository&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-14203 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/fig4-permissions-1024x254.png" alt="Amplify Tanstack Optimistic UI - Configure Repository Permissions" width="1024" height="254"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Figure 4. Configure repository permissions&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;3. Select the GitHub Repository and click Next&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-14204 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/fig5-branch-1024x472.png" alt="Amplify Tanstack Optimistic UI - Select Repository and Branch" width="1024" height="472"&gt;&lt;br&gt; &lt;strong&gt;Figure 5. Select repository and branch&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;4. Set all other options to default and deploy&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-14205 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/fig6-deploy-1024x362.png" alt="Amplify Tanstack Optimistic UI - Deploy Application" width="1024" height="362"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Figure 6. Deploy application&lt;/strong&gt;&lt;/p&gt; 
&lt;h3&gt;Configure the Environment Variables&lt;/h3&gt; 
&lt;p&gt;Configure the Environment variables after the successful deployment&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-14206 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/fig7-envs.png" alt="Amplify Tanstack Optimistic UI - Environment Variables" width="957" height="396"&gt;&lt;br&gt; &lt;strong&gt;Figure 7. Configure environment variables&lt;/strong&gt;&lt;/p&gt; 
&lt;h3&gt;Open the application and test&lt;/h3&gt; 
&lt;p&gt;Open the application through the URL provided and test the application.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-14207 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/fig8-test-1024x521.png" alt="Amplify Tanstack Optimistic UI - Sample Todo entries" width="1024" height="521"&gt;&lt;br&gt; &lt;strong&gt;Figure 8. Sample todo entries&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;MongoDB Atlas Output&lt;br&gt; &lt;img loading="lazy" class="alignnone wp-image-14208 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/fig9-mongo-914x1024.png" alt="Amplify Tanstack Optimistic UI - Data in Mongo" width="914" height="1024"&gt;&lt;br&gt; &lt;strong&gt;Figure 9. Data in Mongo&lt;/strong&gt;&lt;/p&gt; 
&lt;h2&gt;Review the Application&lt;/h2&gt; 
&lt;p&gt;Now that the application is deployed, let’s discuss what happens under the hood and what was configured for us. We utilized Amplify’s git-based workflow to host our full-stack, serverless web application with continuous deployment. Amplify supports various frameworks, including server side rendered (SSR) frameworks like Next.js and Nuxt, single page application (SPA) frameworks like React and Angular, and static site generators (SSG) like Gatsby and Hugo. In this case, we deployed a SPA React based application. We can include feature branches, custom domains, pull request previews, end-to-end testing, and redirects/rewrites. Amplify Hosting provides a Git-based workflow enables atomic deployments ensuring that updates are only applied after the entire deployment is complete.&lt;/p&gt; 
&lt;p&gt;To deploy our application we used AWS Amplify Gen 2, which is a tool designed to simplify the development and deployment of full-stack applications using TypeScript. It leverages the &lt;a href="https://aws.amazon.com/cdk/"&gt;AWS Cloud Development Kit&lt;/a&gt; (CDK) to manage cloud resources, ensuring scalability and ease of use.&lt;/p&gt; 
&lt;p&gt;Before we conclude, it is important to understand our application’s updates concurrency. We implemented a simple optimistic first-come first-served conflict resolution mechanism. The MongoDB Atlas cluster persists updates in the order it receives them. In case of conflicting updates, the latest arriving update will override previous updates. This mechanism works well in applications where update conflicts are rare. It is important to evaluate how this may or may not suit your production needs, requiring more sophisticated approaches. TanStack provides capabilities for more complex mechanisms to handle various connectivity scenarios. By default, TanStack Query provides an “online” network mode, where Queries and Mutations will not be triggered unless you have network connection. If a query runs because you are online, but you go offline while the fetch is still happening, TanStack Query will also pause the retry mechanism. Paused queries will then continue to run once you re-gain network connection. In order to optimistically update the UI with new or changed values, we can also update the local cache with what we expect the response to be. This is approach works well together with TanStack’s “online” network mode, where if the application has no network connectivity, the mutations will not fire, but will be added to the queue, but our local cache can be used to update the UI. Below is a key example of how our sample application optimistically updates the UI with the expected mutation.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;const createMutation = useMutation({
    mutationFn: async (input: { content: string }) =&amp;gt; {
    // Use the Amplify client to make the request to AppSync
      const { data } = await amplifyClient.mutations.addTodo(input);
      return data;
    },
    // When mutate is called:
    onMutate: async (newTodo) =&amp;gt; {
      // Cancel any outgoing refetches
      // so they don't overwrite our optimistic update
      await tanstackClient.cancelQueries({ queryKey: ["listTodo"] });

      // Snapshot the previous value
      const previousTodoList = tanstackClient.getQueryData(["listTodo"]);

      // Optimistically update to the new value
      if (previousTodoList) {
        tanstackClient.setQueryData(["listTodo"], (old: Todo[]) =&amp;gt; [
          ...old,
          newTodo,
        ]);
      }

      // Return a context object with the snapshotted value
      return { previousTodoList };
    },
    // If the mutation fails,
    // use the context returned from onMutate to rollback
    onError: (err, newTodo, context) =&amp;gt; {
      console.error("Error saving record:", err, newTodo);
      if (context?.previousTodoList) {
        tanstackClient.setQueryData(["listTodo"], context.previousTodoList);
      }
    },
    // Always refetch after error or success:
    onSettled: () =&amp;gt; {
      tanstackClient.invalidateQueries({ queryKey: ["listTodo"] });
    },
    onSuccess: () =&amp;gt; {
      tanstackClient.invalidateQueries({ queryKey: ["listTodo"] });
    },
  });&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;We welcome any &lt;a href="https://github.com/mongodb-partners/amplify-mongodb-tanstack-offline/pulls"&gt;PRs&lt;/a&gt; implementing additional conflict resolution strategies.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Try out MongoDB Atlas on &lt;a href="https://aws.amazon.com/marketplace/pp/prodview-pp445qepfdy34"&gt;AWS MarketPlace&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;Get familiar with &lt;a href="https://aws.amazon.com/amplify/"&gt;AWS Amplify&lt;/a&gt;, &lt;a href="https://docs.amplify.aws/react/how-amplify-works/"&gt;Amplify Gen2&lt;/a&gt; and &lt;a href="https://aws.amazon.com/pm/appsync/"&gt;AppSync&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;For detailed instructions on deploying the application, refer to the &lt;a href="https://docs.amplify.aws/react/start/quickstart/#deploy-a-fullstack-app-to-aws"&gt;deployment section&lt;/a&gt; of our documentation&lt;/li&gt; 
 &lt;li&gt;Submit a &lt;a href="https://github.com/mongodb-partners/amplify-mongodb-tanstack-offline"&gt;PR&lt;/a&gt; with your enhancements&lt;/li&gt; 
&lt;/ul&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/dankiuna.jpeg" alt="" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Dan Kiuna – Specialist Solutions Architect, AWS&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;Dan is a Specialist Solutions Architect at AWS AppSync. He is well experienced at leveraging AppSync’s capabilities alongside other AWS services to create high-performance, real-time, and offline-enabled solutions.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/10/igor-alekseev.jpeg" alt="" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Igor Alekseev – Partner Solutions Architect, AWS&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;Igor works with strategic partners helping them build complex, AWS-optimized architectures. Prior joining AWS, as a Data/Solution Architect he implemented many projects in the Big Data domain. As a Data Engineer he was involved in applying AI/ML to fraud detection and office automation. Igor’s projects spanned a variety of industries including communications, finance, public safety, manufacturing, and healthcare.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/06/11/anuj-panchal.png" alt="" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Anuj Panchal – Partner Solutions Architect, Mongo DB&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;As a Partner Solutions Architect at MongoDB, I ideate, develop, and deploy seamless integrations between MongoDB and AWS offerings. My mission is to simplify the developer experience when working with MongoDB and AWS. #LoveYourDevelopers&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Introducing Upgraded Build Instances on Amplify Hosting</title>
		<link>https://aws.amazon.com/blogs/mobile/introducing-upgraded-build-instances-on-amplify-hosting/</link>
		
		<dc:creator><![CDATA[Matt Auerbach]]></dc:creator>
		<pubDate>Thu, 29 May 2025 18:14:26 +0000</pubDate>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Amplify]]></category>
		<category><![CDATA[AWS Amplify Hosting]]></category>
		<guid isPermaLink="false">85de4952f485c39f97089ac8ecd771dfcb0ab466</guid>

					<description>AWS Amplify Hosting has built web applications using a fixed instance environment. As applications grow more complex and require intensive build processes for dependency management, asset optimization, and comprehensive testing, developers need more powerful build environments to maintain productivity and deployment speed. Today, we’re excited to introduce customizable build instances for Amplify Hosting. This update […]</description>
										<content:encoded>&lt;p&gt;&lt;a href="https://aws.amazon.com/amplify/hosting/"&gt;AWS Amplify Hosting&lt;/a&gt; has built web applications using a fixed instance environment. As applications grow more complex and require intensive build processes for dependency management, asset optimization, and comprehensive testing, developers need more powerful build environments to maintain productivity and deployment speed.&lt;/p&gt; 
&lt;p&gt;Today, we’re excited to introduce customizable build instances for Amplify Hosting. This update adds two new instance sizes: Large and XLarge, both offering increased memory and CPU resources. Development teams can now scale their build resources to match their specific needs. This is especially useful when handling large dependency trees, processing numerous static assets, or running memory-intensive operations like TypeScript compilation and parallel testing frameworks.&lt;/p&gt; 
&lt;h3&gt;Challenges with Fixed Size Build Instance&lt;/h3&gt; 
&lt;p&gt;Customers building large applications and running heavier workloads experience &lt;a href="https://github.com/aws-amplify/amplify-hosting/issues/654"&gt;increased build times, slow CI/CD and build failures due to memory errors&lt;/a&gt;. As development teams adopt Amplify Hosting, they expect scalable build environments that can match a variety of workloads. While build time optimizations exist, applications cannot grow within a fixed instance size container configuration of 8GiB memory and 4 vCPUs. Virtual memory (swap space) introduces significant performance penalties compared to physical memory, and no effective alternatives exist for expanding compute capacity or storage within a fixed environment. These hardware limitations create fundamental barriers that software optimizations alone cannot overcome.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Build Instance Specifications&lt;/strong&gt;&lt;/p&gt; 
&lt;table&gt; 
 &lt;thead&gt; 
  &lt;tr&gt; 
   &lt;th&gt;&lt;strong&gt;Build Instance&lt;/strong&gt;&lt;/th&gt; 
   &lt;th&gt;&lt;strong&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;vCPUs&lt;/strong&gt;&lt;/th&gt; 
   &lt;th&gt;&lt;strong&gt;&amp;nbsp; &amp;nbsp; Memory&lt;/strong&gt;&lt;/th&gt; 
   &lt;th&gt;&lt;strong&gt;&amp;nbsp; &amp;nbsp;Disk space&lt;/strong&gt;&lt;/th&gt; 
  &lt;/tr&gt; 
 &lt;/thead&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Standard&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;4&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; 8 GiB&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; &amp;nbsp; 128 GB&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Large&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; &amp;nbsp; 8&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; 16 GiB&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; &amp;nbsp; 128 GB&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;XLarge&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; &amp;nbsp; 36&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; 72 GiB&lt;/td&gt; 
   &lt;td&gt;&amp;nbsp; &amp;nbsp; 256 GB&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;h2&gt;&lt;/h2&gt; 
&lt;h2&gt;Introducing Customizable Build Instances&lt;/h2&gt; 
&lt;p&gt;Amplify Hosting’s new customizable build instances allow developers to select the computing resources that best match their application needs. This allows achieving predictable build performance by eliminating resource constrains. In addition to our Standard build instance, we are introducing Large and XLarge instances. The following table summarizes the specifications of all of Amplify Hosting’s build instance offerings:&lt;/p&gt; 
&lt;p&gt;You can configure build instances at the application level either during the app creation or later udpating your existing applications. Updated configuration will extend to all of your app’s branches and change an app’s build instance size across builds. Any build running for the app, irrespective of the branch, will use the build instance size that you configured at the app level when you triggered the build. You can configure build instance size when creating a new app or updating an existing app.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;span style="text-decoration: underline"&gt;To customize build instance size for a new app via Amplify console:&lt;/span&gt; During the &lt;strong&gt;App Settings&lt;/strong&gt; step, scroll down and click on &lt;strong&gt;Advanced settings&lt;/strong&gt;. From the drop down list, select the Build Instance type you would like to configure for your app.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;div id="attachment_14137" style="width: 1012px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14137" loading="lazy" class="wp-image-14137 " src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-28-at-12.07.22 AM.png" alt="Screenshot of amplify hosting console with a dropdown to select build instances" width="1002" height="329"&gt;
 &lt;p id="caption-attachment-14137" class="wp-caption-text"&gt;Figure 1 – Configure build instance size for a new app&lt;/p&gt;
&lt;/div&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;span style="text-decoration: underline"&gt;To customize build instance size for an existing app via Amplify console:&lt;/span&gt; Navigate to your app, open the &lt;strong&gt;Hosting tab&lt;/strong&gt;, and select &lt;strong&gt;Build Settings&lt;/strong&gt;. Scroll down to &lt;strong&gt;Advanced settings&lt;/strong&gt; and click &lt;strong&gt;Edit&lt;/strong&gt;. From the drop down, select the &lt;strong&gt;Build Instance type&lt;/strong&gt; you would like to update your app with.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;div id="attachment_14139" style="width: 806px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14139" loading="lazy" class="wp-image-14139" title="test" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-28-at-12.10.14 AM.png" alt="Screenshot of build instances setting in an existing app settings" width="796" height="299"&gt;
 &lt;p id="caption-attachment-14139" class="wp-caption-text"&gt;Figure 2 – Modify build instance for an existing app&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;&lt;span style="text-decoration: underline"&gt;Note&lt;/span&gt;: Build instance allocation process can require additional provisioning time before your build starts. For larger instances, especially XLarge, your build might experience latency before the build starts, due to this overhead time. However, you are billed only for the actual build time, not the overhead time.&lt;/p&gt; 
&lt;h2&gt;Performance Testing Explanation&lt;/h2&gt; 
&lt;p&gt;Now we will demonstrate the value of more powerful instances by using a Next.js application that generates more than 100 static pages. We will walk you through the deployment of this app on Amplify Hosting, show you how to debug memory-related build failures, and teach you how to upgrade the build instance to meet your application’s memory needs.&lt;/p&gt; 
&lt;p&gt;Deploying an application to Amplify HostingWe will first deploy the static app using the default settings:&lt;/p&gt; 
&lt;div id="attachment_14141" style="width: 881px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14141" loading="lazy" class=" wp-image-14141" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-28-at-12.26.23 AM.png" alt="Screenshot of the review page in Amplify Hosting" width="871" height="421"&gt;
 &lt;p id="caption-attachment-14141" class="wp-caption-text"&gt;Figure 3 – Review and create an app page&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Once the deployment starts, Amplify Hosting provision the resources and carry the deployment further:&lt;/p&gt; 
&lt;div id="attachment_14142" style="width: 866px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14142" loading="lazy" class=" wp-image-14142" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-27-at-7.57.49 PM.png" alt="Screenshot of build deploy screen" width="856" height="237"&gt;
 &lt;p id="caption-attachment-14142" class="wp-caption-text"&gt;Figure 4 – Deployment in progress&lt;/p&gt;
&lt;/div&gt; 
&lt;h4&gt;&lt;/h4&gt; 
&lt;h3&gt;Configuring app to use large memory&lt;/h3&gt; 
&lt;p&gt;After deployment starts, our app will fail its first build due to JavaScript heap out of memory issue. The runtime environment requests the heap memory, and the host operating system should allocate it. The JavaScript Node.js V8 runtime environment enforces a default heap size limit, which depends on several factors including the host memory size. That is why Standard and Large build instances use a default Node.js heap size of 2096 MB, while the XLarge instance uses a default heap size of 4144 MB.&lt;/p&gt; 
&lt;div id="attachment_14143" style="width: 816px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14143" loading="lazy" class=" wp-image-14143" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-28-at-12.30.00 AM.png" alt="Screenshot of a failed deployment in the amplify hosting console" width="806" height="408"&gt;
 &lt;p id="caption-attachment-14143" class="wp-caption-text"&gt;Figure 5 – Deployment 1 failed due to heap size memory limit&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;We can solve the conservative Node.js default heap memory limit issue by increasing the heap size. Since the app is using Standard build instance with 8 GiB memory, we will need to increase the heap size to 7000 MB which is ~7 GB. This leaves approximately 1 GB memory for operating system to use for other processes and avoids unintended behavior.&lt;/p&gt; 
&lt;p&gt;Next, we will modify the build specification with the following command to increase Node.js heap memory limit.&lt;/p&gt; 
&lt;p&gt;&lt;code&gt;export NODE_OPTIONS='--max-old-space-size=7000'&lt;/code&gt;&lt;/p&gt; 
&lt;p&gt;Alternately, &lt;code&gt;NODE_OPTIONS&lt;/code&gt; can also be set as an environment variable in our app. Refer documentation for more details.&lt;/p&gt; 
&lt;p&gt;We will also update the build spec file as following to accommodate these changes in our build flow:&lt;/p&gt; 
&lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-yaml"&gt;version: 1
frontend:
  phases:
    preBuild:
      commands:
        # Set the heap size to 7000 MB
        - export NODE_OPTIONS='--max-old-space-size=7000'
        # To check the heap size memory limit in MB
        - node -e "console.log('Total available heap size (MB):', v8.getHeapStatistics().heap_size_limit / 1024 / 1024)"
        - npm ci --cache .npm --prefer-offline
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: .next
    files:
      - '**/*'
  cache:
    paths:
      - .next/cache/**/*
      - .npm/**/*&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;We will trigger a new deployment with the modified heap size by going to the Deployments page and clicking on Redeploy this version button&lt;/p&gt; 
&lt;p&gt;However, the app still fails to build. This time, we see a log stating “Total available heap size (MB): 7048” that confirms our increased heap size. Interestingly, we also see a SIGKILL directive that indicates the operating system forcefully terminated the build process. This indicates another out of memory error.&lt;/p&gt; 
&lt;div id="attachment_14144" style="width: 869px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14144" loading="lazy" class=" wp-image-14144" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-28-at-12.49.25 AM.png" alt="Deployment 2 failed due to out of memory" width="859" height="434"&gt;
 &lt;p id="caption-attachment-14144" class="wp-caption-text"&gt;Figure 6 – Deployment 2 failed due to out of memory&lt;/p&gt;
&lt;/div&gt; 
&lt;h3&gt;&lt;/h3&gt; 
&lt;h3&gt;Upgrading build instance&lt;/h3&gt; 
&lt;p&gt;&lt;em&gt;Standard&lt;/em&gt; instance has a memory limit of 8 GiB and despite increasing the heap size close to this limit. Because of that, we are still running out of memory. We can solve this issue by upgrading our build instance to Large. This way we will allocate more memory to our build process.&lt;/p&gt; 
&lt;p&gt;We will update the build spec file as following to deploy on &lt;em&gt;Large&lt;/em&gt; instance:&lt;/p&gt; 
&lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-yaml"&gt;version: 1
frontend:
  phases:
    preBuild:
      commands:
        # Set the heap size to 14000 MB
        - export NODE_OPTIONS='--max-old-space-size=14000'
        # To check the heap size memory limit in MB
        - node -e "console.log('Total available heap size (MB):', v8.getHeapStatistics().heap_size_limit / 1024 / 1024)"
        - npm ci --cache .npm --prefer-offline
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: .next
    files:
      - '**/*'
  cache:
    paths:
      - .next/cache/**/*
      - .npm/**/*

&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;We will update the build instance by opening the&lt;strong&gt; Hosting tab&lt;/strong&gt;, and selecting &lt;strong&gt;Build settings&lt;/strong&gt;. Afterwards, scroll down to &lt;strong&gt;Advanced settings&lt;/strong&gt; and clicking on &lt;strong&gt;Edit.&lt;/strong&gt; From the drop down, select the &lt;strong&gt;Build instance type&lt;/strong&gt; as Large.&lt;/p&gt; 
&lt;div id="attachment_14145" style="width: 928px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14145" loading="lazy" class="wp-image-14145" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-28-at-12.59.46 AM.png" alt="Screenshot of upgrading the instance to large using the dropdown in the amplify hosting console " width="918" height="253"&gt;
 &lt;p id="caption-attachment-14145" class="wp-caption-text"&gt;Figure 7 – Update build instance size to Large&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Next, on the &lt;strong&gt;Deployments&lt;/strong&gt; page, we will click on &lt;strong&gt;Redeploy this version&lt;/strong&gt; to create a new deployment with Large instance. We also notice the first log line in build logs showing the build instance specifications.&lt;/p&gt; 
&lt;div id="attachment_14146" style="width: 867px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14146" loading="lazy" class=" wp-image-14146" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/tt.png" alt="Deployment 3 in progress on Large build instance" width="857" height="432"&gt;
 &lt;p id="caption-attachment-14146" class="wp-caption-text"&gt;Figure 8 – Deployment 3 in progress on Large build instance&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;We can see that our app is deployed successfully.&lt;/p&gt; 
&lt;div id="attachment_14147" style="width: 935px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-14147" loading="lazy" class=" wp-image-14147" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-28-at-1.19.25 AM.png" alt="App is deployed" width="925" height="309"&gt;
 &lt;p id="caption-attachment-14147" class="wp-caption-text"&gt;Figure 10 – App is deployed&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this blog, we have explored how you can create and update app workflows for configuring build instance sizes and demonstrated how you can solve memory issues in your projects hosted by Amplify Hosting. To explore further about build instance sizes and learn more about this feature, check out the &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/custom-build-instance.html"&gt;Amplify Hosting documentation&lt;/a&gt;. For information on pricing for build instances, you can check our &lt;a href="https://aws.amazon.com/amplify/pricing/"&gt;pricing page&lt;/a&gt;.&lt;/p&gt; 
&lt;h3&gt;&lt;/h3&gt; 
&lt;h2&gt;About the Authors&lt;/h2&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignleft wp-image-14148 size-thumbnail" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Sohaib-scaled-e1748544722427-150x150.jpg" alt="Photo of Sohaib Headshot" width="150" height="150"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Sohaib Uddin Syed, Software Engineer, Amplify Hosting&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Sohaib Uddin Syed is a Software Development Engineer at AWS Amplify Hosting. Sohaib builds software that enables customers to build their front-end web applications using the scalability of AWS. In his free time, Sohaib enjoys watching football (soccer), cooking and spending time with family and friends.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="size-thumbnail wp-image-14149 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/29/Screenshot-2025-05-29-at-9.50.45 AM-150x150.png" alt="Angela Zheng headshot" width="150" height="150"&gt;&lt;strong&gt;Angela Zheng, Software Engineer, Amplify Hosting&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Angela Zheng is a Software Development Engineer at AWS Amplify Hosting. She works on creating features that make hosting front-end web applications accessible and straightforward for developers at any scale. In her free time, she enjoys dancing, playing a game of volleyball, experimenting with recipes in the kitchen, and traveling.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class=" wp-image-12567 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/02/19/Screenshot-2024-02-19-at-3.13.45 PM.png" alt="Matt Headshot" width="149" height="153"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Matt Auerbach, Sr Product Manager, Amplify Hosting&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Matt Auerbach is a NYC-based Product Manager on the AWS Amplify Team. He educates developers regarding products and offerings, and acts as the primary point of contact for assistance and feedback. Matt is a mild-mannered programmer who enjoys using technology to solve problems and making people’s lives easier. B night, however…well he does pretty much the same thing. You can find Matt on&amp;nbsp;X @mauerbac. He previously worked at Twitch, Optimizely &amp;amp; Twilio.&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Simplify AWS AppSync Events integration with Powertools for AWS Lambda</title>
		<link>https://aws.amazon.com/blogs/mobile/simplify-aws-appsync-events-integration-with-powertools-for-aws-lambda/</link>
		
		<dc:creator><![CDATA[Ana Falcao]]></dc:creator>
		<pubDate>Wed, 14 May 2025 12:27:22 +0000</pubDate>
				<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[AWS Lambda]]></category>
		<category><![CDATA[Developer Tools]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<category><![CDATA[AppSync]]></category>
		<guid isPermaLink="false">8ca92941c012ec23ed29ac2d092940c3513921e8</guid>

					<description>Real-time capabilities have become essential in modern applications, where users expect immediate updates and interactive experiences. Whether you’re building chat applications, live dashboards, gaming leaderboards, or IoT systems, AWS AppSync Events enables these real-time features through WebSocket APIs, allowing you to build scalable and performant real-time applications, without worrying about scale or connection management. Powertools […]</description>
										<content:encoded>&lt;p&gt;Real-time capabilities have become essential in modern applications, where users expect immediate updates and interactive experiences. Whether you’re building chat applications, live dashboards, gaming leaderboards, or IoT systems, &lt;a href="https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-welcome.html"&gt;AWS AppSync Events&lt;/a&gt; enables these real-time features through WebSocket APIs, allowing you to build scalable and performant real-time applications, without worrying about scale or connection management.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://powertools.aws.dev/"&gt;Powertools for AWS Lambda&lt;/a&gt; is a developer toolkit that includes observability, batch processing, &lt;a href="https://aws.amazon.com/systems-manager/"&gt;AWS Systems Manager&lt;/a&gt; Parameter Store integration, idempotency, feature flags, &lt;a href="https://aws.amazon.com/cloudwatch/"&gt;Amazon CloudWatch&lt;/a&gt; Metrics, structured logging, and more. Powertools for AWS now supports AppSync Events through the new &lt;code&gt;AppSyncEventsResolver&lt;/code&gt;, available in Python, TypeScript, and .NET. This new feature enhances the development experience with capabilities designed to help you focus on your business logic. The &lt;code&gt;AppSyncEventsResolver&lt;/code&gt; provides a simple and consistent interface for processing events, with built-in support for common patterns such as filtering, transforming, and routing events.&lt;/p&gt; 
&lt;p&gt;In this post, you will see examples in &lt;a href="https://docs.powertools.aws.dev/lambda/typescript/latest/"&gt;TypeScript&lt;/a&gt;, but you can use the same functionality in Python and .NET functions using &lt;a href="https://docs.powertools.aws.dev/lambda/python/latest/"&gt;Powertools for AWS (Python)&lt;/a&gt; and &lt;a href="https://docs.powertools.aws.dev/lambda/dotnet/"&gt;Powertools for AWS (.NET)&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter wp-image-14101 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/13/pic1-1.png" alt="Real-time event handling architecture using AWS AppSync, Lambda, and Powertools" width="936" height="412"&gt;&lt;/p&gt; 
&lt;p style="text-align: center"&gt;Figure 1 Real-time event handling architecture using AWS AppSync, Lambda, and Powertools&lt;/p&gt; 
&lt;p&gt;In this post, you’ll learn how to:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Set up event handlers using the &lt;code&gt;AppSyncEventsResolver&lt;/code&gt;&lt;/li&gt; 
 &lt;li&gt;Implement different event processing patterns for optimal performance&lt;/li&gt; 
 &lt;li&gt;Use pattern-based routing to organize your event handlers&lt;/li&gt; 
 &lt;li&gt;Leverage built-in features for common use cases&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Getting Started&lt;/h2&gt; 
&lt;p&gt;The &lt;code&gt;AppSyncEventsResolver&lt;/code&gt; provides a simple, declarative way to handle AppSync Events in your &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt; functions. The event resolver allows you to listen for &lt;code&gt;PUBLISH&lt;/code&gt; and &lt;code&gt;SUBSCRIBE&lt;/code&gt; events. &lt;code&gt;PUBLISH&lt;/code&gt; events occur when clients send messages to a channel, while &lt;code&gt;SUBSCRIBE&lt;/code&gt; events happen when clients attempt to subscribe to a channel. You can register handlers for different namespaces and channels to manage your event-driven communications.&lt;/p&gt; 
&lt;p&gt;Let’s explore how to get started and the core features that will enhance your development experience. Here’s a basic example of how to set up the &lt;code&gt;AppSyncEventsResolver&lt;/code&gt;:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import {
  AppSyncEventsResolver,
  UnauthorizedException,
} from '@aws-lambda-powertools/event-handler/appsync-events';

// Types for our message handling
type ChatMessage = {
    userId: string;
    content: string;
}

// Simple authorization check
const isAuthorized = (path: string, userId?: string): boolean =&amp;gt; {
    // check against your authorization system
    if (path.startsWith('/chat/private') &amp;amp;&amp;amp; !userId) {
        return false;
    }
    return true;
};

// Message processing logic
const processMessage = async (payload: ChatMessage) =&amp;gt; {
    // - Validate message content
    // - Store in database
    // - Enrich with additional data
    return {
        ...payload,
        timestamp: new Date().toISOString()
    };
};

const app = new AppSyncEventsResolver();

// Handle publish events for a specific channel
app.onPublish('/chat/general', async (payload: ChatMessage) =&amp;gt; {
    // Process and return the message
    return processMessage(payload);
});

// Handle subscription events for all channels
app.onSubscribe('/*', async (info) =&amp;gt; {
    const {
        channel: { path },
        request,
    } = info;

    // Perform access control checks
    if (!isAuthorized(path, userId)) {
        throw new UnauthorizedException(`not allowed to subscribe to ${path}`);
    }

    return true;
});

export const handler = async (event, context) =&amp;gt;
  app.resolve(event, context);&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;The &lt;code&gt;AppSyncEventsResolver&lt;/code&gt; class takes care of parsing the incoming event data and invoking the appropriate handler method based on the event type. Let’s break down what’s happening:&lt;/p&gt; 
&lt;h3&gt;Pattern-based Routing&lt;/h3&gt; 
&lt;p&gt;The &lt;code&gt;AppSyncEventsResolver&lt;/code&gt; uses an intuitive pattern-based routing system that allows you to organize your event handlers based on channel paths. You can:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Handle specific channels (/chat/general)&lt;/li&gt; 
 &lt;li&gt;Use wildcards for namespaces (/chat/*)&lt;/li&gt; 
 &lt;li&gt;Create global catch-all handlers (/*)&lt;/li&gt; 
&lt;/ul&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';

const app = new AppSyncEventsResolver();

// Specific channel handler
app.onPublish('/notifications/alerts', async (payload) =&amp;gt; {
    // your logic here
});

// Handle all channels in the notifications namespace
app.onPublish('/notifications/*', async (payload) =&amp;gt; {
    // your logic here
});

// Global catch-all for unhandled channels
app.onPublish('/*', async (payload) =&amp;gt; {
    // your logic here
});

export const handler = async (event, context) =&amp;gt;
  app.resolve(event, context);
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;The most general catch-all handler is &lt;code&gt;/*&lt;/code&gt;, which will match any namespace and channel, while &lt;code&gt;/default/*&lt;/code&gt; will match any channel in the &lt;code&gt;default&lt;/code&gt; namespace. When multiple handlers match the same event, the library will call the most specific handler and ignore the less specific ones. For example, if a handler is registered for &lt;code&gt;/default/channel1&lt;/code&gt;&amp;nbsp;and another one for &lt;code&gt;/default/*&lt;/code&gt;, Powertools will call the first handler for events that match &lt;code&gt;/default/channel1&lt;/code&gt;&amp;nbsp;and ignore the second one. This provides you control over how events are handled and to avoid unnecessary processing. If an event that does not match any handler, by default Powertools will return the events as is and log a warning. This means that the events will be passed through without any modifications. This approach is helpful for gradually adopting the library, allowing you to handle specific events with custom logic while others are processed by the default behavior.&lt;/p&gt; 
&lt;h3&gt;Subscription Handling&lt;/h3&gt; 
&lt;p&gt;Powertools also provides a simple way to handle subscription events. It will automatically parse the incoming event and call the appropriate handler based on the event type. By default, AppSync allows the subscription unless your Lambda handler either throws an error or explicitly rejects the request. When a subscription is rejected, AppSync will return a 4xx response to the client and prevent the subscription from being established.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics';
import type { Context } from 'aws-lambda';

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'chat',
  singleMetric: true,
});
const app = new AppSyncEventsResolver();

app.onSubscribe('/default/foo', (event) =&amp;gt; {
  metrics.addDimension('channel', event.info.channel.path);
  metrics.addMetric('connections', MetricUnit.Count, 1);
});

export const handler = async (event: unknown, context: Context) =&amp;gt;
  app.resolve(event, context);
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;The library calls the appropriate handler and passes the event object as the first argument when a subscription event arrives. You can take any necessary actions based on the subscription event, such as running access control checks:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;app.onSubscribe('/private/*', async (info) =&amp;gt; {
  const userGroups =
    info.identity?.groups &amp;amp;&amp;amp; Array.isArray(info.identity?.groups)
      ? info.identity?.groups
      : [];
  const channelGroup = 'premium-users';

  if (!userGroups.includes(channelGroup)) {
    throw new UnauthorizedException(
      `Subscription requires ${channelGroup} group membership`
    );
  }
})
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Subscription events follow the same matching rules and provide the same access to the full event and context. You can register catch-all handlers for any namespace or channel by using the wildcard &lt;code&gt;*&lt;/code&gt;&amp;nbsp;character, and also access the full event and context objects in your handlers.&lt;/p&gt; 
&lt;h3&gt;Access full event and context&lt;/h3&gt; 
&lt;p&gt;While the resolver simplifies event handling, you still have full access to the event and context objects when needed. This is helpful in scenarios where you need additional information, such as request headers or the remaining execution time from the Lambda context, to implement custom logic.&lt;/p&gt; 
&lt;p&gt;The resolver passes the full event and context to each handler as the second and third arguments. This lets you access all relevant information without changing your existing code.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Logger } from '@aws-lambda-powertools/logger';

const logger = new Logger({
  logLeveL: 'INFO',
  serviceName: 'serverlessAirline'
});
const app = new AppSyncEventsResolver({ logger});

app.onPublish('/orders/process', async (payload, event, context) =&amp;gt; {
    // Access request headers
    const { headers } = event.request;
    
    // Access Lambda context
    const { getRemainingTimeInMillis } = context;

    logger.info('Processing event details', {
        headers,
        remainingTime: getRemainingTimeInMillis()
    });

    return payload;
});

export const handler = async (event, context) =&amp;gt;
  app.resolve(event, context);
&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;Error handling&lt;/h3&gt; 
&lt;p&gt;The &lt;code&gt;AppSyncEventsResolver&lt;/code&gt; offers built-in error handling that prevents Lambda function failures while ensuring errors are properly communicated to AppSync, which then propagates them to the clients. When an error occurs in your handler, instead of failing the entire Lambda invocation, the resolver captures the error and includes it in the response payload for the specific event that failed.&lt;/p&gt; 
&lt;p&gt;This approach ensures the Lambda function continues execution while providing properly formatted error messages to AppSync. When processing multiple events, if one event fails, the others continue processing normally. This is particularly useful in parallel processing scenarios where you want to ensure that an error in one event doesn’t affect the processing of other events.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';

const app = new AppSyncEventsResolver();

app.onPublish('/messages', async (payload) =&amp;gt; {
    // If message contains "error", throw an exception
    if (payload.message === "error") {
        throw new Error("Invalid message");
    }
    return payload;
});

export const handler = async (event, context) =&amp;gt;
  app.resolve(event, context);

// When processing this event:
// {
//     "id": "123",
//     "payload": {
//         "message": "error"
//     }
// }

// The resolver will return:
// {
//     "id": "123",
//     "error": "Error - Invalid message"
// }
&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Advanced Patterns and Best Practices&lt;/h2&gt; 
&lt;p&gt;The &lt;code&gt;AppSyncEventsResolver&lt;/code&gt; has additional advanced features that help you build robust and maintainable real-time applications. Let’s explore these capabilities and how to use them effectively.&lt;/p&gt; 
&lt;h3&gt;On publish processing&lt;/h3&gt; 
&lt;p&gt;By default, we call your route handler once per message. This allows you to focus on your business logic and avoid writing boilerplate code while Powertools handles message iteration and converts the event and response format. You only need to return the value you want to use as payload, or throw an error for that message. The library will then correlate the payload with the correct event id.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics';

type SensorReading = {
    deviceId: string;
    temperature: number;
    humidity: number;
    timestamp: string;
}

const app = new AppSyncEventsResolver();
const metrics = new Metrics({ namespace: 'SensorReadings' });

app.onPublish('/sensors/readings', async (payload: SensorReading) =&amp;gt; {
    // Process each sensor reading independently
    if (payload.temperature &amp;gt; 100) {
        metrics.addDimension('alertType', 'highTemperature');
        metrics.addMetric('HighTemperature', MetricUnit.Count, 1);
        throw new Error('Temperature reading too high');
    }

    // Enrich the payload with processing timestamp
    return {
        ...payload,
        processed: true,
        processedAt: new Date().toISOString()
    };
});

export const handler = async (event, context) =&amp;gt;
  app.resolve(event, context);
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This pattern simplifies development by letting you write only the logic for a single event, Powertools handles the rest automatically.&lt;/p&gt; 
&lt;h3&gt;Aggregate Processing&lt;/h3&gt; 
&lt;p&gt;The aggregate mode lets you to process multiple events as a single batch, rather than handling them individually. This is particularly useful when you want to optimize resource usage, such as sending multiple queries to a database in a single operation, or to analyze multiple events together before processing them. While both modes give you full control over event processing, aggregate mode provides access to the entire list of events at once.&lt;/p&gt; 
&lt;p&gt;To achieve this, you can set the &lt;code&gt;aggregate&lt;/code&gt; option to &lt;code&gt;true&lt;/code&gt;. When using this mode, the resolver sends the entire list of events to your handler in a single call, letting you process them as a batch.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';

const app = new AppSyncEventsResolver();

app.onPublish('/default/*', async (events) =&amp;gt; {
  const results = [];
  for (const event of events) {
    try {
      results.push(await handleDefaultNamespaceCatchAll(event));
    } catch (error) {
      results.push({
        error: {
          errorType: 'Error',
          message: error.message,
        },
        id: event.id,
      });
    }
  }

  return results;
}, {
  aggregate: true,
});

export const handler = async (event, context) =&amp;gt;
  app.resolve(event, context);
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Note that the &lt;code&gt;aggregate&lt;/code&gt;&amp;nbsp;option is only available for publish events, and that when using this option, you are responsible for handling the events and returning the appropriate response. Powertools will still take care of routing the events to the correct handler, but you have full control over how the events are processed.&lt;/p&gt; 
&lt;h3&gt;Event Filtering&lt;/h3&gt; 
&lt;p&gt;To filter out an event, throw an error in the channel handler. If a handler throws an error for a specific event, the library catches it and adds an error object to the response list at the same index. This signals that the corresponding message should be dropped. This allows you to either silently filter events or provide meaningful error feedback to your subscribers.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';

app.onPublish('/moderation/*', async (payload) =&amp;gt; {
    // Filter out inappropriate content
    if (await containsInappropriateContent(payload)) {
        throw new CustomError('Content violates guidelines');
    }

    // Process valid content
    return await processContent(payload);
});

export const handler = async (event, context) =&amp;gt;
  app.resolve(event, context);&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;The &lt;code&gt;AppSyncEventsResolver&lt;/code&gt; in Powertools for AWS enhances your development experience with AppSync Events by providing a simple and consistent interface for processing real-time events. By reducing boilerplate code and offering built-in support for common patterns, you can focus on your business logic rather than infrastructure code.&lt;/p&gt; 
&lt;p&gt;To learn more:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Explore the &lt;a href="https://docs.powertools.aws.dev/lambda/typescript/latest/"&gt;Powertools for AWS documentation&lt;/a&gt; for detailed information&lt;/li&gt; 
 &lt;li&gt;Check out the &lt;a href="https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-welcome.html"&gt;AppSync Events documentation&lt;/a&gt; to learn more about the service&lt;/li&gt; 
 &lt;li&gt;Visit our &lt;a href="https://github.com/aws-powertools"&gt;GitHub repository&lt;/a&gt; to contribute or report issues&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;We’re excited to see what you build with these new capabilities. Share your feedback and let us know how you’re using &lt;code&gt;AppSyncEventsResolver&lt;/code&gt; in your applications!&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>From Build to Embed: Creating and Embedding GenAI Apps with AWS Amplify, CDK, and Amazon Q Business</title>
		<link>https://aws.amazon.com/blogs/mobile/from-build-to-embed-creating-and-embedding-genai-apps-with-aws-amplify-cdk-and-amazon-q-business/</link>
		
		<dc:creator><![CDATA[Ben-Amin York Jr.]]></dc:creator>
		<pubDate>Tue, 06 May 2025 21:41:20 +0000</pubDate>
				<category><![CDATA[Amazon Q Business]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[AWS Cloud Development Kit]]></category>
		<category><![CDATA[Developer Tools]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Amplify]]></category>
		<guid isPermaLink="false">220fe82e300570e4c7c64e3bee65a6c517c8ce91</guid>

					<description>In a enterprise landscape, custom applications play a critical role in improving operations, enhancing productivity, and centralizing knowledge within the organization. However, these tools often lack intelligent, conversational interfaces that help users access relevant information faster and more intuitively. Traditional dashboards and search bars fall short when it comes to interpreting complex queries or surfacing […]</description>
										<content:encoded>&lt;p&gt;In a enterprise landscape, custom applications play a critical role in improving operations, enhancing productivity, and centralizing knowledge within the organization. However, these tools often lack intelligent, conversational interfaces that help users access relevant information faster and more intuitively. Traditional dashboards and search bars fall short when it comes to interpreting complex queries or surfacing contextual insights from vast organizational data.&lt;/p&gt; 
&lt;p&gt;Generative AI offers a powerful solution to this challenge. By embedding conversational experiences directly into developer-controlled applications, organizations can enable users to ask questions in natural language and receive precise, actionable responses. &lt;a href="https://aws.amazon.com/q/business/"&gt;Amazon Q Business&lt;/a&gt; delivers this capability via a secure, &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qbusiness-ug/embed-amazon-q-business.html"&gt;embeddable HTML inline frame&lt;/a&gt; (iframe)—without the burden of managing large language model infrastructure.&lt;/p&gt; 
&lt;p&gt;This blog is aimed at developers building custom or enterprise-owned applications—whether knowledge portals, support dashboards, or internal web tools. It shows how to embed a generative AI-powered conversational experience using Amazon Q Business, &lt;a href="https://docs.amplify.aws/react/how-amplify-works/concepts/"&gt;AWS Amplify Gen 2&lt;/a&gt;, and the &lt;a href="https://aws.amazon.com/cdk/"&gt;AWS Cloud Development Kit&lt;/a&gt; (CDK). Embedding Amazon Q Business into applications requires access to the application’s source code and is not supported in third-party SaaS platforms where embedding custom code is not possible.&lt;/p&gt; 
&lt;p&gt;&lt;b&gt;The presented approach enables&lt;/b&gt;:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Conversational access to internal documents and knowledge bases.&lt;/li&gt; 
 &lt;li&gt;Secure integration with enterprise identity management systems.&lt;/li&gt; 
 &lt;li&gt;Scalable, AI-driven search without complex backend implementations.&lt;/li&gt; 
 &lt;li&gt;Rapid deployment using AWS Amplify’s capabilities for frontend and backend development.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;With Amazon Q Business and AWS Amplify, you can quickly add generative AI to your apps—to boost productivity, reduce manual effort, and accelerate decision-making.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/app1.gif" alt="Opening code in VSCode." width="1430" height="1200"&gt;&lt;em&gt;Figure 1 Submitting a prompt to Amazon Q Business iframe.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;To embed a generative AI assistant into your internal application, you’ll leverage the following AWS services:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;&lt;a href="https://docs.amplify.aws/"&gt;AWS Amplify&lt;/a&gt;:&lt;/strong&gt; A comprehensive set of tools and services that help developers build, deploy, and manage secure full-stack applications. It simplifies frontend and backend development by integrating tightly with services like Amazon Cognito for authentication, Amazon S3 for storage, and the CDK for building additional AWS infrastructure.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;&lt;a href="https://aws.amazon.com/cognito/"&gt;Amazon Cognito&lt;/a&gt;:&lt;/strong&gt; A managed service for adding authentication and authorization to your app. Cognito supports user sign-up, sign-in, and access control, and can be federated through an external identity provider (IdP) for enterprise access management.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;&lt;a href="https://aws.amazon.com/iam/identity-center/"&gt;AWS IAM Identity Center&lt;/a&gt;:&lt;/strong&gt; IAM Identity Center allows secure, centralized access management for your internal users. It supports identity federation with enterprise providers like Okta, Microsoft Entra ID, Ping Identity, and others. This enables your organization to enforce unified authentication policies and ensure only authorized users can interact with the embedded AI assistant.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;&lt;a href="https://aws.amazon.com/q/business/"&gt;Amazon Q Business&lt;/a&gt;:&lt;/strong&gt; A managed generative AI service that can be embedded via iframe into internal applications. Q Business connects to enterprise data sources, such as Amazon S3, and enables natural language querying through an intelligent assistant interface. It supports secure access and integrates with IAM Identity Center for federated enterprise use.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;&lt;a href="https://aws.amazon.com/s3/"&gt;Amazon Simple Storage Service (S3)&lt;/a&gt;:&lt;/strong&gt; A durable and scalable object storage service used to store internal documents, PDFs, manuals, or any unstructured content. These files act as the knowledge base that powers the Amazon Q Business assistant, enabling contextual responses to employee queries.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/archit.png" alt="Opening code in VSCode." width="1430" height="1200"&gt;&lt;em&gt;Figure 2 From Build to Embed Architecture Diagram&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Prerequisites&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;An &lt;a href="https://signin.aws.amazon.com/signup?request_type=register"&gt;AWS account&lt;/a&gt;:&lt;/strong&gt; Note that AWS Amplify is part of the &lt;a href="https://aws.amazon.com/amplify/pricing/"&gt;AWS Free Tier&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Install:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt; (v9 or later), and &lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt; (v2.14.1 or later).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;A Text Editor:&lt;/strong&gt; For this guide we will use VSCode, but you can use your preferred IDE.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Sample Dataset:&lt;/strong&gt; Upload any PDF or explore sample data sets available on &lt;a&gt;Kaggle&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;IAM Identity Center:&lt;/strong&gt; You must &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/enable-identity-center.html#to-enable-identity-center-instance"&gt;enable an IAM Identity Center instance&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/addusers.html"&gt;add a user&lt;/a&gt; to your Identity Center directory.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Cloning the Repo&lt;/h2&gt; 
&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Navigate to the &lt;a href="https://github.com/aws-samples/sample-build-and-embed-genai-apps"&gt;repository&lt;/a&gt; on AWS Samples and fork it to your GitHub repositories.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Clone the app by running the command below in your terminal.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;git clone https://github.com/&amp;lt;YOUR_GITHUB_USERNAME&amp;gt;/sample-build-and-embed-genai-apps.git&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Access the newly cloned repository in VSCode by executing the commands below in your terminal.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;cd sample-build-and-embed-genai-apps 
code . -r &lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;VSCode will open the repository folder, including the Amplify folder, which contains the app code that you’ll review in the next section.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/ide-1.png" alt="Opening code in VSCode." width="1430" height="1200"&gt;&lt;em&gt;Figure 3 Opening code in VSCode.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Install the required packages, including the Amplify Gen 2 packages, by running the following command:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npm install&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;The Amplify Backend&lt;/h2&gt; 
&lt;p&gt;In the final app (as seen in the gif at the beginning of the post), users log into the application, click the chatbot icon, authenticate with federated access (via the &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qbusiness-ug/embed-web-experience.html"&gt;iframe&lt;/a&gt; to &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qbusiness-ug/create-application-iam.html"&gt;access Q Business web experience&lt;/a&gt;), and finally are able to begin asking questions to Amazon Q Business. The code for this is in the repository you cloned. Here, you’ll go over the key steps for creating your Amplify-developed and hosted search engine app.&lt;/p&gt; 
&lt;h6 style="text-align: center"&gt;&lt;img class="alignnone size-full wp-image-12994" style="width: 35%;height: auto" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/ampbackend.png" alt="Amplify Gen 2 Project Folder Structure."&gt;&lt;br&gt; &lt;em&gt;Figure 4 Amplify Gen 2 Project Folder Structure.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;In the &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/auth/resource.ts&lt;/code&gt;&lt;/span&gt; file (Figure 5), authentication is configured to require users to log in with their email to access the application and upload files. By enabling email-based login, you ensure only verified users can interact with sensitive data and functionalities.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { defineAuth } from '@aws-amplify/backend';
 
export const auth = defineAuth({
    loginWith: {
        email: true,
    },
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 5 defineAuth in amplify/auth/resource.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;In the &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/storage/resource.ts&lt;/code&gt;&lt;/span&gt; file (Figure 6), &lt;a href="https://docs.amplify.aws/react/build-a-backend/storage/"&gt;Amplify storage&lt;/a&gt; is configured to enable secure, user-scoped file management. The &lt;code&gt;defineStorage&lt;/code&gt; function instantiates the storage resource with a user-friendly name &lt;code&gt;q-datasource-bucket&lt;/code&gt; and applies access control to the &lt;code&gt;protected/{entity_id}/*&lt;/code&gt; path. This configuration allows authenticated users to read files within their own scoped directory, while granting the file owner permissions to read, write, and delete their content.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { defineStorage } from "@aws-amplify/backend";
 
export const storage = defineStorage({
    name: "q-datasource-bucket",
    access: (allow) =&amp;gt; ({
        'protected/{entity_id}/*': [
            allow.authenticated.to(['read']),
            allow.entity('identity').to(['read', 'write', 'delete'])
        ]
    })
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 6 defineStorage in amplify/storage/resource.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;In the &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/backend.ts&lt;/code&gt;&lt;/span&gt; (Figure 7) file, you import the CDK libraries to configure key aspects of your application. The &lt;code&gt;aws-iam&lt;/code&gt; module is used to manage permissions, &lt;code&gt;aws-kms&lt;/code&gt; handles encryption and key management, and &lt;code&gt;aws-qbusiness&lt;/code&gt; integrates Amazon Q Business into your stack. Each library plays a specific role in ensuring your application is secure and properly integrated with AWS services.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import * as iam from 'aws-cdk-lib/aws-iam';
import * as kms from 'aws-cdk-lib/aws-kms';
import * as q from 'aws-cdk-lib/aws-qbusiness';&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 7 import CDK libraries in amplify/backend.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;Next, use the &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;backend.createStack()&lt;/code&gt;&lt;/span&gt; (Figure 8) method to direct the backend to generate a new CloudFormation Stack to house custom resources. With AWS Amplify Gen 2, you can create custom resources using the CDK, enabling the use of services beyond the Amplify library, with stacks backed by &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html"&gt;CloudFormation&lt;/a&gt; templates for scalability. For example, you could create a Generative AI stack for AI-related services, ensuring logical organization before adding custom AWS resources. Now, you can begin defining custom AWS resources!&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;export const customResource = backend.createStack("CustomResourceStack");&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 8 define backend stack for custom resources in amplify/backend.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;During the &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;CustomResourceStack&lt;/code&gt;&lt;/span&gt; deployment, you’ll see several &lt;b&gt;IAM roles&lt;/b&gt;, &lt;b&gt;trust policies&lt;/b&gt;, and &lt;b&gt;inline access policies&lt;/b&gt; being created—all of which are critical for enabling Amazon Q Business to function securely and interact with other AWS services. These include:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;b&gt;Service Access Role&lt;/b&gt; (QApplicationServiceAccessRole) for Amazon Q Business to emit logs and metrics via CloudWatch.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;Web Experience Roles&lt;/b&gt; (QWebExperienceRole and QWebExperienceRole) for enabling the embedded assistant to function with full application context and user interactions.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;Data Source Role&lt;/b&gt; (QBusinessS3Role) specifically for allowing Amazon Q Business to read documents from the S3 bucket provisioned by Amplify Storage.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Each role includes a trust policy defining &lt;b&gt;who can assume the role&lt;/b&gt;, and a series of fine-grained permissions granting access to specific actions and resources. You can view the full required policy structure for Amazon Q Business data sources in the &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qbusiness-ug/iam-roles-ds.html#create-s3-datasource-iam-role"&gt;IAM roles documentation for Amazon Q Business connectors&lt;/a&gt;.&lt;/p&gt; 
&lt;h2&gt;Setting Up Amazon Q Business&lt;/h2&gt; 
&lt;p&gt;With all foundational IAM roles and policies in place, you’re ready to define the Amazon Q Business application, the core component that powers the embedded generative AI assistant. In your &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/backend.ts&lt;/code&gt;&lt;/span&gt; (Figure 9) file, using the CDK, you can instantiate it declaratively with the necessary configuration, subscription plan, and IAM integration:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;export const qapp = new q.CfnApplication(customResource, "Qapp", {
  displayName: "Qapp",
  description: "CDK instantiated Amazon Q Business App",
  autoSubscriptionConfiguration: {
    autoSubscribe: "ENABLED",
    defaultSubscriptionType: "Q_LITE"
  },
  identityType: "AWS_IAM_IDC",
  roleArn: `arn:aws:iam::${customResource.account}:role/aws-service-role/qbusiness.amazonaws.com/AWSServiceRoleForQBusiness`,
  /* REPLACE WITH YOUR IAM IDENTITY CENTER ARN */
  // identityCenterInstanceArn: "arn:aws:sso:::instance/",
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 9 defining Q Business Application in amplify/backend.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;This step formally creates the Q Business application and associates it with the service-linked role (&lt;b&gt;AWSServiceRoleForQBusiness&lt;/b&gt;). Be sure to substitute your IAM Identity Center ARN before deployment to enable federated access for users.&lt;/p&gt; 
&lt;h2&gt;Creating an Index&lt;/h2&gt; 
&lt;p&gt;Indexes in Amazon Q Business are used to store, organize, and retrieve enterprise data efficiently. You need an index to enable structured querying of stored documents, allowing the chatbot to retrieve relevant responses based on user queries. The following CDK code in your &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/backend.ts&lt;/code&gt;&lt;/span&gt; (Figure 10) sets up an index within your Amazon Q Business application:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;export const qIndex = new q.CfnIndex(customResource, "QIndex", {
  displayName: "QIndex",
  description: "CDK instantiated Amazon Q Business App index",
  applicationId: qapp.attrApplicationId,
  capacityConfiguration: {
    units: 1,
  },
  type: "STARTER",
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 10 defining Q Business Index in amplify/backend.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;Here, the STARTER index type is selected, which is a basic configuration suitable for testing and small-scale deployments. For production use, a higher tier like &lt;b&gt;ENTERPRISE&lt;/b&gt; would be necessary to ensure scalability, availability, and support for advanced features—see the &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qbusiness-ug/tiers.html"&gt;Amazon Q Business pricing and tiers&lt;/a&gt; for guidance.&lt;/p&gt; 
&lt;h2&gt;Creating a Retriever&lt;/h2&gt; 
&lt;p&gt;Retrievers enhance the search capabilities by fetching relevant documents from the index. In Amazon Q Business, a retriever connects to the index and ensures that queries return meaningful responses. The following CDK code in your &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/backend.ts&lt;/code&gt;&lt;/span&gt; (Figure 11) sets up a retriever within your Amazon Q Business application:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;export const qRetriever = new q.CfnRetriever(customResource, "QRetriever", {
  displayName: "QRetriever",
  applicationId: qapp.attrApplicationId,
  type: "NATIVE_INDEX",
  configuration: {
    nativeIndexConfiguration: {
      indexId: qIndex.attrIndexId,
    },
  }
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 11 defining Q Business Retriever in amplify/backend.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;This retriever is configured to work with the previously created &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;qIndex&lt;/code&gt;&lt;/span&gt;, ensuring efficient retrieval of indexed content for Amazon Q Business.&lt;/p&gt; 
&lt;h2&gt;Defining a Data Source&lt;/h2&gt; 
&lt;p&gt;A data source is where the Amazon Q Business application retrieves data for its knowledge base. In this case, the data source is an Amazon S3 bucket defined earlier in the Amplify Storage configuration &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/storage/resource.ts&lt;/code&gt;&lt;/span&gt; and referenced in the &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/backend.ts&lt;/code&gt;&lt;/span&gt; (Figure 12) file. This bucket stores documents that Amazon Q Business will index and analyze.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;export const qDataSource = new q.CfnDataSource(customResource, "QDataSource", {
  displayName: `${backend.storage.resources.bucket.bucketName}`,
  applicationId: qapp.attrApplicationId,
  indexId: qIndex.attrIndexId,
  configuration: {
    type: "S3",
    syncMode: "FULL_CRAWL",
    connectionConfiguration: {
      repositoryEndpointMetadata: {
        BucketName: backend.storage.resources.bucket.bucketName
      }
    },
    repositoryConfigurations: {
      document: {
        fieldMappings: [
          {
            indexFieldName: "s3_document_id",
            indexFieldType: "STRING",
            dataSourceFieldName: "s3_document_id"
          }
        ]
      }
    },
  },
  roleArn: qBusinessS3Role.roleArn
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 12 defining Q Business Data Source in amplify/backend.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;The key elements in this setup:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;b&gt;syncMode&lt;/b&gt;: “FULL_CRAWL” ensures that all documents in the S3 bucket are indexed.&lt;/li&gt; 
 &lt;li&gt;Field mappings define how document metadata is structured for indexing.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Creating the Amazon Q Business Web Experience&lt;/h2&gt; 
&lt;p&gt;The Web Experience in amazon Q business is a frontend that allows users to interact with Amazon Q Business embedded in a customized chatbot. This component is defined in your &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/backend.ts&lt;/code&gt;&lt;/span&gt; (Figure 13) file and defines how the chatbot interface appears and functions, including allowed website origins, branding, and authentication.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;export const qWebExperience = new q.CfnWebExperience(customResource, "QWebExperience", {
  applicationId: qapp.attrApplicationId,
  origins: [
    /* REPLACE WITH YOUR AMPLIFY DOMAIN URL */
    "https://main..amplifyapp.com",
  ],
  samplePromptsControlMode: "ENABLED",
  subtitle: "AnyCompany Generative AI Assistant",
  title: "AnyCompany Q App",
  welcomeMessage: "Welcome to your Amazon Q Business Application!",
  roleArn: qWebExperienceRole.roleArn
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 13 defining Q Business Web Experience in amplify/backend.ts&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Web Experience Configuration&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;b&gt;Origins&lt;/b&gt;: Specifies the website domains permitted to embed the chatbot via an &amp;lt;iframe&amp;gt;. Ensure that your Amplify app’s deployed URL is correctly listed here.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;samplePromptsControlMode&lt;/b&gt;: Enables storing predefined sample prompts within the chatbot UI.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;title &amp;amp; subtitle&lt;/b&gt;: Sets the chatbot’s display name and additional descriptions.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;welcomeMessage&lt;/b&gt;: The first message users see when they open the chat window.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Amazon Q Business and Frontend Integration&lt;/h2&gt; 
&lt;p&gt;After configuring the web experience, you can now &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qbusiness-ug/embed-web-experience.html"&gt;embed your Amazon Q Business web experience&lt;/a&gt; using an iframe. In &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;src/components/qframe.tsx&lt;/code&gt;&lt;/span&gt; (Figure 14), the src is dynamically populated from the &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify_outputs.json&lt;/code&gt;&lt;/span&gt; file, which contains the deployed URL of your Q Web Experience exported by the backend. Users are prompted to authenticate in a new browser tab, after which the chat session continues within the embedded iframe.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import React from 'react';
import rawOutputs from '../../amplify_outputs.json';
 
const outputs = rawOutputs as unknown as {
    custom: {
        q_business_url: string;
    };
};
 
const QFrame: React.FC = () =&amp;gt; {
    const qBusinessDeployedURL = outputs.custom.q_business_url;
 
    return (
        &amp;lt;iframe
            src={qBusinessDeployedURL}
            title="Chatbot"
            className="QFrame"
        /&amp;gt;
    );
};
 
export default QFrame;&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 14 Embeds Amazon Q Business via iframe using config URL.&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Running the App&lt;/h2&gt; 
&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Amplify provides each developer with a personal &lt;a href="https://docs.amplify.aws/react/deploy-and-host/sandbox-environments/setup/"&gt;cloud sandbox environment&lt;/a&gt;, offering isolated development spaces for rapid building, testing, and iteration. To initiate a cloud sandbox environment, open a new terminal window and execute the following command:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npx ampx sandbox&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Execute the following command to start a localhost development server.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npm run dev&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;After running the previous command to start the application, use the ‘&lt;b&gt;Create Account&lt;/b&gt;‘ feature of the &lt;a href="https://ui.docs.amplify.aws/react/connected-components/authenticator/configuration"&gt;Amplify Authenticator component&lt;/a&gt;, and provide an email address and password. After finalizing user setup via a confirmation email, log in to gain access to the application. (Figure 15)&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/localtest.gif" alt="Logging in using the Amplify Authenticator component during local development testing." width="1430" height="1200"&gt;&lt;em&gt;Figure 15 Logging in using the Amplify Authenticator component during local development testing.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;After interacting with the app on the development server, stop the sandbox environment by pressing &lt;b&gt;Ctrl + C&lt;/b&gt; in the terminal. Then enter “&lt;b&gt;npx ampx sandbox delete&lt;/b&gt;” and confirm by entering ‘&lt;b&gt;Y&lt;/b&gt;‘ when prompted to delete resources in the sandbox environment. (Figure 16)&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/sandbox.png" alt="Deleting all resources in the Amplify sandbox environment" width="1430" height="1200"&gt;&lt;em&gt;Figure 16 Deleting all resources in the Amplify sandbox environment&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Deploying backend resources&lt;/h2&gt; 
&lt;p&gt;With the app functioning as expected, deploy your backend resources by following the steps in “&lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/getting-started.html"&gt;Getting Started with Deploying an App to Amplify Hosting&lt;/a&gt;“. Ensure that GitHub is selected as the repository for deployment.&lt;/p&gt; 
&lt;h2&gt;Update Amplify Domain Endpoint&lt;/h2&gt; 
&lt;p&gt;In your backend CDK code, navigate to &lt;span style="color: red;font-weight: bold"&gt;&lt;code&gt;amplify/backend.ts&lt;/code&gt;&lt;/span&gt; and replace the placeholder in the origins field with your actual Amplify domain. You can find this domain in the Amplify console under the app name you created in the previous section. (Figure 17)&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;origins: [
  "https://main.&amp;lt;AMPLIFY_APP_ID&amp;gt;.amplifyapp.com",
],&lt;/code&gt;&lt;/pre&gt; 
&lt;h6&gt;&lt;em&gt;Figure 17 Updating the origins field in the CDK backend code with your Amplify app’s deployed domain.&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Upload Sample Data&lt;/h2&gt; 
&lt;p&gt;After deploying and signing into the application, &lt;b&gt;upload sample data&lt;/b&gt; by accessing the app domain URL generated by Amplify. After uploading files, confirm the upload by checking the public/ folder within the Storage section of the AWS Amplify console. (Figure 18)&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/upload.gif" alt="Uploading sample data in deployed app." width="1430" height="1200"&gt;&lt;em&gt;Figure 18 Uploading sample data in deployed app.&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Configure User Access to Q Business Web Experience&lt;/h2&gt; 
&lt;p&gt;Next, for your Qapp, you need to assign a user who can log in to the embedded web experience. This user should have been created during the prerequisites using &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/addusers.html"&gt;IAM Identity Center&lt;/a&gt;. To add a new or existing user, in the &lt;a href="https://us-east-1.console.aws.amazon.com/amazonq/business/applications?"&gt;Amazon Q Business&lt;/a&gt; console, select your &lt;b&gt;Qapp&lt;/b&gt;, then navigate to the &lt;b&gt;User access&lt;/b&gt; section and click Manage user access, followed by &lt;b&gt;Add groups and users&lt;/b&gt;. (Figure 19)&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/assignUser.png" alt="Assigning user access via IAM Identity Center." width="1430" height="1200"&gt;&lt;em&gt;Figure 19 Assigning user access via IAM Identity Center.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;If the user already exists, choose &lt;b&gt;Assign existing users and groups&lt;/b&gt; and search for the user you want to assign. Once added, &lt;b&gt;confirm&lt;/b&gt; your selection to grant access to the Amazon Q Business web experience. (Figure 20)&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/confirmUser.png" alt="Granting access to existing IAM Identity Center users." width="1430" height="1200"&gt;&lt;em&gt;Figure 20 Granting access to existing IAM Identity Center users.&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Sync S3 Data to Q Business Index&lt;/h2&gt; 
&lt;p&gt;After uploading your sample document, sync the Amazon S3 bucket with the Amazon Q Business index to store and retrieve your data. From the &lt;a href="https://us-east-1.console.aws.amazon.com/amazonq/business/applications?"&gt;Amazon Q Business&lt;/a&gt; console, navigate to &lt;b&gt;Data Sources&lt;/b&gt;, select &lt;b&gt;amplify-your-unique-bucket-name&lt;/b&gt;. Then, select &lt;b&gt;Sync now&lt;/b&gt; (Figure 21).&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/sync.png" alt="Syncing uploaded S3 data to Q Business index." width="1430" height="1200"&gt;&lt;em&gt;Figure 21 Syncing uploaded S3 data to Q Business index.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;Once the data source sync completes and the last sync status shows “&lt;b&gt;completed&lt;/b&gt;“, you’re almost ready to begin using your Amazon Q Business web experience. &lt;b&gt;Before proceeding, make sure to sign in. After a successful sign-in&lt;/b&gt;, you’ll see a “&lt;b&gt;Sign in Complete&lt;/b&gt;” message. At that point, you can return to your application—authentication is now complete. (Figure 22)&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/signin.gif" alt="Confirming sign-in completion for authentication." width="1430" height="1200"&gt;&lt;em&gt;Figure 22 Confirming sign-in completion for authentication.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;Now that you are signed in, start querying your Amazon Q Business web experience directly through your application! (Figure 23)&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/02/app2.gif" alt="Querying Amazon Q Business in your application." width="1430" height="1200"&gt;&lt;em&gt;Figure 23 Querying Amazon Q Business in your application.&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Cleaning Up&lt;/h2&gt; 
&lt;p&gt;To remove the &lt;a href="https://console.aws.amazon.com/singlesignon/home"&gt;AWS Identity Center console&lt;/a&gt; instance, go to “&lt;b&gt;Settings&lt;/b&gt;“, and open the “&lt;b&gt;Management&lt;/b&gt;” tab. Under the “&lt;b&gt;Delete IAM Identity Center instance&lt;/b&gt;” section, select “&lt;b&gt;Delete&lt;/b&gt;” to remove it.&lt;/p&gt; 
&lt;p&gt;Lastly, within the &lt;a href="https://console.aws.amazon.com/amplify/home#/home"&gt;AWS Amplify console&lt;/a&gt;, select “&lt;b&gt;View app&lt;/b&gt;” for the application you created following this blog. Then, select “&lt;b&gt;App Settings&lt;/b&gt;” followed by “&lt;b&gt;General Settings&lt;/b&gt;.” Finally, select “&lt;b&gt;Delete app&lt;/b&gt;” to remove the application and associated backend resources. Note, Amplify will delete &lt;i&gt;all backend resources&lt;/i&gt; created as a part of your project.&lt;/p&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this blog, we explained the key steps for integrating Amazon Q Business into a custom application: setting up the frontend with AWS Amplify, configuring the Q Business application using the CDK, and embedding the chatbot via an iframe. By leveraging these AWS Services and tools, you can create an AI-powered search experience that improves user interaction and knowledge accessibility.&lt;/p&gt; 
&lt;p&gt;Now it’s your turn! Embed conversational AI into your applications and unlock new levels of productivity and decision-making with Amazon Q Business. Then, supercharge your development workflow with &lt;a href="https://aws.amazon.com/codewhisperer/"&gt;Amazon Q Developer&lt;/a&gt;. Harness the power of generative AI and cloud computing to accelerate your development and quickly build modern applications that drive innovation.&lt;/p&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/19/ybenamin.jpg" alt="Photo of author" width="101" height="134"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ben-Amin York Jr.&lt;/h3&gt; 
  &lt;p&gt;Ben-Amin is an AWS Solutions Architect specializing in Frontend Web &amp;amp; Mobile technologies, supports Automotive &amp;amp; Manufacturing enterprises drive digital transformation.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/04/dianne.jpg" alt="Photo of author" width="101" height="134"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Dianne Eldridge&lt;/h3&gt; 
  &lt;p&gt;Dianne Eldridge leads global business development for industrial AI at AWS, driving strategy and growth since 2021. Previously, she spent 20 years at Emerson overseeing a global manufacturing portfolio across the US, China, and Italy.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/04/Vaidehi-1.png" alt="Photo of author" width="101" height="134"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Vaidehi Patel&lt;/h3&gt; 
  &lt;p&gt;Vaidehi Patel is a Solutions Architect at AWS, supporting Automotive and Manufacturing enterprises. She specializes in Serverless and Amazon Connect, helping customers design and scale AI/ML and Generative AI workloads to drive innovation and digital transformation.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/05/04/dextpham.jpg" alt="Photo of author" width="101" height="134"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Dexter Pham&lt;/h3&gt; 
  &lt;p&gt;Dexter Pham is a Solutions Architect at AWS, supporting enterprise customers in the Automotive &amp;amp; Manufacturing sector to realize business and technology goals. He supports customers on their cloud journey covering infrastructure, data, AI/ML,and specializes in VMware and SAP workloads.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>New features and developer experience with enhanced Amazon Location Service</title>
		<link>https://aws.amazon.com/blogs/mobile/new-features-and-developer-experience-with-enhanced-amazon-location-service/</link>
		
		<dc:creator><![CDATA[Zach Elliott]]></dc:creator>
		<pubDate>Wed, 02 Apr 2025 18:23:48 +0000</pubDate>
				<category><![CDATA[Amazon Location]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<category><![CDATA[Amazon Location Service]]></category>
		<guid isPermaLink="false">87b29e49784019e2ddf72d32372a2af778fb9599</guid>

					<description>This blog post was written by Yasunori Kirimoto – CEO of MIERUNE Building geospatial applications requires expertise in handling geospatial data, as well as designing and developing systems. It also requires skills in collecting and managing vast amounts of geospatial data and using it effectively within the application. This process can be very labor intensive, […]</description>
										<content:encoded>&lt;p&gt;&lt;em&gt;This blog post was written by Yasunori Kirimoto – CEO of MIERUNE&lt;/em&gt;&lt;/p&gt; 
&lt;p&gt;Building geospatial applications requires expertise in handling geospatial data, as well as designing and developing systems. It also requires skills in collecting and managing vast amounts of geospatial data and using it effectively within the application. This process can be very labor intensive, but its complexity can be greatly reduced by leveraging &lt;a href="https://aws.amazon.com/location/?nc2=type_a"&gt;Amazon Location Service&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;With Amazon Location Service, highly accurate geospatial data can be quickly obtained from APIs, allowing developers to focus on building applications. In addition, Amazon Location Service has been updated, adding new features in addition to its previous functionality. We’ll introduce the new features of Amazon Location Service and demonstrate how to leverage them in your application.&lt;/p&gt; 
&lt;h2&gt;New features released from Amazon Location Service&lt;/h2&gt; 
&lt;p&gt;The biggest change is that resource creation is no longer required. This means users no longer need to create individual resources (such as Place Indexes, Maps, and Route Calculators), but can set up an API key and immediately start using Amazon Location Service.&lt;/p&gt; 
&lt;p&gt;In addition, significant enhancements and new features have been added to the Maps, Places, and Routes APIs. The Maps API has been updated with additional styles, as well as the new static map capability. The Places API has been enhanced with new search and geocoding capabilities. Finally, the Routes API has been updated with new features such as Snap to Road, Waypoint Optimization, and additional travel modes.&lt;/p&gt; 
&lt;h2&gt;Creating API Keys&lt;/h2&gt; 
&lt;p&gt;In order to create an &lt;a href="https://docs.aws.amazon.com/location/latest/developerguide/using-apikeys.html"&gt;API Key&lt;/a&gt;, we can utilize the &lt;a href="https://aws.amazon.com/console/?nc2=type_a"&gt;AWS Management Console&lt;/a&gt;, or the &lt;a href="https://aws.amazon.com/cloudcontrolapi/?nc2=type_a"&gt;AWS Cloud Control API&lt;/a&gt;. For this example, we will use the console. Navigate to the Amazon Location Service Console, and select &lt;strong&gt;API keys&lt;/strong&gt; under &lt;strong&gt;Manage resources&lt;/strong&gt;. Select &lt;strong&gt;Create API key&lt;/strong&gt;&lt;/p&gt; 
&lt;div id="attachment_13989" style="width: 1440px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13989" loading="lazy" class="size-full wp-image-13989" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-01.png" alt="Image showing the Amazon Location Service console, specifically API Key actions such as Create API Key. The Create API key buttons have squares around them to alert readers to their location within the image." width="1430" height="499"&gt;
 &lt;p id="caption-attachment-13989" class="wp-caption-text"&gt;Figure 1 – The Amazon Location Service Console – API keys console&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;For the purposes of our demonstration, we will name the API Key &lt;strong&gt;LasVegasMaps&lt;/strong&gt; and select the following actions:&lt;/p&gt; 
&lt;p&gt;– GetStaticMap&lt;br&gt; – GetTile&lt;br&gt; – Geocode&lt;br&gt; – GetPlace&lt;br&gt; – SearchNearby&lt;br&gt; – SearchText&lt;br&gt; – CalculateIsolines&lt;br&gt; – SnapToRoads&lt;/p&gt; 
&lt;div id="attachment_13990" style="width: 1440px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13990" loading="lazy" class="size-full wp-image-13990" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-02.png" alt="Image showing the API Key creation process and selecting the &amp;quot;Actions&amp;quot; that the API Key is authorized to perform." width="1430" height="665"&gt;
 &lt;p id="caption-attachment-13990" class="wp-caption-text"&gt;Figure 2 – Selecting API Key actions&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Scrolling down, we have additional options including the ability to set an Expire time, and Referers. These are optional, but we highly recommend them for production applications. &lt;strong&gt;NOTE&lt;/strong&gt;: For the purposes of this demonstration we are leaving these as Default.&lt;/p&gt; 
&lt;div id="attachment_13991" style="width: 1439px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13991" loading="lazy" class="size-full wp-image-13991" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-03.png" alt="Image showing additional options for API Keys including Expiration time, Referers and Tags." width="1429" height="533"&gt;
 &lt;p id="caption-attachment-13991" class="wp-caption-text"&gt;Figure 3 –Additional API Key options&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Select &lt;strong&gt;Create API key&lt;/strong&gt;.&lt;/p&gt; 
&lt;p&gt;Now with the API Key created, we need to retrieve the value to use in our application. Select &lt;strong&gt;Show API key value&lt;/strong&gt; and copy the value into a safe location.&lt;/p&gt; 
&lt;div id="attachment_13992" style="width: 1440px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13992" loading="lazy" class="size-full wp-image-13992" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-04.png" alt="Image showing a completed API Key, with the option to Show API Key Value for usage in our application. The Show API Key Value button has a square around it to alert readers to its location within the image.v" width="1430" height="1072"&gt;
 &lt;p id="caption-attachment-13992" class="wp-caption-text"&gt;Figure 4 – API Key Console showing API Key Value&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;New Maps API features&lt;/h2&gt; 
&lt;p&gt;First, we will highlight and introduce the GetStyleDescriptor and GetStaticMap functions.&lt;/p&gt; 
&lt;h2&gt;Building the foundation for a map application with GetStyleDescriptor&lt;/h2&gt; 
&lt;p&gt;The GetStyleDescriptor function allows you to retrieve map style information and quickly build the foundation for your map application. This feature could be used for various geospatial solutions and application foundations. The new version offers expanded map styles, purpose-built for different applications—offering dark and light mode with varying levels of map detail.&lt;/p&gt; 
&lt;p&gt;We will demonstrate how to take advantage of these map styles using MapLibre GL JS. We’ll create a very straightforward HTML page using MapLibre GL JS and Amazon Location Service API Keys.&lt;/p&gt; 
&lt;p&gt;Begin by creating a blank HTML page, naming it &lt;strong&gt;simpleMap.html&lt;/strong&gt;, and copying the following code into the page:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-html"&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;title&amp;gt;Amazon Location Service Map&amp;lt;/title&amp;gt;

    &amp;lt;!-- MapLibre GL CSS --&amp;gt;
    &amp;lt;link href="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.css" rel="stylesheet" /&amp;gt;

    &amp;lt;style&amp;gt;
        body {
            margin: 0;
        }
        #map {
            height: 100vh; /* Full viewport height */
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
    &amp;lt;!-- Map container --&amp;gt;
    &amp;lt;div id="map"&amp;gt;&amp;lt;/div&amp;gt;

    &amp;lt;!-- JavaScript dependencies --&amp;gt;
    &amp;lt;script src="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.js"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;script&amp;gt;
        // Configuration
        const region = "&amp;lt;AWS Region&amp;gt;"; // Replace with your AWS region
        const mapApiKey = "&amp;lt;Your API Key&amp;gt;"; // Replace with your Amazon Location Service API key

        async function initializeMap() {
            // Initialize the map
            const map = new maplibregl.Map({
                container: "map", // HTML element ID where the map will be rendered
                center: [-115.1473824627421, 36.17071351509272], // Initial map center coordinates (Las Vegas)
                zoom: 12, // Initial zoom level
                style: `https://maps.geo.${region}.amazonaws.com/v2/styles/Standard/descriptor?&amp;amp;color-scheme=Light&amp;amp;variant=Default&amp;amp;key=${mapApiKey}`, // Map style URL
            });

            // Add navigation controls to the map
            map.addControl(new maplibregl.NavigationControl(), "top-left");
        }

        // Call the function to initialize the map
        initializeMap();
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Now open this HTML page in a browser. You should see a map of Las Vegas, NV. The key to this map is the style URL we set previously in our code. In this URL, we’ve requested that we use the Standard style map in a light color scheme. We can add additional parameters such as political views as well.&lt;/p&gt; 
&lt;div id="attachment_13993" style="width: 1535px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13993" loading="lazy" class="size-full wp-image-13993" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-05.png" alt="Image showing the Las Vegas Metro area with a light color scheme." width="1525" height="693"&gt;
 &lt;p id="caption-attachment-13993" class="wp-caption-text"&gt;Figure 5 – Map centered on Las Vegas, NV&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Creating a static map image with GetStaticMap&lt;/h2&gt; 
&lt;p&gt;The GetStaticMap feature allows you to create a static map image based on the coordinates, zoom level, and image size you specify. This feature helps include map images in printed materials and media posts. There are various parameters for this feature, including the ability to overlay other data (such as points, lines and polygons). We’ve provided a basic example. Be certain to edit the URL for your AWS Region and your newly created API Key. Paste the following URL into the address bar of your web browser to display a static map image of the specified location:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;https://maps.geo.&amp;lt;Your AWS Region&amp;gt;.amazonaws.com/v2/static/map?center=-115.170,36.122&amp;amp;zoom=15&amp;amp;width=1024&amp;amp;height=1024&amp;amp;key=&amp;lt;Your API Key&amp;gt;&lt;/strong&gt;&lt;/p&gt; 
&lt;div id="attachment_13994" style="width: 1034px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13994" loading="lazy" class="size-full wp-image-13994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-06.jpg" alt="Image showing the Las Vegas strip in satellite view, configured from a static map image." width="1024" height="1024"&gt;
 &lt;p id="caption-attachment-13994" class="wp-caption-text"&gt;Figure 6 –Static map image showing the Las Vegas strip&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;New Places API features&lt;/h2&gt; 
&lt;p&gt;Next, we will highlight and introduce the SearchText and SearchNearby features.&lt;/p&gt; 
&lt;h2&gt;Searching for specified POI data with SearchText&lt;/h2&gt; 
&lt;p&gt;The SearchText feature allows users to search for and present specified points of interest (POI) data. This feature is designed for users to quickly search for a specific location or facility. Users can send a POST request with the specified parameters and receive a response containing candidate point data. We will demonstrate an example of visualizing that data on a map.&lt;/p&gt; 
&lt;p&gt;Create a new HTML file named &lt;strong&gt;searchText.html&lt;/strong&gt; and paste the following contents into the file:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-html"&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;title&amp;gt;Amazon Location Service – Search Text&amp;lt;/title&amp;gt;

    &amp;lt;link href="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.css" rel="stylesheet" /&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
      }
      #map {
        height: 100vh;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;!-- map container --&amp;gt;
    &amp;lt;div id="map"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;!-- JavaScript dependencies --&amp;gt;
    &amp;lt;script src="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Import the Amazon Location Client --&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/@aws/amazon-location-client@1"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Import the utility library --&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/@aws/amazon-location-utilities-datatypes@1"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script&amp;gt;
      // Configuration
      // Set the AWS region for Amazon Location Service
      const region = "&amp;lt;AWS Region&amp;gt;";
      // API key for authenticating requests
      const mapApiKey = "&amp;lt;Amazon Location Service API Key&amp;gt;";
    
      async function initializeMap() {
        // Create an authentication helper using the API key
        const authHelper = await amazonLocationClient.withAPIKey(mapApiKey, region);
        
        // Initialize the Amazon Location Service Places client
        const client = new amazonLocationClient.places.GeoPlacesClient(
          authHelper.getClientConfig()
        );
    
        // Define search parameters for coffee shops
        const SearchTextInput = {
          BiasPosition: [-115.170, 36.122], // Las Vegas coordinates
          MaxResults: 25,
          QueryText: "Coffee Shops"
        }
    
        // Perform the search using Amazon Location Service
        const searchResults = await client.send(
          new amazonLocationClient.places.SearchTextCommand(SearchTextInput)
        )
    
        // Initialize the map
        const map = new maplibregl.Map({
          container: "map",
          center: [-115.170, 36.122], // Las Vegas coordinates
          zoom: 14,
          style: `https://maps.geo.${region}.amazonaws.com/v2/styles/Standard/descriptor?&amp;amp;color-scheme=Light&amp;amp;variant=Default&amp;amp;key=${mapApiKey}`,
        });
    
        // Add navigation controls to the map
        map.addControl(new maplibregl.NavigationControl(), "top-left");
    
        // When the map is loaded, add search results as a layer
        map.on("load", () =&amp;gt; {
          // Convert search results into a GeoJSON FeatureCollection
          const featureCollection = amazonLocationDataConverter.searchTextResponseToFeatureCollection(searchResults);
    
          // Add a data source containing GeoJSON from the search results
          map.addSource("place-index-results", {
            type: "geojson",
            data: featureCollection,
          });
    
          // Add a new layer to visualize the points
          map.addLayer({
            id: "place-index-results",
            type: "circle",
            source: "place-index-results",
            paint: {
              "circle-radius": 8,
              "circle-color": "#0080ff",
            },
          });
    
          // Add click event listener for the search result points
          map.on('click', 'place-index-results', (e) =&amp;gt; {
            if (e.features.length &amp;gt; 0) {
              const feature = e.features[0];
              const coordinates = feature.geometry.coordinates.slice();
              
              // Create a formatted HTML string with the feature's properties
              const properties = feature.properties;
              let description = '&amp;lt;h3&amp;gt;' + (properties['Title'] || 'Unnamed Location') + '&amp;lt;/h3&amp;gt;';
              description += '&amp;lt;p&amp;gt;Address: ' + (properties['Address.Label'] || 'N/A') + '&amp;lt;/p&amp;gt;';
    
              // Create and display a popup with the location information
              new maplibregl.Popup()
                .setLngLat(coordinates)
                .setHTML(description)
                .addTo(map);
            }
          });
          map.on('mouseenter', 'place-index-results', () =&amp;gt; {
            map.getCanvas().style.cursor = 'pointer';
          });
          map.on('mouseleave', 'place-index-results', () =&amp;gt; {
            map.getCanvas().style.cursor = '';
          });
        });
      }
    
      // Call the function to initialize the map
      initializeMap();
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Opening the HTML file in a browser will show the following map with coffee shops centered around the Venetian Resort in Las Vegas, NV (Figure 7).&lt;/p&gt; 
&lt;div id="attachment_13995" style="width: 2510px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13995" loading="lazy" class="size-full wp-image-13995" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-07.png" alt="Image showing multiple dots on a map representing coffee shops on the Las Vegas strip." width="2500" height="1134"&gt;
 &lt;p id="caption-attachment-13995" class="wp-caption-text"&gt;Figure 7 – Map showing the results of the SearchText API&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Searching to retrieve POI data around a specified location with SearchNearby&lt;/h2&gt; 
&lt;p&gt;The SearchNearby function allows you to retrieve POI data near a specified location. This feature is handy for users to search for nearby stores and attractions. Users can send a POST request with the specified parameters and receive a response containing candidate point data. We will demonstrate an example of visualizing that data on a map.&lt;/p&gt; 
&lt;p&gt;Create a new HTML file named &lt;strong&gt;searchNearby.html&lt;/strong&gt; and paste the following contents into the file:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-html"&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;title&amp;gt;Amazon Location Service – Search Nearby&amp;lt;/title&amp;gt;

    &amp;lt;link href="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.css" rel="stylesheet" /&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
      }
      #map {
        height: 100vh;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;!-- map container --&amp;gt;
    &amp;lt;div id="map"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;!-- JavaScript dependencies --&amp;gt;
    &amp;lt;script src="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Import the Amazon Location Client --&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/@aws/amazon-location-client@1"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Import the utility library --&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/@aws/amazon-location-utilities-datatypes@1"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script&amp;gt;
      // Configuration
      
      // Set the AWS region for Amazon Location Service
      const region = "&amp;lt;AWS Region&amp;gt;";
      // API key for authenticating map requests
      const mapApiKey = "&amp;lt;Amazon Location Service API Key&amp;gt;";
    
      async function initializeMap() {
        // Create an authentication helper using the API key
        const authHelper = await amazonLocationClient.withAPIKey(mapApiKey, region);
        
        // Initialize the Amazon Location Service Places client
        const client = new amazonLocationClient.places.GeoPlacesClient(
          authHelper.getClientConfig()
        );
    
        // Define search parameters for nearby casinos and hotels
        const SearchNearbyInput = {
          QueryPosition: [-115.170, 36.122], // Las Vegas coordinates
          MaxResults: 25,
          Filter: {
            IncludeCategories: [
              "casino",
              "hotel"
            ]
          }
        }
    
        // Perform the nearby search using Amazon Location Service
        const searchResults = await client.send(
          new amazonLocationClient.places.SearchNearbyCommand(SearchNearbyInput)
        )
    
        // Initialize the map
        const map = new maplibregl.Map({
          container: "map",
          center: [-115.170, 36.122], // Las Vegas coordinates
          zoom: 15,
          style: `https://maps.geo.${region}.amazonaws.com/v2/styles/Standard/descriptor?&amp;amp;color-scheme=Light&amp;amp;variant=Default&amp;amp;key=${mapApiKey}`,
        });
    
        // Add navigation controls to the map
        map.addControl(new maplibregl.NavigationControl(), "top-left");
    
        // When the map is loaded, add search results as a layer
        map.on("load", () =&amp;gt; {
          // Convert search results into a GeoJSON FeatureCollection
          const featureCollection = amazonLocationDataConverter.searchNearbyResponseToFeatureCollection(searchResults);
    
          // Add a data source containing GeoJSON from the search results
          map.addSource("place-index-results", {
            type: "geojson",
            data: featureCollection,
          });
        
          // Add a new layer to visualize the points
          map.addLayer({
            id: "place-index-results",
            type: "circle",
            source: "place-index-results",
            paint: {
              "circle-radius": 8,
              "circle-color": "#0080ff",
            },
          });
    
          // Add click event listener for the search result points
          map.on('click', 'place-index-results', (e) =&amp;gt; {
            if (e.features.length &amp;gt; 0) {
              const feature = e.features[0];
              const coordinates = feature.geometry.coordinates.slice();
              
              // Create a formatted HTML string with the feature's properties
              const properties = feature.properties;
              let description = '&amp;lt;h3&amp;gt;' + (properties['Title'] || 'Unnamed Location') + '&amp;lt;/h3&amp;gt;';
              description += '&amp;lt;p&amp;gt;Address: ' + (properties['Address.Label'] || 'N/A') + '&amp;lt;/p&amp;gt;';
    
              // Create and display a popup with the location information
              new maplibregl.Popup()
                .setLngLat(coordinates)
                .setHTML(description)
                .addTo(map);
            }
          });
          map.on('mouseenter', 'place-index-results', () =&amp;gt; {
            map.getCanvas().style.cursor = 'pointer';
          });
          map.on('mouseleave', 'place-index-results', () =&amp;gt; {
            map.getCanvas().style.cursor = '';
          });
        });
      }
    
      // Call the function to initialize the map
      initializeMap();
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Opening the HTML file in a browser will show the following map showing hotel and casino locations centered around the Venetian Resort in Las Vegas, NV (Figure 8).&lt;/p&gt; 
&lt;div id="attachment_13996" style="width: 2510px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13996" loading="lazy" class="size-full wp-image-13996" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-08.png" alt="Image showing multiple dots on a map representing casinos and hotels centered around the Venetian Resort in Las Vegas, NV." width="2500" height="1132"&gt;
 &lt;p id="caption-attachment-13996" class="wp-caption-text"&gt;Figure 8 – Map showing results of the SearchNearby API&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;New Routes API features&lt;/h2&gt; 
&lt;p&gt;Finally, we will discuss the new CalculateIsolines and SnapToRoads functions.&lt;/p&gt; 
&lt;h2&gt;Find the reachable range from a specified location with CalculateIsolines&lt;/h2&gt; 
&lt;p&gt;The CalculateIsolines function can retrieve the reachable range from a specified point. Some use cases for Isolines include identifying deliverable areas and evaluating property locations. Users can send a POST request with the specified parameters and receive a response containing polygon data indicating the reachable area. We will demonstrate an example of visualizing that data on a map.&lt;/p&gt; 
&lt;p&gt;Create a new HTML file and name it &lt;strong&gt;calculateIsolines.html&lt;/strong&gt; and paste the following contents into the file:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-html"&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;title&amp;gt;Amazon Location Service - Isolines&amp;lt;/title&amp;gt;

    &amp;lt;link href="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.css" rel="stylesheet" /&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
      }
      #map {
        height: 100vh;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;!-- map container --&amp;gt;
    &amp;lt;div id="map"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;!-- JavaScript dependencies --&amp;gt;
    &amp;lt;script src="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Import the Amazon Location Client --&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/@aws/amazon-location-client@1"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Import the utility library --&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/@aws/amazon-location-utilities-datatypes@1"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script&amp;gt;
      // Configuration
      
      // Set the AWS region for the Amazon Location Service
      const region = "&amp;lt;AWS Region&amp;gt;";
      // API key for authenticating map requests
      const mapApiKey = "&amp;lt;Amazon Location Service API Key";
    
      async function initializeMap() {
        // Create an authentication helper using the API key
        const authHelper = await amazonLocationClient.withAPIKey(mapApiKey, region);
        
        // Initialize the Amazon Location Service Routes client
        const client = new amazonLocationClient.routes.GeoRoutesClient(
          authHelper.getClientConfig()
        );
    
        // Define parameters for calculating isolines
        const IsolinesInput = {
          Origin: [-115.17015436843275, 36.12122662193694], // Starting point coordinates
          Thresholds: {
            Time: [
              300, 600, 900 // Time thresholds in seconds
            ]
          },
          TravelMode: "Pedestrian" // Travel mode for isoline calculation
        }
    
        // Calculate isolines using Amazon Location Service
        const routeResults = await client.send(
          new amazonLocationClient.routes.CalculateIsolinesCommand(IsolinesInput)
        )
    
        // Initialize the map
        const map = new maplibregl.Map({
          container: "map",
          center: [-115.16766776735061, 36.12177195550658], // Map center coordinates
          zoom: 15,
          style: `https://maps.geo.${region}.amazonaws.com/v2/styles/Standard/descriptor?&amp;amp;color-scheme=Light&amp;amp;variant=Default&amp;amp;key=${mapApiKey}`,
        });
    
        // Add navigation controls to the map
        map.addControl(new maplibregl.NavigationControl(), "top-left");
    
        // Add a marker at the origin point
        const marker = new maplibregl.Marker()
          .setLngLat([-115.17015436843275, 36.12122662193694])
          .addTo(map)
    
        // When the map is loaded, add isolines as layers
        map.on("load", () =&amp;gt; {
          // Convert isoline results into a GeoJSON FeatureCollection
          const featureCollection = amazonLocationDataConverter.calculateIsolinesResponseToFeatureCollection(routeResults);
    
          // Add a data source containing GeoJSON from the isoline results
          map.addSource("isolines", {
            type: "geojson",
            data: featureCollection
          });
    
          // Add a fill layer to visualize the isoline areas
          map.addLayer({
            'id': 'isolines-fill-900',
            'type': 'fill',
            'source': 'isolines',
            'filter': ['==', ['get', 'TimeThreshold'], 900],
            'paint': {
              'fill-color': '#0000ff',
              'fill-opacity': 0.5
            }
          });

          // Add a layer for 600m (10)
          map.addLayer({
            'id': 'isolines-fill-600',
            'type': 'fill',
            'source': 'isolines',
            'filter': ['==', ['get', 'TimeThreshold'], 600],
            'paint': {
              'fill-color': '#00ff00',
              'fill-opacity': .5
            }
          });

          // Add a layer for 300m (5)
          map.addLayer({
            'id': 'isolines-fill-300',
            'type': 'fill',
            'source': 'isolines',
            'filter': ['==', ['get', 'TimeThreshold'], 300],
            'paint': {
              'fill-color': '#f10000',
              'fill-opacity': 0.5
            }
          });
    
          // Add an outline layer to highlight the isoline boundaries
          map.addLayer({
            'id': 'isolines-outline',
            'type': 'line',
            'source': 'isolines',
            'paint': {
              'line-color': '#000000',
              'line-width': 2
            }
          });
        });
      }
    
      // Call the function to initialize the map
      initializeMap();
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Opening the HTML file in a browser will show the following map (Figure 9). This map shows walkable distances from the Venetian Resort in 5-, 15-, and 30-minute timeframes.&lt;/p&gt; 
&lt;div id="attachment_13997" style="width: 2510px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13997" loading="lazy" class="size-full wp-image-13997" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-09.png" alt="Image showing walking distances using the CalculateIsolines API from the Venetian resort in 5, 10, and 15 minute intervals. Five minutes is represented in brown, 10 in green and 15 in purple." width="2500" height="1131"&gt;
 &lt;p id="caption-attachment-13997" class="wp-caption-text"&gt;Figure 9 – Map showing walking distances from the Venetian resort&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Obtaining location-corrected route data with SnapToRoads&lt;/h2&gt; 
&lt;p&gt;The SnapToRoads function allows you to snap GPS data and other location data to the nearest road and obtain line data after location correction. This feature is very useful in improving the accuracy of vehicle tracking and traffic analysis. Users can send a POST request with specified parameters and receive a response containing position-corrected line data. We will demonstrate an example of visualizing the data before and after processing on a map.&lt;/p&gt; 
&lt;p&gt;Create a new HTML file named &lt;strong&gt;snapToRoad.html&lt;/strong&gt; and paste the following contents into the file:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-html"&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;title&amp;gt;Amazon Location Service - Snap to Roads&amp;lt;/title&amp;gt;

    &amp;lt;!-- MapLibre GL CSS --&amp;gt;
    &amp;lt;link href="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.css" rel="stylesheet" /&amp;gt;

    &amp;lt;style&amp;gt;
        body {
            margin: 0;
        }
        #map {
            height: 100vh; /* Full viewport height */
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
    &amp;lt;!-- Map container --&amp;gt;
    &amp;lt;div id="map"&amp;gt;&amp;lt;/div&amp;gt;

    &amp;lt;!-- JavaScript dependencies --&amp;gt;
    &amp;lt;script src="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Amazon Location Client --&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/@aws/amazon-location-client@1"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Amazon Location utility library --&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/@aws/amazon-location-utilities-datatypes@1"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;script&amp;gt;
        // Configuration
        const region = "&amp;lt;AWS Region&amp;gt;"; // Replace with your AWS region
        const mapApiKey = "&amp;lt;Amazon Location Service API Key&amp;gt;"; // Replace with your API key

        async function initializeMap() {
            // Create authentication helper
            const authHelper = await amazonLocationClient.withAPIKey(mapApiKey, region);
            // Initialize Amazon Location Service Routes client
            const client = new amazonLocationClient.routes.GeoRoutesClient(
                authHelper.getClientConfig()
            );

            // GPS trace coordinates
            const gpsTraceCoordinates = [
                [-115.14564318728544, 36.09359703860663],
                [-115.14522142988834, 36.09368914760097],
                [-115.14477687479442, 36.09345887491297],
                [-115.14452610012599, 36.093560194978636],
                [-115.14432092085157, 36.09364309311755],
                [-115.14383077036334, 36.09360624951118],
                [-115.14303285096376, 36.09360624951118],
                [-115.14273648090104, 36.09375362383305],
                [-115.14218933616989, 36.09353256224662],
                [-115.14163079259018, 36.0936154604141],
                [-115.14126602943631, 36.093633882217304],
                [-115.14136861907319, 36.09338518751021],
                [-115.14126602943631, 36.093035171404196],
                [-115.14093546282783, 36.092906217708844],
                [-115.140479508885, 36.09289700672278],
                [-115.14033132385379, 36.09270357576062],
                [-115.13951060675709, 36.09278647480254],
                [-115.13900905742025, 36.09291542869438],
                [-115.13839351959763, 36.09298911653757],
                [-115.1378349760179, 36.092906217708844],
                [-115.13733342668108, 36.09301674946067],
                [-115.13703705661834, 36.09286016276663]
             ];

            // Format trace points for Snap to Roads API
            const tracePoints = gpsTraceCoordinates.map(coord =&amp;gt; ({
                Position: coord,
            }));    

            // Snap to Roads API input
            const SnapInput = {
                TracePoints: tracePoints
            };

            // Call Snap to Roads API
            const snapResults = await client.send(
                new amazonLocationClient.routes.SnapToRoadsCommand(SnapInput)
            );

            // Initialize the map
            const map = new maplibregl.Map({
                container: "map",
                center: [-115.14127503567818, 36.09249839687936], // Las Vegas area
                zoom: 16,
                style: `https://maps.geo.${region}.amazonaws.com/v2/styles/Standard/descriptor?&amp;amp;color-scheme=Light&amp;amp;variant=Default&amp;amp;key=${mapApiKey}`,
            });

            // Add navigation controls
            map.addControl(new maplibregl.NavigationControl(), "top-left");

            // When the map is loaded, add the GPS trace and snapped route
            map.on("load", () =&amp;gt; {
                // Convert snap results to GeoJSON
                const featureCollection = amazonLocationDataConverter.snapToRoadsResponseToFeatureCollection(snapResults);

                // Add GPS trace source
                map.addSource("gpsTrace", {
                    type: "geojson",
                    data: {
                        type: "Feature",
                        geometry: {
                            type: "LineString",
                            coordinates: gpsTraceCoordinates
                        }
                    }
                });

                // Add snapped trace source
                map.addSource("snappedTrace", {
                    type: "geojson",
                    data: featureCollection
                });

                // Add GPS trace layer
                map.addLayer({
                    'id': 'gpsTrace',
                    'type': 'line',
                    'source': 'gpsTrace',
                    layout: {
                        "line-join": "round",
                        "line-cap": "round",
                    },
                    paint: {
                        "line-color": "#00b0ff",
                        "line-width": 8,
                    }
                });

                // Add snapped trace layer
                map.addLayer({
                    'id': 'snappedTrace',
                    'type': 'line',
                    'source': 'snappedTrace',
                    layout: {
                        "line-join": "round",
                        "line-cap": "round",
                    },
                    paint: {
                        "line-color": "#d59a9a",
                        "line-width": 8,
                    }
                });
            });
        }

        // Initialize the map
        initializeMap();
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Opening the HTML file in a browser will show the following map (Figure 10), with the blue line representing a GPS trace, and the red line representing a version that is snapped to roads.&lt;/p&gt; 
&lt;div id="attachment_13998" style="width: 2510px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13998" loading="lazy" class="size-full wp-image-13998" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-10.png" alt="Image showing a curvy blue line representing a GPS trace and a corrected (red line) route on a map." width="2500" height="1130"&gt;
 &lt;p id="caption-attachment-13998" class="wp-caption-text"&gt;Figure 10 – Map showing a corrected route based on a GPS trace&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Cleanup&lt;/h2&gt; 
&lt;p&gt;The only Amazon Location Service resources created during this demonstration was an API Key. To delete the API Key, navigate to the Amazon Location Service Console, select the API Key we created, and select &lt;strong&gt;Deactivate&lt;/strong&gt;. Confirm your decision to deactivate the key, then select &lt;strong&gt;Delete&lt;/strong&gt;. Also select that you would like to bypass the standard 90-day deactivation period.&lt;/p&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;Amazon Location Service now offers even greater flexibility with the new features. With this update, the previously required resource creation procedure is no longer necessary, and various APIs can be used by setting an API key. This allows users to quickly and smoothly build geospatial applications.&lt;/p&gt; 
&lt;p&gt;Notable new features include GetStyleDescriptor and GetStaticMap in the Maps API, SearchText and SearchNearby in the Places API, and CalculateIsolines and SnapToRoads in the Routes API.&lt;/p&gt; 
&lt;p&gt;In the Maps API, GetStyleDescriptor can be used to retrieve various map styles and apply them to your application, and GetStaticMap can generate static map images based on the coordinates and zoom levels you specify. The Places API allows you to search POI data using SearchText, and SearchNearby will enable you to find POIs around a specific location. The Routes API can use CalculateIsolines to calculate reachability from a specified point and SnapToRoads to correct GPS data to obtain accurate route data.&lt;/p&gt; 
&lt;p&gt;These new features allow application developers to more effectively utilize geospatial data and significantly improve the user experience. Contact an &lt;a href="https://aws.amazon.com/contact-us/sales-support/?pg=locationprice&amp;amp;cta=herobtn"&gt;AWS Representative&lt;/a&gt; for more information about how we can help accelerate your business.&lt;/p&gt; 
&lt;h2&gt;Further reading&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/aws/announcing-new-apis-for-amazon-location-service-routes-places-and-maps/"&gt;Announcing new APIs for Amazon Location Service Routes, Places, and Maps&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://docs.aws.amazon.com/location/latest/APIReference/Welcome.html"&gt;Amazon Location Service API Documentation&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://github.com/aws-geospatial"&gt;AWS Geospatial on GitHub&lt;/a&gt;&lt;/li&gt; 
&lt;/ul&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/24/mobile-1891-11.jpg" alt="" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Yasunori Kirimoto&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;Yasunori Kirimoto is an AWS DevTools Hero, the Co-Founder and CEO of MIERUNE. He specializes in GIS (Geographic Information System) and FOSS4G (Free and Open-Source Software for Geospatial). He has 20+ years of experience and extensive knowledge of geospatial data and applications.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Building real-time apps with AWS AppSync Events’ WebSocket publishing</title>
		<link>https://aws.amazon.com/blogs/mobile/building-real-time-apps-with-aws-appsync-events-websocket-publishing/</link>
		
		<dc:creator><![CDATA[Brice Pellé]]></dc:creator>
		<pubDate>Thu, 13 Mar 2025 21:04:20 +0000</pubDate>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">dba91acfbfe6d68c9e3accd5af6297f16e0d31ca</guid>

					<description>Real-time features have become essential in modern applications. Whether you’re building collaborative tools, live dashboards, or interactive games, users have come to expect instant and seamless updates as they interact with apps. AWS AppSync Events, a fully-managed service for serverless WebSocket APIs, has been helping developers add real-time capabilities to their applications, enabling them to […]</description>
										<content:encoded>&lt;p&gt;Real-time features have become essential in modern applications. Whether you’re building collaborative tools, live dashboards, or interactive games, users have come to expect instant and seamless updates as they interact with apps. &lt;a href="https://aws.amazon.com/appsync/"&gt;AWS AppSync Events&lt;/a&gt;, a fully-managed service for serverless WebSocket APIs, has been helping developers add real-time capabilities to their applications, enabling them to build responsive and engaging experiences at scale.&lt;/p&gt; 
&lt;p&gt;Today, I’m excited to announce an enhancement to AWS AppSync Events: the ability to publish messages directly over WebSocket connections, in addition to publishing over the API’s HTTP endpoint. This update allows developers to use a single WebSocket connection for both publishing and receiving events, streamlining the development of real-time features and reducing implementation complexity. This is particularly beneficial for chatty applications that previously faced the overhead of establishing new HTTP connections for each message publication. Developers now have more flexibility: they can publish over HTTP endpoint, e.g.: for publishing from a backend, or publish over WebSocket which might be preferred for web and mobile application clients. In this post, I’ll go over the new enhancement, and show you how you can start integrating publishing over WebSocket in your apps.&lt;/p&gt; 
&lt;h2&gt;Getting started&lt;/h2&gt; 
&lt;p&gt;As I mentioned in my Announcing AWS AppSync Events &lt;a href="https://aws.amazon.com/blogs/mobile/announcing-aws-appsync-events-serverless-websocket-apis/"&gt;post&lt;/a&gt;, getting started with AppSync Events is simple, and you can now publish over WebSocket or HTTP directly from the console. From the &lt;a href="https://console.aws.amazon.com/appsync"&gt;AppSync console&lt;/a&gt;, you can create an API and automatically get a &lt;code&gt;default&lt;/code&gt; channel namespace along with an API key. on the Pub/Sub Editor, you can immediately try out your API. As shown in the image below, choose the “Publish” button, and in the dropdown, choose “WebSocket”. Your events are published over the WebSocket. You receive a &lt;code&gt;publish_success&lt;/code&gt; message confirming the request.&lt;/p&gt; 
&lt;div id="attachment_13892" style="width: 1447px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-13892" loading="lazy" class="wp-image-13892 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/10/appsync-console-publish.png" alt="AppSync Pub/Sub editor showing new &amp;quot;Publish&amp;quot; dropdown button with &amp;quot;HTTP&amp;quot;, and &amp;quot;WebScoket&amp;quot; option" width="1437" height="560"&gt;
 &lt;p id="caption-attachment-13892" class="wp-caption-text"&gt;Publishing over WebSocket in the AppSync’s console Pub/Sub editor&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Message format&lt;/h2&gt; 
&lt;p&gt;AppSync now supports a new “publish” WebSocket operation to publish events. After connecting to the WebSocket, your client can start publishing events to channels in configured channel namespaces. Note that you do not need to subscribe to a channel before publishing to it. To publish events, you simply create a data message, specify an &lt;strong&gt;id&lt;/strong&gt; that uniquely identifies the message, the &lt;strong&gt;channel&lt;/strong&gt; you are sending the message to, the list of &lt;strong&gt;events&lt;/strong&gt; your are sending (up to 5), and the &lt;strong&gt;authorization&lt;/strong&gt; headers to allow the request. You can find more information about the authorization headers format in the &lt;a href="https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-websocket-protocol.html#authorization-formatting-by-mode"&gt;documentation&lt;/a&gt;. Each event in the events array must be a valid JSON string. Here’s an example.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-json"&gt;{
  "type": "publish",
  "id": "an-identifier-for-this-request",
  "channel": "/namespace/my/path",
  "events": [ "{ \"msg\": \"Hello World!\" }" ],
  "authorization": {
    "x-api-key": "da2-12345678901234567890123456-example",
   }
}
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;After publishing, you receive a “publish_success” response with details on each event sent, or a “publish_error” response if the operation was not successful.&lt;/p&gt; 
&lt;h2&gt;Integrate with your application&lt;/h2&gt; 
&lt;p&gt;In the previous post, I showed you how to implement a simple client that uses the browser’s Web API &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API"&gt;WebSocket&lt;/a&gt;. I’ll do this again to connect and publish on an Event API WebSocket endpoint. In this example, I’ll build a real-time demo chat application where clients can send and receive messages instantly over WebSocket. Messages are published to the &lt;code&gt;/default/messages&lt;/code&gt; channel, and clients subscribe to &lt;code&gt;/default/*&lt;/code&gt; to receive all messages in the default namespace.&lt;/p&gt; 
&lt;div id="attachment_13900" style="width: 749px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-13900" loading="lazy" class="wp-image-13900 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/10/pub-sub-messages.png" alt="A simple chat titled &amp;quot;Messages&amp;quot; show some received messages above an input form" width="739" height="367"&gt;
 &lt;p id="caption-attachment-13900" class="wp-caption-text"&gt;A demo chat application&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;I’ll use the new &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html#events"&gt;AWS Cloud Development Kit (CDK) L2 constructs for AppSync Events&lt;/a&gt; to configure and deploy an AppSync Event API, with a single channel namespace named “default”, and a single API key. &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html"&gt;Learn more&lt;/a&gt; about getting started with AWS CDK. If needed, use the Node Package Manager to install the CDK CLI.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-sh"&gt;$ npm install -g aws-cdk
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;I start by initializing a new folder structure and create my CDK application.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-sh"&gt;$ mkdir -p events-app/cdk-events-publish
$ cd events-app/cdk-events-publish
$ cdk init app --language javascript
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Next, I update the &lt;code&gt;lib/cdk-events-publish-stack.js&lt;/code&gt; file with this code:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-js"&gt;const { Stack, CfnOutput } = require('aws-cdk-lib');
const { EventApi, AppSyncAuthorizationType } = require('aws-cdk-lib/aws-appsync');

class CdkEventsPublishStack extends Stack {
  constructor(scope, id, props) {
    super(scope, id, props);
    const apiKeyProvider = { authorizationType: AppSyncAuthorizationType.API_KEY };

    // create an API called `my-event-api` that uses API Key authorization
    const api = new EventApi(this, 'api', {
      apiName: 'my-event-api',
      authorizationConfig: { authProviders: [apiKeyProvider] }
    });

    // add a channel namespace called `default`
    api.addChannelNamespace('default');

    // output configuration properties
    new CfnOutput(this, 'apiKey', { value: api.apiKeys['Default'].attrApiKey });
    new CfnOutput(this, 'httpDomain', { value: api.httpDns });
    new CfnOutput(this, 'realtimeDomain', { value: api.realtimeDns });
  }
}

module.exports = { CdkEventsPublishStack }
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;I then deploy the stack and save the output to a JSON file.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-sh"&gt;$ npm run cdk deploy -- -O output.json
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;I get output that looks like this:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-txt"&gt;Outputs:
CdkStack.apiKey = da2-12345678901234567890123456-example
CdkStack.httpDomain = a12345678901234567890123456.appsync-api.us-east-2.amazonaws.com
CdkStack.realtimeDomain = a12345678901234567890123456.appsync-realtime-api.us-east-2.amazonaws.com
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Next, I create the web app using &lt;a href="https://vite.dev/"&gt;vite&lt;/a&gt; (a frontend build tool), and vite’s vanilla javascript template. From the &lt;code&gt;events-app&lt;/code&gt; folder, I run:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-sh"&gt;$ npm create vite@latest app -- --template vanilla
$ cd app
$ npm install
$ ln -s ../../cdk-events-publish/output.json src
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This creates a link to the &lt;code&gt;output.json&lt;/code&gt; file that I can refer to in the app. In the new &lt;code&gt;app&lt;/code&gt; folder, I replace &lt;code&gt;src/main.js&lt;/code&gt; with the code below. Note that the code uses the CDK output in &lt;code&gt;output.json&lt;/code&gt;.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-js"&gt;import './style.css'
import output from './output.json'

// use the output from the CDK stack deployment
const HTTP_DOMAIN = output.CdkEventsPublishStack.httpDomain
const REALTIME_DOMAIN = output.CdkEventsPublishStack.realtimeDomain
const API_KEY = output.CdkEventsPublishStack.apiKey 
     
const authorization = { 'x-api-key': API_KEY, host: HTTP_DOMAIN }

document.querySelector('#app').innerHTML = `
    &amp;lt;style&amp;gt;
      #top{width: calc(100vw - 8rem); max-width: 1280px; background: oklch(0.977 0.013 236.62); margin: 2rem 0; height: calc(100vh - 8rem); box-shadow: inset 0 0 20px rgba(0,0,0,0.1); position: relative; margin-inline: auto;}
      #container{display: flex;flex-direction: column;height: calc(100% - 6.5rem);}
      h2{color: oklch(0.3 0.013 236.62); margin-bottom: 1.5em; font-size: 1.8rem;}
      #messages{display: flex; flex-direction: column-reverse; gap: 1em; max-height: calc(100vh - 200px); flex: 1; min-height: 0; overflow-y: auto; text-align: left;}
      form{position: absolute; bottom: 2rem; left: 2rem; right: 2rem; padding: 1rem 0;}
      input{width: 90%; padding: 0.8em 1.2em; border: 1px solid oklch(0.8 0.013 236.62); border-radius: 25px; font-size: 1rem; outline: none; transition: all 0.2s ease; box-shadow: 0 2px 5px rgba(0,0,0,0.05);}
      .msg{padding: 0 1rem; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;}
    &amp;lt;/style&amp;gt;
    &amp;lt;div id="top"&amp;gt;
      &amp;lt;div id="container"&amp;gt;&amp;lt;h2&amp;gt;Messages&amp;lt;/h2&amp;gt;&amp;lt;div id="messages"&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;form id="form"&amp;gt; &amp;lt;input id="messageInput" name="message" type="text" autocomplete="off" /&amp;gt;&amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;`

// construct the protocol header for the connection
function getAuthProtocol() {
  const header = btoa(JSON.stringify(authorization))
    .replace(/\+/g, '-') // Convert '+' to '-'
    .replace(/\//g, '_') // Convert '/' to '_'
    .replace(/=+$/, '') // Remove padding `=`
  return `header-${header}`
}

const socket = await new Promise((resolve, reject) =&amp;gt; {
  const socket = new WebSocket(`wss://${REALTIME_DOMAIN}/event/realtime`, [
    'aws-appsync-event-ws',
    getAuthProtocol(),
  ])
  socket.onopen = () =&amp;gt; resolve(socket)
  socket.onclose = (event) =&amp;gt; reject(new Error(event.reason))
  socket.onmessage = (_evt) =&amp;gt; {
    const data = JSON.parse(_evt.data)
    // if this is a `data` event, add the content to the list of messages
    if (data.type === 'data') {
      const event = JSON.parse(data.event)
      const div = document.createElement('div')
      div.className = 'msg'
      div.innerHTML = `↑ ${event.time} | ↓ ${new Date().toISOString().split('T')[1]} | ${event.message}`
      messages.prepend(div)
    }
  }
})

// subscribe to `/default/*`
socket.send(
  JSON.stringify({
    type: 'subscribe',
    id: crypto.randomUUID(),
    channel: '/default/*',
    authorization,
  }),
)

const form = document.querySelector('#form')
const messageInput = document.querySelector('#messageInput')
const messages = document.querySelector('#messages')

// when the form is submitted, send an event to `/default/messages`
form.addEventListener('submit', (e) =&amp;gt; {
  e.preventDefault()
  const message = new FormData(e.currentTarget).get('message')
  messageInput.value = ''
  socket.send(
    JSON.stringify({
      type: 'publish',
      id: crypto.randomUUID(),
      channel: '/default/messages',
      events: [JSON.stringify({ message, time: new Date().toISOString().split('T')[1] })],
      authorization,
    }),
  )
})
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Now in the &lt;code&gt;app&lt;/code&gt; directory, I start the web server:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-sh"&gt;$ npm run dev
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;I can open the website at the provided address on multiple browsers to send and receive messages.&lt;/p&gt; 
&lt;h3&gt;Cleaning up&lt;/h3&gt; 
&lt;p&gt;When done with the example, I can remove the resources I deployed with CDK.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-sh"&gt;$ cd ../cdk-events-publish
$ npm run cdk destroy
&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this post, I went over the new Publish over WebSocket feature for AWS AppSync Events, and showed how to get started with the feature. This enhancement offers several benefits:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Simplified implementation with a single connection for publishing and subscribing&lt;/li&gt; 
 &lt;li&gt;Reduced connection overhead for chatty applications&lt;/li&gt; 
 &lt;li&gt;Flexibility to choose between WebSocket and HTTP publishing based on your use case&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Publishing over WebSocket is now available in all regions where AppSync is available. Clients can publish at a rate of 25 requests per second per client WebSocket connection. You can continue using your API’s HTTP endpoint to publish at higher rates (adjustable default of 10,000 events per second). Visit AppSync’s &lt;a href="https://docs.aws.amazon.com/general/latest/gr/appsync.html"&gt;endpoints and quotas page&lt;/a&gt; for more details. We’re excited to see what you build with this new capability. Get started today by trying out the example in this post or by adding WebSocket publishing to your existing AppSync Events applications. To learn more about AppSync Events, visit the &lt;a href="https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-welcome.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Amplify Hosting Announces Skew Protection Support</title>
		<link>https://aws.amazon.com/blogs/mobile/amplify-hosting-announces-skew-protection/</link>
		
		<dc:creator><![CDATA[Matt Auerbach]]></dc:creator>
		<pubDate>Thu, 13 Mar 2025 19:38:18 +0000</pubDate>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<guid isPermaLink="false">52a22c799955974bbacd1bdb4462d1170ff07193</guid>

					<description>A common challenge in web application development and deployment is version skew between client and server resources. Today, we’re excited to announce deployment skew protection for applications deployed to AWS Amplify Hosting. This feature helps verifies end users have a seamless experience during application deployments. The Challenge Modern web applications are complex systems comprising numerous […]</description>
										<content:encoded>&lt;p&gt;A common challenge in web application development and deployment is version skew between client and server resources. Today, we’re excited to announce deployment skew protection for applications deployed to &lt;a href="https://aws.amazon.com/amplify/hosting"&gt;AWS Amplify Hosting&lt;/a&gt;. This feature helps verifies end users have a seamless experience during application deployments.&lt;/p&gt; 
&lt;h2&gt;The Challenge&lt;/h2&gt; 
&lt;p&gt;Modern web applications are complex systems comprising numerous static assets and server-side components that must all work together. In a world where its common to have multiple deployments occur hourly, version compatibility becomes a critical concern. When new deployments occur, users with cached versions of your application may attempt to fetch resources from the updated deployment, potentially resulting in 404 errors and broken functionality.&lt;/p&gt; 
&lt;p&gt;This challenge is compounded by client-server version skew, which manifests in two common scenarios. First, users often keep browser tabs open for extended periods, continuing to run older versions of your application while attempting to interact with updated backend services. Second, in mobile applications, users with disabled auto-updates may continue using outdated versions indefinitely, requiring backend services to maintain compatibility with multiple client versions simultaneously.&lt;/p&gt; 
&lt;p&gt;These version management challenges can significantly impact user experience and application reliability if not properly addressed. Consider the scenario:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;A user loads your application (version A)&lt;/li&gt; 
 &lt;li&gt;You deploy a new version (version B)&lt;/li&gt; 
 &lt;li&gt;The user’s cached JavaScript tries to load assets that only existed in version A&lt;/li&gt; 
 &lt;li&gt;Result: broken functionality and poor user experience&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3&gt;&lt;strong&gt;How skew protection works&lt;/strong&gt;&lt;/h3&gt; 
&lt;p&gt;Amplify Hosting now intelligently coordinates request resolution across multiple client sessions, ensuring precise routing to intended deployment versions.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;1. Smart asset routing&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;When a request comes in, Amplify Hosting now:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Identifies the deployment version that originated the request&lt;/li&gt; 
 &lt;li&gt;Routes and resolves the request to the identified version of the asset&lt;/li&gt; 
 &lt;li&gt;A hard refresh will always serve the latest deployment assets in a user session&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;2. Consistent version serving&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;The system confirms that:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;All assets from a single user session come from the same deployment&lt;/li&gt; 
 &lt;li&gt;New user sessions always get the latest version&lt;/li&gt; 
 &lt;li&gt;Existing sessions continue working with their original version until refresh&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;Advantages to skew protection&lt;/h3&gt; 
&lt;p&gt;Here are some of the advantages of having skew protection:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Zero Configuration: Works out of the box with popular frameworks&lt;/li&gt; 
 &lt;li&gt;Reliable Deployments: Eliminate 404 errors due to deployment skew&lt;/li&gt; 
 &lt;li&gt;Performance Optimized: Minimal impact on response times&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;Best practices&lt;/h3&gt; 
&lt;p&gt;While skew protection handles most scenarios automatically, we recommend:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Using atomic deployments&lt;/li&gt; 
 &lt;li&gt;Testing your deployment process in a staging environment&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2&gt;&lt;strong&gt;Enabling Skew Protection&lt;/strong&gt;&lt;/h2&gt; 
&lt;p&gt;Skew protection must be enabled for each Amplify app. This is done at the branch level for every Amplify Hosting app. You can read the full &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/skew-protection.html"&gt;Amplify Hosting documentation&lt;/a&gt; here.&lt;/p&gt; 
&lt;p&gt;1. To enable a branch, click on &lt;strong&gt;App Settings&lt;/strong&gt;, then click &lt;strong&gt;Branch Settings&lt;/strong&gt;. &lt;strong&gt;Next&lt;/strong&gt;, select the branch you want to enable followed by the &lt;strong&gt;Actions tab&lt;/strong&gt;.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13908" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/11/Screenshot-2025-03-04-at-4.21.35 PM.png" alt="Screenshot of the branches tab in Amplify Console settings" width="1530" height="309"&gt;&lt;/p&gt; 
&lt;p style="text-align: center"&gt;FIGURE 1 – Amplify Hosting Branch Settings&lt;/p&gt; 
&lt;p style="text-align: left"&gt;&amp;nbsp;2. For skew protection to take effect, you must deploy the application once.&lt;/p&gt; 
&lt;p style="text-align: left"&gt;Note: Skew protection will not be available to customers using our legacy SSRv1/WEB_DYNAMIC applications.&lt;/p&gt; 
&lt;p style="text-align: left"&gt;&lt;span style="text-decoration: underline"&gt;Pricing:&lt;/span&gt; There is &lt;strong&gt;no additional cost&lt;/strong&gt; for this feature and it is available to all Amplify Hosting regions.&lt;/p&gt; 
&lt;h2&gt;Tutorial&lt;/h2&gt; 
&lt;p&gt;To get started, follow these steps to create a Next.js application and enable skew protection on it.&lt;/p&gt; 
&lt;h3&gt;Prerequisites&lt;/h3&gt; 
&lt;p&gt;Before you begin, make sure you have the following installed:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Node.js (v18.x or later)&lt;/li&gt; 
 &lt;li&gt;npm or npx (v10.x or later)&lt;/li&gt; 
 &lt;li&gt;Git (v2.39.5 or later)&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;Create a Next.js app&lt;/h3&gt; 
&lt;p&gt;Let’s create a Next.js app to see skew protection in action.&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Create a new Next.js 15 app with &lt;a href="https://www.typescriptlang.org/"&gt;Typescript&lt;/a&gt; and &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt;&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;$ npx create-next-app@latest skew-protection-demo --typescript --tailwind --eslint
$ cd skew-protection-demo&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;2. Create a SkewProtectionDemo component that lists fingerprinted assets with deployment IDs. Use the following code to create the component.&lt;/p&gt; 
&lt;p&gt;Note: Amplify will automatically tag the fingerprinted assets of a NextJS app with a &lt;code&gt;dpl&lt;/code&gt; query parameter set to a UUID. This UUID is also available during the build via the &lt;code&gt;AWS_AMPLIFY_DEPLOYMENT_ID&lt;/code&gt; environment variable for other frameworks.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// app/components/SkewProtectionDemo.tsx

"use client";

import { useState, useEffect } from "react";
import DeploymentTester from "./DeploymentTester";

interface Asset {
  type: string;
  url: string;
  dpl: string;
}

export default function SkewProtectionDemo() {
  const [fingerprintedAssets, setFingerprintedAssets] = useState&amp;lt;Asset[]&amp;gt;([]);
  const [deploymentId, setDeploymentId] = useState&amp;lt;string&amp;gt;("Unknown");

  // Detect all assets with dpl parameters on initial load
  useEffect(() =&amp;gt; {
    detectFingerprintedAssets();
  }, []);

  // Function to detect all assets with dpl parameters
  const detectFingerprintedAssets = () =&amp;gt; {
    // Find all assets with dpl parameter (CSS and JS)
    const allElements = [
      ...Array.from(document.querySelectorAll('link[rel="stylesheet"]')),
      ...Array.from(document.querySelectorAll("script[src]"))
    ];
    
    const assets = allElements
      .map(element =&amp;gt; {
        const url = element.getAttribute(element.tagName.toLowerCase() === "link" ? "href" : "src");
        if (!url || !url.includes("?dpl=")) return null;

        // Extract the dpl parameter
        let dplParam = "unknown";
        try {
          const urlObj = new URL(url, window.location.origin);
          dplParam = urlObj.searchParams.get("dpl") || "unknown";
        } catch (e) {
          console.error("Error parsing URL:", e);
        }

        return {
          type: element.tagName.toLowerCase() === "link" ? "css" : "js",
          url: url,
          dpl: dplParam,
        };
      })
      .filter(asset =&amp;gt; asset !== null);

    setFingerprintedAssets(assets);
    
    // Set deployment ID if assets were found
    if (assets.length &amp;gt; 0) {
      setDeploymentId(assets[0]?.dpl || "Unknown");
    }
  };

  // Function to format URL to highlight the dpl parameter
  const formatUrl = (url: string) =&amp;gt; {
    if (!url.includes("?dpl=")) return url;
    
    const [baseUrl, params] = url.split("?");
    const dplParam = params.split("&amp;amp;").find(p =&amp;gt; p.startsWith("dpl="));
    
    if (!dplParam) return url;
    
    const otherParams = params.split("&amp;amp;").filter(p =&amp;gt; !p.startsWith("dpl=")).join("&amp;amp;");
    
    return (
      &amp;lt;&amp;gt;
        &amp;lt;span className="text-gray-400"&amp;gt;{baseUrl}?&amp;lt;/span&amp;gt;
        {otherParams &amp;amp;&amp;amp; &amp;lt;span className="text-gray-400"&amp;gt;{otherParams}&amp;amp;&amp;lt;/span&amp;gt;}
        &amp;lt;span className="text-yellow-300 font-bold"&amp;gt;{dplParam}&amp;lt;/span&amp;gt;
      &amp;lt;/&amp;gt;
    );
  };

  return (
    &amp;lt;main className="min-h-screen p-6 bg-white"&amp;gt;
      &amp;lt;div className="w-full max-w-2xl mx-auto"&amp;gt;
        &amp;lt;h1 className="text-2xl font-bold text-gray-900 mb-6"&amp;gt;
          Amplify Skew Protection Demo
        &amp;lt;/h1&amp;gt;

        &amp;lt;div className="grid grid-cols-1 gap-4 mb-6"&amp;gt;
          &amp;lt;div className="flex items-center p-4 bg-white text-gray-800 rounded-md border border-gray-200 hover:bg-gray-50 transition-colors"&amp;gt;
            &amp;lt;div className="w-10 h-10 flex items-center justify-center bg-gray-100 rounded-lg mr-3"&amp;gt;
              &amp;lt;span className="text-lg"&amp;gt;?&amp;lt;/span&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;p className="font-medium"&amp;gt;Zero-Downtime Deployments&amp;lt;/p&amp;gt;
              &amp;lt;p className="text-xs text-gray-600"&amp;gt;Assets and API routes remain accessible during deployments using deployment ID-based routing&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          
          &amp;lt;div className="flex items-center p-4 bg-white text-gray-800 rounded-md border border-gray-200 hover:bg-gray-50 transition-colors"&amp;gt;
            &amp;lt;div className="w-10 h-10 flex items-center justify-center bg-gray-100 rounded-lg mr-3"&amp;gt;
              &amp;lt;span className="text-lg"&amp;gt;⚡&amp;lt;/span&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;p className="font-medium"&amp;gt;Built-in Next.js Support&amp;lt;/p&amp;gt;
              &amp;lt;p className="text-xs text-gray-600"&amp;gt;Automatic asset fingerprinting and deployment ID injection for Next.js applications&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          
          &amp;lt;div className="flex items-center p-4 bg-white text-gray-800 rounded-md border border-gray-200 hover:bg-gray-50 transition-colors"&amp;gt;
            &amp;lt;div className="w-10 h-10 flex items-center justify-center bg-gray-100 rounded-lg mr-3"&amp;gt;
              &amp;lt;span className="text-lg"&amp;gt;?&amp;lt;/span&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;p className="font-medium"&amp;gt;Advanced Security&amp;lt;/p&amp;gt;
              &amp;lt;p className="text-xs text-gray-600"&amp;gt;Protect against compromised builds by calling the delete-job API to remove affected deployments&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;div className="bg-gradient-to-r from-blue-900 to-purple-900 p-4 rounded-md mb-6"&amp;gt;
          &amp;lt;h2 className="text-sm font-medium text-blue-200 mb-1"&amp;gt;
            Current Deployment ID
          &amp;lt;/h2&amp;gt;
          &amp;lt;div className="p-2 bg-black bg-opacity-30 rounded-md font-mono text-lg text-center text-yellow-300"&amp;gt;
            {deploymentId}
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;

        {fingerprintedAssets.length &amp;gt; 0 ? (
          &amp;lt;&amp;gt;
            &amp;lt;h2 className="text-xl font-bold text-gray-900 mb-6"&amp;gt;
              Fingerprinted Assets
            &amp;lt;/h2&amp;gt;
            &amp;lt;div className="border border-gray-200 rounded-md overflow-hidden mb-6 bg-white"&amp;gt;
              &amp;lt;div className="max-h-48 overflow-y-auto"&amp;gt;
                &amp;lt;table className="min-w-full divide-y divide-gray-200"&amp;gt;
                  &amp;lt;thead className="bg-gray-50"&amp;gt;
                    &amp;lt;tr&amp;gt;
                      &amp;lt;th className="px-3 py-2 text-left text-xs font-medium text-gray-900 uppercase tracking-wider w-16"&amp;gt;
                        Type
                      &amp;lt;/th&amp;gt;
                      &amp;lt;th className="px-3 py-2 text-left text-xs font-medium text-gray-900 uppercase tracking-wider"&amp;gt;
                        URL
                      &amp;lt;/th&amp;gt;
                    &amp;lt;/tr&amp;gt;
                  &amp;lt;/thead&amp;gt;
                  &amp;lt;tbody className="divide-y divide-gray-200"&amp;gt;
                    {fingerprintedAssets.map((asset, index) =&amp;gt; (
                      &amp;lt;tr key={index} className="bg-white hover:bg-gray-50"&amp;gt;
                        &amp;lt;td className="px-3 py-2 text-sm text-gray-900"&amp;gt;
                          &amp;lt;span
                            className={`inline-block px-2 py-0.5 rounded-full text-xs ${
                              asset.type === "css"
                                ? "bg-blue-100 text-blue-800"
                                : "bg-yellow-100 text-yellow-800"
                            }`}
                          &amp;gt;
                            {asset.type}
                          &amp;lt;/span&amp;gt;
                        &amp;lt;/td&amp;gt;
                        &amp;lt;td className="px-3 py-2 text-xs font-mono break-all text-gray-900"&amp;gt;
                          {formatUrl(asset.url)}
                        &amp;lt;/td&amp;gt;
                      &amp;lt;/tr&amp;gt;
                    ))}
                  &amp;lt;/tbody&amp;gt;
                &amp;lt;/table&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            
            &amp;lt;h2 className="text-xl font-bold text-gray-900 mb-6"&amp;gt;
              Test Deployment Routing
            &amp;lt;/h2&amp;gt;
            &amp;lt;DeploymentTester /&amp;gt;
          &amp;lt;/&amp;gt;
        ) : (
          &amp;lt;div className="p-6 text-center text-gray-600 border border-gray-200 rounded-md"&amp;gt;
            &amp;lt;p&amp;gt;No fingerprinted assets detected&amp;lt;/p&amp;gt;
            &amp;lt;p className="text-sm mt-2"&amp;gt;
              Deploy to Amplify Hosting to see skew protection in action
            &amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;3. Next, create a DeploymentTester component that demonstrates how API requests maintain deployment consistency by sending the &lt;code&gt;X-Amplify-Dpl&lt;/code&gt; header with each request, allowing Amplify to route to the correct API version. Use the following code to create the component.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// app/components/DeploymentTester.tsx

'use client';

import { useState } from 'react';

interface ApiResponse {
  message: string;
  timestamp: string;
  version: string;
  deploymentId: string;
}

export default function DeploymentTester() {
  const [testInProgress, setTestInProgress] = useState(false);
  const [testOutput, setTestOutput] = useState&amp;lt;ApiResponse | null&amp;gt;(null);
  const [callCount, setCallCount] = useState(0);

  const runApiTest = async () =&amp;gt; {
    setTestInProgress(true);
    setCallCount(prev =&amp;gt; prev + 1);
    
    try {
      const response = await fetch('/api/skew-protection', {
        headers: {
        // Amplify provides the deployment ID as an environment variable during build time
          'X-Amplify-Dpl': process.env.AWS_AMPLIFY_DEPLOYMENT_ID || '',
        }
      });
      
      if (!response.ok) {
        throw new Error(`API returned ${response.status}`);
      }
      const data = await response.json();
      setTestOutput(data);
    } catch (error) {
      console.error("API call failed", error);
      setTestOutput({
        message: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
        timestamp: new Date().toISOString(),
        version: 'error',
        deploymentId: 'error'
      });
    } finally {
      setTestInProgress(false);
    }
  };

  return (
    &amp;lt;div className="border border-gray-200 rounded-md overflow-hidden bg-white"&amp;gt;
      &amp;lt;div className="bg-gray-50 px-4 py-3 flex justify-between items-center border-b border-gray-200"&amp;gt;
        &amp;lt;span className="font-medium text-gray-800"&amp;gt;Test Deployment Routing&amp;lt;/span&amp;gt;
        &amp;lt;button
          onClick={runApiTest}
          disabled={testInProgress}
          className={`px-3 py-1 rounded text-sm ${
            testInProgress
              ? 'bg-gray-200 text-gray-500 cursor-not-allowed'
              : 'bg-blue-600 hover:bg-blue-700 text-white'
          }`}
        &amp;gt;
          {testInProgress ? "Testing..." : "Test Route"}
        &amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
      
      &amp;lt;div className="p-4"&amp;gt;
        {testOutput ? (
          &amp;lt;div className="p-3 bg-gray-50 rounded border border-gray-200 font-mono text-sm"&amp;gt;
            &amp;lt;div className="text-green-600 mb-2"&amp;gt;{testOutput.message}&amp;lt;/div&amp;gt;
            &amp;lt;div className="text-gray-600 text-xs space-y-1"&amp;gt;
              &amp;lt;div&amp;gt;API Version: &amp;lt;span className="text-blue-600 font-medium"&amp;gt;{testOutput.version}&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
              &amp;lt;div&amp;gt;Deployment ID: &amp;lt;span className="text-purple-600 font-medium"&amp;gt;{testOutput.deploymentId}&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
              &amp;lt;div&amp;gt;Call #: {callCount}&amp;lt;/div&amp;gt;
              &amp;lt;div&amp;gt;Time: {new Date(testOutput.timestamp).toLocaleTimeString()}&amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        ) : testInProgress ? (
          &amp;lt;div className="p-3 bg-gray-50 rounded border border-gray-200 text-sm text-gray-600"&amp;gt;
            Testing deployment routing...
          &amp;lt;/div&amp;gt;
        ) : (
          &amp;lt;div className="p-3 bg-gray-50 rounded border border-gray-200 text-sm text-gray-600"&amp;gt;
            Click &amp;amp;quot;Test Route&amp;amp;quot; to verify how requests are routed to the correct deployment version
          &amp;lt;/div&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
} &lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;4. Now create an API route that uses the &lt;code&gt;X-Amplify-Dpl&lt;/code&gt; header to identify which deployment the request is coming from, simulating how Amplify routes API requests to maintain version consistency during deployments. Use the following code to create the API route:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// app/api/skew-protection/route.ts
import { NextResponse } from 'next/server';
import { type NextRequest } from 'next/server';

// This version identifier can be changed between deployments to demonstrate skew protection
const CURRENT_API_VERSION = "v2.0";

export async function GET(request: NextRequest) {
  // Get the deployment ID from the X-Amplify-Dpl header
  // This is how Amplify routes API requests to the correct deployment version
  const deploymentId = request.headers.get('x-amplify-dpl') || '';
  
  // Determine which version to serve based on deployment ID
  const apiVersion = CURRENT_API_VERSION;
  const message = `Hello from API ${apiVersion}! ?`;
  
  // Return the response with deployment information
  return NextResponse.json({
    message,
    version: apiVersion,
    deploymentId: deploymentId || 'none',
    timestamp: new Date().toISOString()
  });
} 
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;5. Add the Amplify deployment ID environment variable to make it accessible to client code&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  env: {
    AWS_AMPLIFY_DEPLOYMENT_ID: process.env.AWS_AMPLIFY_DEPLOYMENT_ID || '',
  }
};
export default nextConfig;
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;6. Push the changes to a GitHub repository&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Create a new GitHub repository&lt;/li&gt; 
 &lt;li&gt;Add and commit the changes to the Git branch&lt;/li&gt; 
 &lt;li&gt;Add remote origin and push the changes upstream&lt;/li&gt; 
&lt;/ul&gt; 
&lt;pre&gt;&lt;code class="lang-git"&gt;git add .
git commit -m "initial commit"
git remote add origin https://github.com/&amp;lt;OWNER&amp;gt;/amplify-skew-protection-demo.git
git push -u origin main&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;Deploy the application to Amplify Hosting&lt;/h3&gt; 
&lt;p&gt;Use the following steps to deploy your newly constructed application to AWS Amplify Hosting:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Sign in to the &lt;a href="https://console.aws.amazon.com/amplify/"&gt;AWS Amplify console&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Create new app&lt;/strong&gt; and select &lt;strong&gt;GitHub&lt;/strong&gt; as the repository source&lt;/li&gt; 
 &lt;li&gt;Authorize Amplify to access your GitHub account&lt;/li&gt; 
 &lt;li&gt;Choose the repository and branch you created&lt;/li&gt; 
 &lt;li&gt;Review the &lt;strong&gt;App&lt;/strong&gt; settings and then choose &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt; 
 &lt;li&gt;Review the overall settings and choose &lt;strong&gt;Save and deploy&lt;/strong&gt;&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3&gt;Enable skew protection&lt;/h3&gt; 
&lt;p&gt;In the Amplify console, navigate to &lt;strong&gt;App settings&lt;/strong&gt; and then &lt;strong&gt;Branch settings&lt;/strong&gt;. Select the &lt;strong&gt;Branch&lt;/strong&gt;, and from the Actions dropdown menu choose &lt;strong&gt;Enable skew protection&lt;/strong&gt;.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13913" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/11/Screenshot-2025-03-06-at-3.45.58 PM.png" alt="Screenshot of the branch settings in the AWS Console" width="1754" height="588"&gt;&lt;/p&gt; 
&lt;p style="text-align: center"&gt;FIGURE 2 – Amplify Hosting Branch Settings&lt;/p&gt; 
&lt;p&gt;Next, navigate to the deployments page and redeploy your application. When skew protection is enabled for an application, AWS Amplify must update its CDN cache conﬁguration. Therefore, you should expect your ﬁrst deployment after enabling skew protection to take up to ten minutes.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13914" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/11/Screenshot-2025-03-06-at-3.46.44 PM.png" alt="Screenshot of the AWS Amplify Console. Shows a deployed app" width="1752" height="830"&gt;&lt;/p&gt; 
&lt;p style="text-align: center"&gt;FIGURE 3 – Amplify Hosting App Deployments&lt;/p&gt; 
&lt;h3&gt;Access the deployed Next.js app&lt;/h3&gt; 
&lt;p&gt;Navigate to the &lt;strong&gt;Overview&lt;/strong&gt; tab in the Amplify console and open the default Amplify generated URL in the browser. You should now observe a list of fingerprinted assets for your app along with the deployment ID.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13915" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/11/Screenshot-2025-03-06-at-3.45.04 PM.png" alt="Screenshot of Amplify Hosting settings " width="1752" height="776"&gt;&lt;/p&gt; 
&lt;p style="text-align: center"&gt;FIGURE 4 – Amplify Hosting App Settings – Branch level&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13916" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/11/Screenshot-2025-03-07-at-4.02.28 PM.png" alt="Screenshot of the deployed app to see skew protection demo" width="2156" height="1924"&gt;&lt;/p&gt; 
&lt;p style="text-align: center"&gt;FIGURE 5 – Demo App Homepage&lt;/p&gt; 
&lt;h3&gt;Testing skew protection&lt;/h3&gt; 
&lt;p&gt;When you deploy your Next.js application to Amplify, each deployment gets assigned a unique deployment ID. This ID is automatically injected into your static assets (JS, CSS) and API routes to ensure version consistency. Let’s see it in action:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Asset Fingerprinting: Notice how each static asset URL includes a ?dpl= parameter with your current deployment ID. This ensures browsers always fetch the correct version of your assets.&lt;/li&gt; 
 &lt;li&gt;API Routing: The &lt;code&gt;Test Route&lt;/code&gt; button demonstrates how Amplify routes API requests. When clicked, it makes a request to the &lt;code&gt;/api/skew-protection&lt;/code&gt; endpoint. Since the request utilizes the &lt;code&gt;X-Amplify-Dpl&lt;/code&gt; header to match your current deployment ID, it ensures routing to the correct API version.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;This means that even during deployments, your users won’t experience version mismatches and each user’s session stays consistent with the version they initially loaded, preventing bugs that could occur when client and server versions don’t match.&lt;/p&gt; 
&lt;h3&gt;Try it yourself&lt;/h3&gt; 
&lt;ol&gt; 
 &lt;li&gt;Keep your current browser tab open and click Test Route to see the API version and deployment ID match.&lt;/li&gt; 
 &lt;li&gt;Deploy a new version with a different CURRENT_API_VERSION in &lt;code&gt;api/skew-protection/route.ts&lt;/code&gt;&lt;/li&gt; 
 &lt;li&gt;Open your application in a new incognito window 
  &lt;ol&gt; 
   &lt;li&gt;Compare the behavior: 
    &lt;ul&gt; 
     &lt;li style="list-style-type: none"&gt; 
      &lt;ul&gt; 
       &lt;li&gt;Your original tab will maintain the old version&lt;/li&gt; 
       &lt;li&gt;The new incognito window will show the new version&lt;/li&gt; 
       &lt;li&gt;Each tab’s assets and API calls will consistently match their respective versions&lt;/li&gt; 
       &lt;li&gt;Try clicking &lt;code&gt;Test Route&lt;/code&gt; repeatedly in both windows – each will consistently route to its respective version, demonstrating how Amplify maintains session consistency even when multiple versions are live&lt;/li&gt; 
      &lt;/ul&gt; &lt;/li&gt; 
    &lt;/ul&gt; &lt;p&gt;&lt;img loading="lazy" class="size-full wp-image-13946 aligncenter" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/13/Screenshot-2025-03-13-at-12.28.02 PM.png" alt="Screenshot of the demo app comparing both versions" width="1002" height="589"&gt;&lt;/p&gt;&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;p style="text-align: center"&gt;FIGURE 6 -Side by Side Comparison of Skew Protection Behavior&lt;/p&gt; 
&lt;p&gt;This demonstrates how Amplify maintains version consistency for each user session, even when multiple versions of your application are running during deployments.&lt;/p&gt; 
&lt;p&gt;Congratulations, you’ve successfully created and verified skew protection on your Next.js application deployments on Amplify Hosting.&lt;/p&gt; 
&lt;h3&gt;Cleanup&lt;/h3&gt; 
&lt;p&gt;Delete the AWS Amplify app by navigating to &lt;strong&gt;App settings&lt;/strong&gt;, next go to &lt;strong&gt;General settings&lt;/strong&gt;, then choose &lt;strong&gt;Delete app&lt;/strong&gt;.&lt;/p&gt; 
&lt;h2&gt;Next Steps&lt;/h2&gt; 
&lt;ol&gt; 
 &lt;li&gt;Enable Skew Protection for your application.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/skew-protection.html"&gt;Read our documentation&lt;/a&gt; to learn more about this feature!&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3&gt;About the Authors&lt;/h3&gt; 
&lt;p&gt;&lt;img loading="lazy" class="size-full wp-image-12567 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/02/19/Screenshot-2024-02-19-at-3.13.45 PM.png" alt="Matt Headshot" width="142" height="146"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Matt Auerbach, Senior Product Manager, Amplify Hosting&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Matt Auerbach is a NYC-based Product Manager on the AWS Amplify Team. He educates developers regarding products and offerings, and acts as the primary point of contact for assistance and feedback. Matt is a mild-mannered programmer who enjoys using technology to solve problems and making people’s lives easier. B night, however…well he does pretty much the same thing. You can find Matt on X &lt;a href="https://x.com/mauerbac"&gt;@mauerbac&lt;/a&gt;. He previously worked at Twitch, Optimizely and Twilio.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignleft wp-image-13182 size-thumbnail" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/08/13/IMG_9570-150x150.jpg" alt="Jay Author" width="150" height="150"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Jay Raval, Solutions Architect, Amplify Hosting&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Jay Raval is a Solutions Architect on the AWS Amplify team. He’s passionate about solving complex customer problems in the front-end, web and mobile domain and addresses real-world architecture problems for development using front-end technologies and AWS. In his free time, he enjoys traveling and sports. You can find Jay on X &lt;a href="https://x.com/i/flow/login?redirect_after_login=%2F_Jay_Raval_"&gt;@_Jay_Raval_&lt;/a&gt;&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Simplifying Multi-App Management in AWS Amplify Hosting</title>
		<link>https://aws.amazon.com/blogs/mobile/simplifying-multi-app-management-in-aws-amplify-hosting/</link>
		
		<dc:creator><![CDATA[Matt Auerbach]]></dc:creator>
		<pubDate>Mon, 10 Mar 2025 20:09:13 +0000</pubDate>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<guid isPermaLink="false">3cc7af765466373ed3282b679ea3ba47f7635f1e</guid>

					<description>AWS Amplify Hosting has now made it possible to connect many more Amplify apps to a single repository. This change improves the way developers can integrate with Git providers which is especially beneficial for monorepo architectures. Amplify now uses a single webhook per repository for all associated apps, streamlining the development workflow. For specific limit […]</description>
										<content:encoded>&lt;p&gt;&lt;a href="https://aws.amazon.com/amplify/hosting/"&gt;AWS Amplify Hosting&lt;/a&gt; has now made it possible to connect many more Amplify apps to a single repository. This change improves the way developers can integrate with Git providers which is especially beneficial for monorepo architectures. Amplify now uses a single webhook per repository for all associated apps, streamlining the development workflow. For specific limit details, please refer to our &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/quotas-chapter.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;Previously, Amplify users were constrained by the webhook limits provided by Git providers. As Amplify created a new webhook for each app associated with a repository, users with multiple Amplify apps linked to a single repository could reach these limits quickily, preventing them from adding more apps. This was particularly challenging for teams working with monorepos, where multiple projects (and thus, multiple Amplify apps) exist within a single repository.&lt;/p&gt; 
&lt;p&gt;While the specific number of webhooks allowed varies by Git provider, these limits presented an obstacle for teams looking to scale their projects or work with complex repository structures.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;GitHub has a webhook &lt;a href="https://docs.github.com/en/webhooks/testing-and-troubleshooting-webhooks/troubleshooting-webhooks#cannot-have-more-than-20-webhooks"&gt;limit&lt;/a&gt; of 20&lt;/li&gt; 
 &lt;li&gt;GitLab has webhook &lt;a href="https://docs.gitlab.com/ee/user/gitlab_com/index.html#other-limits"&gt;limit&lt;/a&gt; of 100&lt;/li&gt; 
 &lt;li&gt;BitBucket has webhook &lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/manage-webhooks/"&gt;limit&lt;/a&gt; of 50&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;Unified Webhooks&lt;/h3&gt; 
&lt;p&gt;Unified webhooks solve this pain point by consolidating all Amplify-related webhooks into a single, comprehensive webhook for your repository. This approach simplifies repository management while ensuring that all associated Amplify apps receive updates and triggers without being constrained by webhook restrictions.&lt;/p&gt; 
&lt;h4&gt;Key Benefits&lt;/h4&gt; 
&lt;ul&gt; 
 &lt;li&gt;Overcome Git Provider Limitations: Remove current webhook constraints, allowing you to connect as many Amplify apps as needed to a single repository.&lt;/li&gt; 
 &lt;li&gt;Enhanced Monorepo Support: Gain more flexibility and efficiency when working with monorepo structures, where multiple projects share a single repository.&lt;/li&gt; 
 &lt;li&gt;Simplified Management: Manage multiple Amplify apps with a single repository webhook, reducing complexity and potential points of failure.&lt;/li&gt; 
 &lt;li&gt;Improved Workflow Integration: Free up webhook slots for other essential workflows in your development process.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;&lt;/h3&gt; 
&lt;h3&gt;Getting Started with Unified Webhooks&lt;/h3&gt; 
&lt;p&gt;&lt;strong&gt;For New Apps&lt;/strong&gt;&lt;br&gt; &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/getting-started.html"&gt;Deploy a web app&lt;/a&gt; with Amplify Hosting. The unified webhook feature will be automatically implemented for your repository.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;For Existing Amplify Users&lt;/strong&gt;&lt;br&gt; To take advantage of this new feature, you’ll need to reconnect your repository to your Amplify app. Here’s how:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Navigate to your Amplify app in the AWS Management Console.&lt;/li&gt; 
 &lt;li&gt;Locate the repository settings for your app.&lt;/li&gt; 
 &lt;li&gt;Click on the “Reconnect” button next to your repository information.&lt;/li&gt; 
 &lt;li&gt;Confirm the action to replace existing webhooks with the new unified webhook.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3&gt;&lt;strong&gt;Walkthrough&lt;/strong&gt;&lt;/h3&gt; 
&lt;p&gt;Here is an example of a current repository with &lt;strong&gt;two Amplify apps&lt;/strong&gt; switching to use the new unified webhook.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13868" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/10/redactedImage.jpg-scaled.jpeg" alt="Screenshot of the GitHub webhook settings" width="2560" height="882"&gt;&lt;/p&gt; 
&lt;p&gt;Each webhook looks like&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13869" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/10/redactedImage-1.jpg-scaled.jpeg" alt="GitHub Webhook payload settings" width="2560" height="1831"&gt;&lt;/p&gt; 
&lt;p&gt;Then go to your Amplify Console, find the &lt;strong&gt;Reconnect Repository&lt;/strong&gt; button in &lt;strong&gt;App settings → Branch settings&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13873" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/10/Amplify-Branch-Settings-scaled.jpeg" alt="Screenshot of Amplify console and the branch settings area" width="2560" height="1093"&gt;&lt;/p&gt; 
&lt;p&gt;Click the &lt;strong&gt;Configure GitHub App &lt;/strong&gt;and &lt;strong&gt;Complete installation&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13874" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/10/Screenshot-2024-11-20-at-11.20.52 AM.png" alt="Screenshot of Amplify Console install github app" width="2868" height="698"&gt;&lt;/p&gt; 
&lt;p&gt;After a few moments, you will see the repository is switched to the unified webhook.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13875" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/10/redactedImage-4.jpg-1-scaled.jpeg" alt="Screenshot of GitHub webhook settings to see the updated webhook from amplify" width="2560" height="1830"&gt;&lt;/p&gt; 
&lt;p&gt;You are all set now. Any new amplify app connected to this repository will use the same unified webhook here.&lt;/p&gt; 
&lt;h3&gt;Important Considerations&lt;/h3&gt; 
&lt;p&gt;&lt;strong&gt;Webhook Limit During Migration:&lt;/strong&gt; If you’ve already reached the maximum number of webhooks allowed by your Git provider, the automatic migration may not work. In this case, you might need to manually remove at least one existing webhook before reconnecting.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Regional Operations:&lt;/strong&gt; All operations, including the webhook migration, are region-based. This means the migration will only occur for the regional webhooks where you reconnect your Amplify app.&lt;/p&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;The introduction of unified webhooks in AWS Amplify Hosting simplifies repository management and enhances support for complex project structures like monorepos. By reducing webhook overhead and streamlining the connection between your git repository and Amplify apps, we’re enabling developers to focus more on building great applications and less on managing infrastructure limitations.&lt;/p&gt; 
&lt;p&gt;We’re excited to see how this feature will improve your development workflow, especially for those working with large, complex repositories. Deploy your Next.js, Nuxt, React, Angular, Vue, or other frontend apps in the &lt;a href="https://us-east-1.console.aws.amazon.com/amplify/apps"&gt;Amplify Console&lt;/a&gt;, and join our community on &lt;a href="http://discord.gg/amplify"&gt;Discord&lt;/a&gt; to share your thoughts and experiences!&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;About the Authors&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="wp-image-5165 size-thumbnail alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2021/01/28/matt_A_headshot-150x150.jpg" alt="Matt's headshot with a rock and forest behind him" width="150" height="150"&gt;&lt;strong&gt;Matt Auerbach, Senior Product Manager, Amplify Hosting&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Matt Auerbach is a NYC-based Product Manager on the AWS Amplify Team. He educates developers regarding products and offerings, and acts as the primary point of contact for assistance and feedback. Matt is a mild-mannered programmer who enjoys using technology to solve problems and making people’s lives easier. B night, however…well he does pretty much the same thing. You can find Matt on Twitter&amp;nbsp;@mauerbac. He previously worked in Developer Relations at Twitch, Optimizely &amp;amp; Twilio.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="wp-image-13883 size-thumbnail alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/03/10/IMG_6173-1-150x150.jpg" alt="linson headshot with a national park in the background" width="150" height="150"&gt;&lt;strong&gt;Linsong Wang, Software Development Engineer, Amplify Hosting&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Linsong builds features that make it easier for customers to host front-end web applications backed by the reliability and convenience of AWS. In his free time, Linsong enjoys exploring cooking recipes, playing piano, and building life improvement prototype&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>IAM Compute Roles for Server-Side Rendering with AWS Amplify Hosting</title>
		<link>https://aws.amazon.com/blogs/mobile/iam-compute-roles-for-server-side-rendering-with-aws-amplify-hosting/</link>
		
		<dc:creator><![CDATA[Jay Raval]]></dc:creator>
		<pubDate>Tue, 18 Feb 2025 21:17:34 +0000</pubDate>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[AWS Identity and Access Management (IAM)]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Amplify]]></category>
		<category><![CDATA[AWS Amplify Hosting]]></category>
		<category><![CDATA[iam]]></category>
		<guid isPermaLink="false">0713c4b378f74caf4d9f5ab96f496b0bb5a726b6</guid>

					<description>Today, AWS Amplify Hosting is introducing compute roles for AWS Amplify applications, enabling you to extend server-side rendering capabilities with secure access to AWS services from the compute runtime. With compute roles, developers can attach specific permissions to their server-side rendered apps, allowing Amplify to make authorized calls to other AWS services. This new capability […]</description>
										<content:encoded>&lt;p&gt;Today, &lt;a href="https://aws.amazon.com/amplify/hosting/"&gt;AWS Amplify Hosting&lt;/a&gt; is introducing compute roles for AWS Amplify applications, enabling you to extend server-side rendering capabilities with secure access to AWS services from the compute runtime. With compute roles, developers can attach specific permissions to their server-side rendered apps, allowing Amplify to make authorized calls to other AWS services. This new capability helps streamline the development process while maintaining security best practices for your applications.&lt;/p&gt; 
&lt;h2&gt;&lt;span style="text-decoration: underline"&gt;Key capabilities&lt;/span&gt;&lt;/h2&gt; 
&lt;p&gt;With compute roles, you can now:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Access sensitive configuration data at runtime within Next.js API routes using &lt;a href="https://aws.amazon.com/secrets-manager/"&gt;AWS Secrets Manager&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html"&gt;AWS Systems Manager Parameter Store&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;Connect your SSR apps directly to databases like &lt;a href="https://aws.amazon.com/rds/"&gt;Amazon RDS&lt;/a&gt;, &lt;a href="https://aws.amazon.com/dynamodb/"&gt;Amazon DynamoDB&lt;/a&gt;. and other AWS databases&lt;/li&gt; 
 &lt;li&gt;Define fine-grained permissions for your compute environment using AWS Identity and Access Management (IAM) policies&lt;/li&gt; 
 &lt;li&gt;Make secure, authenticated calls to any AWS service from your server-side code&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2&gt;&lt;span style="text-decoration: underline"&gt;Tutorial&lt;/span&gt;&lt;/h2&gt; 
&lt;p&gt;To get started, follow these steps to create an IAM compute role and associate it with an Amplify Next.js application:&lt;/p&gt; 
&lt;h3&gt;&lt;span style="text-decoration: underline"&gt;Prerequisites&lt;/span&gt;&lt;/h3&gt; 
&lt;p&gt;Before you begin, make sure you have the following installed:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;a href="https://nodejs.org/en"&gt;Node.js&lt;/a&gt; (v18.x or later)&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://docs.npmjs.com/getting-started"&gt;npm&lt;/a&gt; or &lt;a href="https://docs.npmjs.com/cli/v11/commands/npx"&gt;npx&lt;/a&gt; (v10.x or later)&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt; (v2.39.5 or later)&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/cli/"&gt;AWS CLI&lt;/a&gt;&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;&lt;span style="text-decoration: underline"&gt;Create an IAM compute role&lt;/span&gt;&lt;/h3&gt; 
&lt;p&gt;Let’s create an IAM role that allows Amplify Hosting’s SSR compute service to securely access AWS resources based on the role’s permission and trust relationship. In our example, we’ll securely access an Amazon Simple Storage Service (Amazon S3) bucket.&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Sign in to the AWS Management console and navigate to the &lt;a href="https://us-east-1.console.aws.amazon.com/iamv2/home?"&gt;IAM console&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;Under the Access Management tab, choose Roles, then select &lt;strong&gt;Create role&lt;/strong&gt;&lt;/li&gt; 
 &lt;li&gt;Select Custom trust policy and enter the following policy to allow Amplify to assume the role:&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-json"&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "amplify.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;ol start="4"&gt; 
 &lt;li&gt;Add the AWS managed &lt;code&gt;AmazonS3ReadOnlyAccess&lt;/code&gt; permission policy to the role and choose &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt; 
 &lt;li&gt;Name the role &lt;code&gt;amplify-compute-role&lt;/code&gt; and choose &lt;strong&gt;Create role&lt;/strong&gt;&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;You have successfully created a compute role that Amplify can assume to connect to other AWS services.&lt;/p&gt; 
&lt;p&gt;&lt;span style="text-decoration: underline"&gt;&lt;strong&gt;Security Best Practice&lt;/strong&gt;:&lt;/span&gt; Implement the principle of least privilege by granting only essential permissions for your specific use case.&lt;/p&gt; 
&lt;h3&gt;&lt;span style="text-decoration: underline"&gt;Create an Amazon S3 bucket&lt;/span&gt;&lt;/h3&gt; 
&lt;p&gt;We will now setup a private Amazon S3 bucket that the Next.js app can securely access using the compute role. Instead of exposing Amazon S3 objects publicly or managing access keys, we’ll use Amplify Hosting’s compute role to securely retrieve private content from Amazon S3.&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Sign in to the AWS Management console and navigate to the &lt;a href="https://us-east-1.console.aws.amazon.com/s3/home?"&gt;Amazon S3 console&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Create bucket&lt;/strong&gt; and enter a unique bucket name&lt;/li&gt; 
 &lt;li&gt;Keep &lt;strong&gt;ACLs disabled&lt;/strong&gt; and &lt;strong&gt;Block all public access&lt;/strong&gt; enabled&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignleft wp-image-13806 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/02/18/Screenshot-2025-02-14-at-2.54.30 PM.png" alt="Object Ownership and Public Access settings for the bucket" width="1682" height="1450"&gt;&lt;/p&gt; 
&lt;ol start="4"&gt; 
 &lt;li&gt;Keep the default settings and choose &lt;strong&gt;Create bucket&lt;/strong&gt;&lt;/li&gt; 
 &lt;li&gt;Navigate to the bucket and choose &lt;strong&gt;Upload&lt;/strong&gt;&lt;/li&gt; 
 &lt;li&gt;Upload an image (e.g. amplify-logo.png)&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;&lt;span style="text-decoration: underline"&gt;&lt;strong&gt;Security Verification&lt;/strong&gt;:&lt;/span&gt; To confirm that your S3 bucket is completely locked down, run the following &lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/get-public-access-block.html"&gt;get-public-access-block&lt;/a&gt; AWS CLI command:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;// Note: Replace amplify-compute-role-demo with your specific bucket name
aws s3api get-public-access-block --bucket amplify-compute-role-demo&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;The expected output should show:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-json"&gt;{
    "PublicAccessBlockConfiguration": {
        "BlockPublicAcls": true,
        "IgnorePublicAcls": true,
        "BlockPublicPolicy": true,
        "RestrictPublicBuckets": true
    }
}&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;These settings ensure:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;No public ACLs can be created&lt;/li&gt; 
 &lt;li&gt;Existing public ACLs are ignored&lt;/li&gt; 
 &lt;li&gt;Public bucket policies are blocked&lt;/li&gt; 
 &lt;li&gt;Public access to the bucket is restricted&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;&lt;span style="text-decoration: underline"&gt;Create a Next.js app&lt;/span&gt;&lt;/h3&gt; 
&lt;p&gt;Next, we will create a Next.js app to securely access private content (an image) from an Amazon S3 bucket using an API route.&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Create a new Next.js 15 app with Typescript and Tailwind CSS&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npx create-next-app@latest compute-role-demo --typescript --tailwind --eslint
cd compute-role-demo&lt;/code&gt;&lt;/pre&gt; 
&lt;ol start="2"&gt; 
 &lt;li&gt;Install the AWS SDK for JavaScript S3 Client package:&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npm install @aws-sdk/client-s3&lt;/code&gt;&lt;/pre&gt; 
&lt;ol start="3"&gt; 
 &lt;li&gt;Create an Amazon S3 Image API route:&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// app/api/image/route.ts

import { S3Client, GetObjectCommand, S3ServiceException } from "@aws-sdk/client-s3";
import { NextResponse } from 'next/server';

const BUCKET_NAME = 'amplify-compute-role-demo';
const IMAGE_KEY = 'amplify-logo.png';

export async function GET() {
  console.log(`[S3 Image Request] Starting - Bucket: ${BUCKET_NAME}, Key: ${IMAGE_KEY}`);
  const s3Client = new S3Client({});
  
  try {
    console.log('[S3 Image Request] Creating GetObjectCommand...');
    const command = new GetObjectCommand({
      Bucket: BUCKET_NAME,
      Key: IMAGE_KEY
    });

    console.log('[S3 Image Request] Sending request to S3...');
    const response = await s3Client.send(command);
    console.log('[S3 Image Request] Received S3 response:', {
      contentType: response.ContentType,
      contentLength: response.ContentLength,
      metadata: response.Metadata
    });

    console.log('[S3 Image Request] Converting response to byte array...');
    const buffer = await response.Body?.transformToByteArray();

    if (!buffer) {
      console.error('[S3 Image Request] No buffer received from S3');
      throw new Error('No image data received from S3');
    }

    console.log('[S3 Image Request] Successfully processed image:', {
      bufferSize: buffer.length,
      contentType: response.ContentType
    });

    return new NextResponse(buffer, {
      headers: {
        'Content-Type': response.ContentType || 'image/png',
        'Cache-Control': 'public, max-age=31536000, immutable'
      }
    });

  } catch (error) {
    if (error instanceof S3ServiceException) {
      console.error('[S3 Image Request] AWS S3 Error:', {
        message: error.message,
        code: error.name,
        requestId: error.$metadata?.requestId,
        statusCode: error.$metadata?.httpStatusCode
      });
    } else {
      console.error('[S3 Image Request] Unexpected error:', error);
    }

    return NextResponse.json({
      success: false,
      error: 'Failed to load content'
    }, { 
      status: 500 
    });
  } finally {
    console.log('[S3 Image Request] Request completed');
  }
}&lt;/code&gt;&lt;/pre&gt; 
&lt;ol start="4"&gt; 
 &lt;li&gt;Create a client component:&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// app/components/S3Component.tsx

'use client';

import { useState } from 'react';
import Image from 'next/image';

export default function S3Component() {
  const [imageError, setImageError] = useState(false);
  const [isRevealed, setIsRevealed] = useState(false);

  return (
    &amp;lt;div className="absolute inset-0 top-[88px] flex items-center justify-center"&amp;gt;
      &amp;lt;div className="w-full max-w-2xl rounded-2xl mx-4"&amp;gt;
        &amp;lt;div className="flex flex-col items-center justify-center min-h-[400px] p-8"&amp;gt;
          {!isRevealed ? (
            &amp;lt;button
              onClick={() =&amp;gt; setIsRevealed(true)}
              className="px-8 py-4 bg-[rgb(117,81,194)] text-white text-lg rounded-xl 
                       hover:bg-[rgb(107,71,184)] transition-colors"
            &amp;gt;
              &amp;lt;span className="flex items-center gap-2"&amp;gt;
                Access Private S3 Bucket
                &amp;lt;span className="text-xl"&amp;gt;⚡&amp;lt;/span&amp;gt;
              &amp;lt;/span&amp;gt;
            &amp;lt;/button&amp;gt;
          ) : (
            &amp;lt;div className="space-y-8 w-full"&amp;gt;              
              {!imageError &amp;amp;&amp;amp; (
                &amp;lt;div className="relative h-64 w-full"&amp;gt;
                  &amp;lt;Image 
                    src="/api/image"
                    alt="Amplify Logo"
                    fill
                    unoptimized
                    priority
                    onError={() =&amp;gt; setImageError(true)}
                    className="object-contain"
                    sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                  /&amp;gt;
                &amp;lt;/div&amp;gt;
              )}
              
              &amp;lt;div className="text-center"&amp;gt;
                &amp;lt;p className="text-gray-400 text-sm"&amp;gt;
                  This content is securely served from S3 using an IAM Compute Role.
                &amp;lt;/p&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          )}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt; 
&lt;ol start="5"&gt; 
 &lt;li&gt;Update the home page to render the client component:&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import S3Component from './components/S3Component';

export default function HomePage() {
  return (
    &amp;lt;div className="relative min-h-screen bg-[rgb(0,0,0)]"&amp;gt;
      &amp;lt;h1 className="text-2xl font-bold text-white py-8 text-center relative z-10"&amp;gt;
        Amplify Hosting Compute Role Demo
      &amp;lt;/h1&amp;gt;
      &amp;lt;S3Component /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt; 
&lt;ol start="6"&gt; 
 &lt;li&gt;Push the changes to a git repository: 
  &lt;ol&gt; 
   &lt;li&gt;Create a &lt;a href="https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository"&gt;new&lt;/a&gt; GitHub repository&lt;/li&gt; 
   &lt;li&gt;Add and commit the changes to the git branch&lt;/li&gt; 
   &lt;li&gt;Add remote origin and push the changes upstream&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-git"&gt;git add .
git commit -m "initial commit"
git remote add origin https://github.com/&amp;lt;OWNER&amp;gt;/amplify-compute-role-demo.git
git push -u origin main&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;&lt;span style="text-decoration: underline"&gt;Deploy the application to Amplify Hosting&lt;/span&gt;&lt;/h3&gt; 
&lt;ol&gt; 
 &lt;li&gt;Sign in to the AWS Management Console and navigate to the &lt;a href="https://us-east-1.console.aws.amazon.com/amplify/home?"&gt;Amplify console&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Create new app&lt;/strong&gt; and select GitHub as the repository source&lt;/li&gt; 
 &lt;li&gt;Authorize Amplify to access your GitHub account&lt;/li&gt; 
 &lt;li&gt;Choose the repository and branch you created&lt;/li&gt; 
 &lt;li&gt;Review the App settings and then choose &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt; 
 &lt;li&gt;Review the overall settings and choose &lt;strong&gt;Save and deploy&lt;/strong&gt;&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3&gt;&lt;span style="text-decoration: underline"&gt;Attaching the IAM compute role to an Amplify app&lt;/span&gt;&lt;/h3&gt; 
&lt;ol&gt; 
 &lt;li&gt;In the Amplify console, select your app and navigate to App settings &amp;gt; IAM roles&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;img loading="lazy" class="alignnone wp-image-13815 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/02/18/Screenshot-2025-02-06-at-1.00.12 PM.png" alt="IAM roles section displayed in the Amplify console" width="1273" height="693"&gt;&lt;/pre&gt; 
&lt;ol start="2"&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Edit&lt;/strong&gt; in the compute role section&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-13816 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/02/18/Screenshot-2025-02-06-at-1.01.29 PM.png" alt="Compute role section displayed in the Amplify console" width="1279" height="416"&gt;&lt;/p&gt; 
&lt;ol start="3"&gt; 
 &lt;li&gt;From the role menu, select the &lt;code&gt;amplify-compute-role&lt;/code&gt; and choose &lt;strong&gt;Save&lt;/strong&gt;&lt;/li&gt; 
 &lt;li&gt;You can also add branch overrides to use a unique compute role per branch. This can particularly be helpful across different git environments such as dev, staging, or production.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3&gt;&lt;span style="text-decoration: underline"&gt;Access the deployed Next.js app&lt;/span&gt;&lt;/h3&gt; 
&lt;p&gt;Navigate to the app’s &lt;strong&gt;Overview&lt;/strong&gt; tab in the Amplify console and open the default &lt;strong&gt;Amplify generated URL&lt;/strong&gt; in the browser.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-13819 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/02/18/Screenshot-2025-02-17-at-5.54.53 PM.png" alt="Overview tab with a Visit deployed URL button in the Amplify console" width="1013" height="322"&gt;&lt;/p&gt; 
&lt;p&gt;Next, choose the &lt;strong&gt;Access Private S3&lt;/strong&gt; button. You should now observe the image that was uploaded to the Amazon S3 bucket.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-13818 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/02/18/Screenshot-2025-02-14-at-3.30.06 PM-1024x777.png" alt="" width="1024" height="777"&gt;&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-13817 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/02/18/Screenshot-2025-02-14-at-3.30.13 PM-1024x828.png" alt="" width="1024" height="828"&gt;&lt;/p&gt; 
&lt;h3&gt;&lt;span style="text-decoration: underline"&gt;Review the hosting compute logs&lt;/span&gt;&lt;/h3&gt; 
&lt;p&gt;From the App home page, navigate to Hosting &amp;gt; Monitoring and then choose the &lt;strong&gt;Hosting compute logs&lt;/strong&gt; tab.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-13821 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/02/18/Screenshot-2025-02-06-at-12.49.29 PM.png" alt="Monitoring section of the app displayed in the Amplify console" width="904" height="544"&gt;&lt;/p&gt; 
&lt;p&gt;Navigate to the Amazon CloudWatch log streams URL and review the latest log stream. The logs should contain the Amazon S3 image requests:&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone wp-image-13822 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/02/18/Screenshot-2025-02-14-at-3.37.04 PM-1.png" alt="Hosting compute logs displaying Amazon S3 image requests" width="2898" height="1658"&gt;&lt;/p&gt; 
&lt;p&gt;Congratulations! You’ve successfully created and attached an IAM compute role to your Next.js SSR application on Amplify Hosting, enabling secure content retrieval from your private Amazon S3 bucket! ?&lt;/p&gt; 
&lt;h2&gt;&lt;span style="text-decoration: underline"&gt;Cleanup&lt;/span&gt;&lt;/h2&gt; 
&lt;ol&gt; 
 &lt;li&gt;Delete the AWS Amplify app by navigating to App settings &amp;gt; General settings, then choose Delete app.&lt;/li&gt; 
 &lt;li&gt;Delete the Amazon S3 bucket by navigating to S3 console &amp;gt; Select bucket &amp;gt; Select Delete &amp;gt; Enter the bucket name to confirm deletion&lt;/li&gt; 
 &lt;li&gt;Delete the IAM role by navigating to IAM console &amp;gt; Select Roles &amp;gt; Find the amplify-compute-role name &amp;gt; Select Delete &amp;gt; Enter the role name to confirm deletion&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2&gt;&lt;span style="text-decoration: underline"&gt;Summary&lt;/span&gt;&lt;/h2&gt; 
&lt;p&gt;AWS Amplify Hosting now offers compute roles for server-side rendered (SSR) applications, solving the challenge of securely accessing AWS services. Previously, developers manually managed credentials through environment variables. Now, with compute roles, developers can directly attach IAM permissions to their SSR apps, simplifying service integration and enhancing security.&lt;/p&gt; 
&lt;p&gt;Add compute roles to your SSR apps today by checking out the Amplify &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/amplify-SSR-compute-role.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt; 
&lt;div class="blog-author-box"&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-box"&gt; 
   &lt;h2&gt;&lt;span style="text-decoration: underline"&gt;About the Authors&lt;/span&gt;&lt;/h2&gt; 
   &lt;footer&gt; 
    &lt;table style="width: 100%;text-align: left"&gt; 
     &lt;tbody&gt; 
      &lt;tr&gt; 
       &lt;td&gt; 
        &lt;div class="blog-author-box"&gt; 
         &lt;div class="blog-author-image"&gt;
          &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/08/13/IMG_9570.jpg" alt="Photo of author" width="120" height="160"&gt;
         &lt;/div&gt; 
         &lt;h3 class="lb-h4"&gt;Jay Raval&lt;/h3&gt; 
         &lt;p&gt;Jay Raval is a Solutions Architect on the AWS Amplify team. He’s passionate about solving complex customer problems in the front-end, web and mobile domain and addresses real-world architecture problems for development using front-end technologies and AWS. In his free time, he enjoys traveling and sports. You can find Jay on &lt;a href="https://x.com/_Jay_Raval_"&gt;X @_Jay_Raval_&lt;/a&gt;&lt;/p&gt; 
        &lt;/div&gt; &lt;/td&gt; 
      &lt;/tr&gt; 
      &lt;tr&gt; 
       &lt;td&gt; 
        &lt;div class="blog-author-box"&gt; 
         &lt;div class="blog-author-image"&gt;
          &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/02/19/Screenshot-2024-02-19-at-3.13.45 PM.png" alt="Photo of author" width="120" height="160"&gt;
         &lt;/div&gt; 
         &lt;h3 class="lb-h4"&gt;Matt Auerbach&lt;/h3&gt; 
         &lt;p&gt;Matt Auerbach is a NYC-based Product Manager on the AWS Amplify Team. He educates developers regarding products and offerings, and acts as the primary point of contact for assistance and feedback. Matt is a mild-mannered programmer who enjoys using technology to solve problems and making people’s lives easier. B night, however…well he does pretty much the same thing. You can find Matt on &lt;a href="https://x.com/mauerbac"&gt;X @mauerbac&lt;/a&gt;. He previously worked at Twitch, Optimizely &amp;amp; Twilio.&lt;/p&gt; 
        &lt;/div&gt; &lt;/td&gt; 
      &lt;/tr&gt; 
     &lt;/tbody&gt; 
    &lt;/table&gt; 
   &lt;/footer&gt; 
  &lt;/div&gt; 
 &lt;/div&gt; 
&lt;/div&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Build a Scalable Search Solution with AWS Amplify and OpenSearch Serverless</title>
		<link>https://aws.amazon.com/blogs/mobile/build-a-scalable-search-solution-with-aws-amplify-and-opensearch-serverless/</link>
		
		<dc:creator><![CDATA[Anil Maktala]]></dc:creator>
		<pubDate>Fri, 31 Jan 2025 21:40:00 +0000</pubDate>
				<category><![CDATA[Amazon OpenSearch Service]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[#serverless]]></category>
		<category><![CDATA[Amplify]]></category>
		<category><![CDATA[AppSync]]></category>
		<category><![CDATA[aws-amplify]]></category>
		<category><![CDATA[aws-appsync]]></category>
		<guid isPermaLink="false">7a84627f602a62e19e9b01039c7c676eff755755</guid>

					<description>Introduction In today’s data-driven digital ecosystem, building scalable search solutions has become essential for organizations managing large, complex datasets across industries. Whether it’s a streaming platform, an e-commerce marketplace, or a research database, the ability to efficiently search through structured and unstructured data is key to delivering a seamless user experience. Traditional search solutions often […]</description>
										<content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt; 
&lt;p&gt;In today’s data-driven digital ecosystem, building scalable search solutions has become essential for organizations managing large, complex datasets across industries. Whether it’s a streaming platform, an e-commerce marketplace, or a research database, the ability to efficiently search through structured and unstructured data is key to delivering a seamless user experience. Traditional search solutions often involve complex data transformation processes and managing dedicated search clusters, which can increase operational costs and complexity.&lt;/p&gt; 
&lt;p&gt;By leveraging &lt;a href="https://aws.amazon.com/amplify"&gt;AWS Amplify&lt;/a&gt;, &lt;a href="https://aws.amazon.com/opensearch-service/"&gt;Amazon OpenSearch&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/pm/dynamodb"&gt;Amazon DynamoDB&lt;/a&gt; with Zero ETL, you can build scalable, cost-effective, and serverless search solutions. This approach simplifies infrastructure management, reduces costs, and ensures a seamless user experience even under fluctuating traffic.&lt;/p&gt; 
&lt;p&gt;In this post, we’ll demonstrate how to create a movie search solution using AWS Amplify and Amazon OpenSearch. This solution will provide search capabilities, allowing users to quickly find relevant movie information. We’ll use AWS Amplify’s streamlined development experience and Amazon OpenSearch’s to build this solution.&lt;/p&gt; 
&lt;div id="attachment_13771" style="width: 725px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-13771" loading="lazy" class="wp-image-13771" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/01/24/blog_figure8-1024x941.png" alt="Figure 1: A movie collection website displaying a search bar and grid of five popular movie titles with their plot descriptions in white cards." width="715" height="657"&gt;
 &lt;p id="caption-attachment-13771" class="wp-caption-text"&gt;Figure 1: Movie Search Solution&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Prerequisites&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;a href="https://portal.aws.amazon.com/billing/signup#/start/email"&gt;AWS account&lt;/a&gt;: Required to access and deploy AWS services. Note that Amplify is part of the &lt;a href="https://aws.amazon.com/amplify/pricing/"&gt;AWS Free Tier&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://nodejs.org/en"&gt;Node.js&lt;/a&gt; v18.17+ and &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt; v9+.&lt;/li&gt; 
 &lt;li&gt;Git v2.14.1+: For version control and repository management.&lt;/li&gt; 
 &lt;li&gt;Text Editor (VSCode recommended): Provides an integrated development environment.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;High Level Architecture&lt;/h2&gt; 
&lt;p&gt;Our serverless search solution combines several AWS services:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;AWS Amplify: Provides a modern development framework for building cloud-based applications.&lt;/li&gt; 
 &lt;li&gt;Amazon OpenSearch: Service: Powers the search functionality with advanced search capabilities.&lt;/li&gt; 
 &lt;li&gt;Amazon DynamoDB: Acts as the primary data store for the application.&lt;/li&gt; 
 &lt;li&gt;Amazon DynamoDB Zero ETL: Streamlines the data ingestion process from DynamoDB to OpenSearch Service.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/s3/"&gt;Amazon S3&lt;/a&gt;: The S3 bucket will be used for the initial export, which OpenSearch Ingestion will retrieve data from at the beginning.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;High-level architecture diagram showing the integration of AWS Amplify, Amazon DynamoDB, Amazon OpenSearch Serverless, and Amazon S3 in a serverless search solution. The diagram illustrates data flow between services, highlighting how DynamoDB Zero ETL synchronizes data with OpenSearch through an S3 staging area.&lt;/p&gt; 
&lt;div id="attachment_13757" style="width: 813px" class="wp-caption aligncenter"&gt;
 &lt;img aria-describedby="caption-attachment-13757" loading="lazy" class="wp-image-13757" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/01/24/blog_figure2-1024x728.png" alt="Figure 2: High-level technical architecture diagram illustrating the integration of AWS Amplify, Amazon DynamoDB, Amazon OpenSearch Serverless, and Amazon S3, with detailed data synchronization and flow paths" width="803" height="571"&gt;
 &lt;p id="caption-attachment-13757" class="wp-caption-text"&gt;Figure 2: Architecture Diagram of the Movie Search Solution&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Use the AWS Amplify-powered frontend to submit search queries, which Amazon OpenSearch processes against pre-indexed data. Amazon DynamoDB serves as the primary data store, capturing and updating application data in real time. The Zero ETL feature automatically synchronizes this data with OpenSearch, enabling instant indexing of your latest information.&lt;br&gt; Amazon S3 serves as an initial staging area during the first data transfer between Dynamo db and OpenSearch. This architecture ensures a responsive, low-latency search experience, providing users with accurate and relevant results while maintaining high scalability and performance.&lt;/p&gt; 
&lt;h2&gt;Implementation Steps&lt;/h2&gt; 
&lt;h2&gt;Step 1: Repository Setup&lt;/h2&gt; 
&lt;ol&gt; 
 &lt;li&gt;Navigate to the &lt;a href="https://github.com/aws-samples/amplify-opensearch-serverless-integration"&gt;amplify-opensearch-serverless-integration&lt;/a&gt; repository on AWS Samples and fork it to your GitHub repositories.&lt;/li&gt; 
 &lt;li&gt;Clone the app by running the command below in your terminal &lt;pre&gt;&lt;code class="lang-bash"&gt;git clone https://github.com/&amp;lt;YOUR_GITHUB&amp;gt;/amplify-opensearch-serverless-integration.git&lt;/code&gt;&lt;/pre&gt; &lt;/li&gt; 
 &lt;li&gt;Access the newly cloned repository in VSCode by executing the commands below in your terminal. &lt;pre&gt;&lt;code class="lang-bash"&gt;cd amplify-opensearch-serverless-integration
code .&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;VSCode will open the repository folder, including the Amplify folder, which contains the backend details that we’ll discuss in the next section.&lt;/p&gt; 
  &lt;ol&gt; 
   &lt;li&gt;&lt;strong&gt;Project Structure Overview&lt;/strong&gt;: The structure includes folders like amplify, backend, data, and storage, with key configuration files like &lt;code&gt;backend.ts,&lt;/code&gt; &lt;code&gt;resource.ts&lt;/code&gt;, and &lt;code&gt;searchMovieResolver.js&lt;/code&gt; visible. &lt;p&gt;&lt;/p&gt;
    &lt;div id="attachment_13756" style="width: 527px" class="wp-caption aligncenter"&gt;
     &lt;img aria-describedby="caption-attachment-13756" loading="lazy" class=" wp-image-13756" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/01/24/blog_figure3-1024x971.png" alt=" Figure 3: Project Structure Overview in Visual Studio Code" width="517" height="490"&gt;
     &lt;p id="caption-attachment-13756" class="wp-caption-text"&gt;Figure 3: Project Structure Overview in Visual Studio Code&lt;/p&gt;
    &lt;/div&gt;&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
 &lt;li&gt;&amp;nbsp;Run the following command at the project root in the terminal to install the necessary packages, including the Amplify packages.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p style="text-align: left"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt; 
&lt;h2&gt;Step 2: Amplify Backend Configuration&lt;/h2&gt; 
&lt;p&gt;In the final application, users can search for movies by entering a &lt;code&gt;title&lt;/code&gt; and clicking the search button to retrieve results from Amazon OpenSearch. The code for this functionality is included in the repository you cloned. In this section, we’ll walk through the key steps to integrate your Amplify app with Amazon OpenSearch.&lt;/p&gt; 
&lt;h3&gt;2.1 Define Data Model&lt;/h3&gt; 
&lt;p&gt;Firstly, go to the &lt;code&gt;amplify/data/resource.ts&lt;/code&gt; file and add the Movie model to your schema.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";

const schema = a.schema({
  Movie: a.model({
    title: a.string().required(),
    description: a.string(),
    releaseYear: a.integer(),
    director: a.string(),
    actors: a.string().array(),
    genre: a.string(),
    rating: a.float(),
  }).authorization((allow)=&amp;gt;[allow.owner(), allow.publicApiKey()]),
});

export type Schema = ClientSchema&amp;lt;typeof schema&amp;gt;;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: "userPool",
  },
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;strong&gt;Important considerations:&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;To ensure smooth data integration between Amazon DynamoDB and Amazon OpenSearch, you’ll need to configure two critical features:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Point-in-Time Recovery (PITR) PITR allows you to: 
  &lt;ol&gt; 
   &lt;li&gt;Restore your table to any point in the last 35 days.&lt;/li&gt; 
   &lt;li&gt;Capture continuous backups of your data.&lt;/li&gt; 
   &lt;li&gt;Enable reliable data recovery and synchronization.&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
 &lt;li&gt;DynamoDB Streams DynamoDB Streams provide: 
  &lt;ol&gt; 
   &lt;li&gt;Real-time tracking of data modifications.&lt;/li&gt; 
   &lt;li&gt;Capture of all item-level changes.&lt;/li&gt; 
   &lt;li&gt;Essential support for streaming data to OpenSearch.&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Navigate to your &lt;code&gt;amplify/backend.ts&lt;/code&gt; file and add the following configuration:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import { defineBackend } from "@aws-amplify/backend";
import { auth } from "./auth/resource";
import { data } from "./data/resource";


const backend = defineBackend({
  auth,
  data,
});

// Get reference to the Movie table from Amplify resources
const movieTable =
  backend.data.resources.cfnResources.amplifyDynamoDbTables["Movie"];
// Enable Point-in-Time Recovery (PITR)  
movieTable.pointInTimeRecoveryEnabled = true;
// Configure DynamoDB Streams
movieTable.streamSpecification = {
  streamViewType: dynamodb.StreamViewType.NEW_IMAGE,
};
&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Step 3: OpenSearch Serverless collection Setup&lt;/h2&gt; 
&lt;p&gt;In your code go to &lt;code&gt;amplify/backend.ts&lt;/code&gt; file, create an Amazon OpenSearch serverless collection.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
import * as oss from "aws-cdk-lib/aws-opensearchserverless";
import { RemovalPolicy,Stack } from "aws-cdk-lib";

// Get the data stack
const openSearchStack = Stack.of(backend.data);
const region = openSearchStack.region;
const collectionName = "dynamodb-etl-collection";
const openSearchServerlessCollection = new oss.CfnCollection(
  openSearchStack,
  "OpenSearchServerlessCollection",
  {
    name: collectionName,
    description: "DynamoDB to OpenSearch Pipeline ETL Integration.",
    type: "SEARCH",
  }
);
// set removalPolicy to DESTROY to make sure the OpenSearch collection is deleted on stack deletion.
openSearchServerlessCollection.applyRemovalPolicy(RemovalPolicy.DESTROY);&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;3.1 Add OpenSearch as HttpDatasource to backend&lt;/h3&gt; 
&lt;p&gt;To add the OpenSearch data source, add this code to &lt;code&gt;amplify/backend.ts&lt;/code&gt;.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
/**
 * Create OpenSearch Serverless Data Source
 * This configures the OpenSearch endpoint as an HTTP data source for API operations
 */
const openSearchDataSource = backend.data.addHttpDataSource(
  "OpenSearchServerlessDataSource",
  openSearchServerlessCollection.attrCollectionEndpoint,
  {
    authorizationConfig: {
      // Region where the OpenSearch collection exists
      signingRegion: region,
      
      // Service name for request signing (aoss = Amazon OpenSearch Serverless)
      signingServiceName: "aoss",
    },
  }
);

/**
 * Configure Data Source IAM Permissions
 * Grants necessary permissions for OpenSearch operations using IAM policies
 */
openSearchDataSource.grantPrincipal.addToPrincipalPolicy(
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ["aoss:APIAccessAll"],
    resources: [
      // Grant access to the specific collection
      openSearchServerlessCollection.attrArn,
      // Grant access to all indices within the collection
      `${openSearchServerlessCollection.attrArn}/*`,
    ],
  })
);

const httpDataSourceRole = openSearchDataSource.grantPrincipal as iam.Role;
const httpDataSourceRoleArn = httpDataSourceRole.roleArn;&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Step 4: Zero ETL Integration&lt;/h2&gt; 
&lt;h3&gt;4.1 Storage Configuration&lt;/h3&gt; 
&lt;p&gt;Establish Storage to back up raw events consumed by the Amazon OpenSearch pipeline. Generate a file named &lt;code&gt;amplify/storage/resource.ts&lt;/code&gt; and insert the provided content to set up a storage resource. Tailor your storage configurations to regulate access to different paths within your storage bucket.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/storage/resource.ts 
import { defineStorage } from "@aws-amplify/backend";

export const storage = defineStorage({
  name: "opensearchserverless",
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;In your &lt;code&gt;amplify/backend.ts&lt;/code&gt; file, get the &lt;code&gt;s3BucketArn&lt;/code&gt; value from storage resource as shown below. Additionally, configure an IAM role for the pipeline and assign the roles as indicated below. For further information on the required IAM roles, please refer to the &lt;a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/pipeline-security-overview.html#pipeline-security-create"&gt;Setting up roles and users&lt;/a&gt; documentation.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
import { storage } from "./storage/resource";

//Update backend variable by passing storage
const backend = defineBackend({
  auth,
  data,
  storage,
});

// Get the S3Bucket ARN
const s3BucketArn = backend.storage.resources.bucket.bucketArn;&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;For the Amazon S3 bucket, adhere to standard security practices: block public access, encrypt data at rest, and enable versioning. Note that versioning is enabled by default in Amplify.&lt;/p&gt; 
&lt;h3&gt;4.2 IAM Role Configuration&lt;/h3&gt; 
&lt;p&gt;&lt;strong&gt;Understanding IAM Role Setup&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;In this section, you’ll configure the security permissions that allow your OpenSearch Ingestion Service (OSIS) to securely access and transfer data between different AWS services. Navigate to your &lt;code&gt;amplify/backend.ts&lt;/code&gt; file and add the following configuration:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Key Security Objectives:&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Grant precise access to OpenSearch.&lt;/li&gt; 
 &lt;li&gt;Secure DynamoDB and S3 data transfers.&lt;/li&gt; 
 &lt;li&gt;Follow the principle of least privilege.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;4.2.1 DynamoDB Policies&lt;/h3&gt; 
&lt;h4&gt;4.2.1.1 Export Job Policy&lt;/h4&gt; 
&lt;p&gt;Grants permissions to describe tables, check backups, and initiate exports.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
const tableName = backend.data.resources.tables["Movie"].tableName;
const tableArn = backend.data.resources.tables["Movie"].tableArn;

// Create base role with OpenSearch Ingestion managed policy
const openSearchIntegrationPipelineRole = new iam.Role(
  openSearchStack,
  "OpenSearchIntegrationPipelineRole",
  {
    assumedBy: new iam.ServicePrincipal("osis-pipelines.amazonaws.com"),
    managedPolicies: [
      iam.ManagedPolicy.fromAwsManagedPolicyName(
        "AmazonOpenSearchIngestionFullAccess"
      ),
    ],
  }
);

/**
 * Policy for DynamoDB export operations
 * Allows:
 * - Describing table configuration
 * - Checking backup capabilities
 * - Initiating table exports
 */
const dynamoDBExportJobPolicy = new iam.PolicyStatement({
  sid: "allowRunExportJob",
  effect: iam.Effect.ALLOW,
  actions: [
    // Required for getting table metadata before export
    "dynamodb:DescribeTable",
    // Required for verifying point-in-time recovery status
    "dynamodb:DescribeContinuousBackups",
    // Required for initiating table export operation
    "dynamodb:ExportTableToPointInTime",
  ],
  resources: [tableArn],
});

openSearchIntegrationPipelineRole.addToPolicy(dynamoDBExportJobPolicy);&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;4.2.1.2 Export Job Monitoring Policy&lt;/h4&gt; 
&lt;p&gt;Allows tracking of export job status using wildcard permissions for dynamic export IDs.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
import * as iam from "aws-cdk-lib/aws-iam";

/**
 * Policy for monitoring export job status
 * Allows:
 * - Checking status of ongoing export operations
 * - Monitoring export progress
 * Note: Uses wildcard for export operations as export IDs are dynamically generated
 */
const dynamoDBExportCheckPolicy = new iam.PolicyStatement({
  sid: "allowCheckExportjob",
  effect: iam.Effect.ALLOW,
  actions: ["dynamodb:DescribeExport"],
  resources: [`${tableArn}/export/*`],
});

openSearchIntegrationPipelineRole.addToPolicy(dynamoDBExportCheckPolicy);&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;4.2.1.3 Stream Access Policy&lt;/h4&gt; 
&lt;p&gt;Provides real-time synchronization by granting permissions to read stream data and manage iterators.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
/**
 * Policy for DynamoDB Stream operations
 * Allows:
 * - Reading stream metadata
 * - Accessing stream records
 * - Managing stream iterators
 * Required for real-time data synchronization
 */
const dynamoDBStreamPolicy = new iam.PolicyStatement({
  sid: "allowReadFromStream",
  effect: iam.Effect.ALLOW,
  actions: [
    // Required for getting stream metadata
    "dynamodb:DescribeStream",
    // Required for reading actual records from the stream
    "dynamodb:GetRecords",
    // Required for managing stream position
    "dynamodb:GetShardIterator",
  ],
  resources: [`${tableArn}/stream/*`],
});

openSearchIntegrationPipelineRole.addToPolicy(dynamoDBStreamPolicy);&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;4.2.2 S3 Policy for Export Operations&lt;/h3&gt; 
&lt;p&gt;Grants permissions for reading and writing data to an S3 bucket during DynamoDB exports.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
/**
 * Policy for S3 operations during export
 * Allows:
 * - Reading exported data
 * - Writing export files
 * - Managing multipart uploads
 * - Setting object ACLs
 * Scoped to specific export path for security
 */
const s3ExportPolicy = new iam.PolicyStatement({
  sid: "allowReadAndWriteToS3ForExport",
  effect: iam.Effect.ALLOW,
  actions: [
    // Required for reading exported data
    "s3:GetObject",
    // Required for handling failed uploads
    "s3:AbortMultipartUpload",
    // Required for writing export files
    "s3:PutObject",
    // Required for setting object permissions
    "s3:PutObjectAcl",
  ],
  resources: [`${s3BucketArn}`,`${s3BucketArn}/${tableName}/*`],
});
openSearchIntegrationPipelineRole.addToPolicy(s3ExportPolicy);&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;4.2.3 OpenSearch Policies&lt;/h3&gt; 
&lt;h4&gt;4.2.3.1 OpenSearch Collection Policy&lt;/h4&gt; 
&lt;p&gt;Enables indexing, querying, and managing OpenSearch collections.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
/**
 * Policy for OpenSearch domain access
 * Allows:
 * - HTTP operations for indexing and querying
 * - Domain management operations
 * Includes permissions for both domain-level and index-level operations
 */
const openSearchCollectionPolicy = new iam.PolicyStatement({
  sid: "allowOpenSearchAccess",
  effect: iam.Effect.ALLOW,
  actions: [
    // Allows batch retrieval of collection information
    "aoss:BatchGetCollection",
    // Required for search, index, and administrative operations
    "aoss:APIAccessAll",
    // Needed for encryption and network policy validation
    "aoss:GetSecurityPolicy",
    // Required for initial setup and scaling operations
    "aoss:CreateCollection",
    // Needed for collection discovery and management
    "aoss:ListCollections",
    // Required for updating collection settings and configurations
    "aoss:UpdateCollection",
    // Needed for cleanup and resource management
    "aoss:DeleteCollection",
  ],
  resources: [
    openSearchServerlessCollection.attrArn,
    `${openSearchServerlessCollection.attrArn}/*`,
    `arn:aws:aoss:${region}:${openSearchStack.account}:collection/*`,
  ],
});
openSearchIntegrationPipelineRole.addToPolicy(openSearchCollectionPolicy);
&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;4.2.3.2 Encryption Policy&lt;/h4&gt; 
&lt;p&gt;Defines encryption settings for OpenSearch Serverless collections.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts

import * as oss from "aws-cdk-lib/aws-opensearchserverless";

/**
 * Create Encryption Policy for OpenSearch Serverless
 * This policy defines how data is encrypted at rest within the collection
 */
const encryptionPolicy = new oss.CfnSecurityPolicy(
  openSearchStack,
  "EncryptionPolicy",
  {
    // Unique identifier for the encryption policy
    name: "ddb-etl-encryption-serverless",
    
    // Policy type - 'encryption' handles data encryption at rest
    type: "encryption",
    
    // Human-readable description for the policy
    description: `Encryption policy for ${collectionName} collection`,
    
    // Policy configuration
    policy: JSON.stringify({
      Rules: [{
        // Specify resource type this encryption applies to
        ResourceType: "collection",
        
        // Define which collections this policy affects
        Resource: [`collection/${collectionName}*`]
      }],
      // Use AWS owned KMS key for encryption
      // Alternative: Set to false and specify 'KmsKeyId' for customer managed key
      AWSOwnedKey: true
      
      /* Example of customer managed key configuration:
      AWSOwnedKey: false,
      KmsKeyId: 'arn:aws:kms:region:account:key/key-id'
      */
    })
  }
);

openSearchServerlessCollection.addDependency(encryptionPolicy);&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;4.2.3.3. Network Policy&lt;/h4&gt; 
&lt;p&gt;Controls network access to the OpenSearch collection.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
/**
 * Create Network Policy for OpenSearch Serverless
 * This policy controls network access to the OpenSearch collection
 */
const networkPolicy = new oss.CfnSecurityPolicy(
  openSearchStack,
  "NetworkPolicy",
  {
    // Unique identifier for the network policy
    name: "ddb-etl-network-serverless",
    
    // Policy type - 'network' controls access patterns
    type: "network",
    
    // Human-readable description for the policy
    description: `Network policy for ${collectionName} collection`,
    
    // Network access rules
    policy: JSON.stringify([{
      Rules: [{
        // Specify resource type this network policy applies to
        ResourceType: "collection",
        
        // Define which collections this policy affects
        Resource: [`collection/${collectionName}`]
      }],
      // Allow public access to the collection
      // WARNING: For production, consider restricting access:
      AllowFromPublic: true

    }])
  }
);
// Add policy dependencies

openSearchServerlessCollection.addDependency(networkPolicy);&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;4.2.3.4 Data Access Policy&lt;/h4&gt; 
&lt;p&gt;Grants permissions for creating, updating, and querying data in OpenSearch collections and indexes.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
/**
 * Creates a data access policy for OpenSearch Serverless
 * This policy defines who can access what within the collection and its indexes
 */
const dataAccessPolicy = new oss.CfnAccessPolicy(
  openSearchStack,
  "DataAccessPolicy",
  {
    name: `ddb-etl-access-policy`,
    type: "data",
    description: `Data access policy for ${collectionName} collection`,
    policy: JSON.stringify([
      {
        /**
         * Collection Level Permissions
         * - CreateCollectionItems: Required for adding new documents
         * - DeleteCollectionItems: Required for removing documents
         * - UpdateCollectionItems: Required for modifying documents
         * - DescribeCollectionItems: Required for reading documents
         */
        Rules: [
          {
            ResourceType: "collection",
            Resource: [`collection/${collectionName}`],
            Permission: [
              "aoss:CreateCollectionItems",
              "aoss:DeleteCollectionItems",
              "aoss:UpdateCollectionItems",
              "aoss:DescribeCollectionItems",
            ],
          },
          /**
           * Index Level Permissions
           * - ReadDocument: Required for search operations
           * - WriteDocument: Required for document updates
           * - CreateIndex: Required for index initialization
           * - DeleteIndex: Required for index cleanup
           * - UpdateIndex: Required for index modifications
           * - DescribeIndex: Required for index metadata
           */
          {
            ResourceType: "index",
            Resource: [`index/${collectionName}/*`],
            Permission: [
              "aoss:ReadDocument",
              "aoss:WriteDocument",
              "aoss:CreateIndex",
              "aoss:DeleteIndex",
              "aoss:UpdateIndex",
              "aoss:DescribeIndex",
            ],
          },
        ],
        Principal: [
          // ETL Pipeline role
          openSearchIntegrationPipelineRole.roleArn,
         
          // AppSync HTTPDataSource role
          httpDataSourceRoleArn,
        ],
      },
    ]),
  }
);&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;4.3 Create OpenSearch Service Pipeline&lt;/h2&gt; 
&lt;p&gt;When using OpenSearch, defining an index template or mapping in advance provides powerful capabilities for precise data ingestion and search. This approach you can:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Set specific data types for each document field&lt;/li&gt; 
 &lt;li&gt;Optimize search performance&lt;/li&gt; 
 &lt;li&gt;Ensure consistent data structure.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The configuration is a data-prepper feature of OpenSearch. For specific documentation on DynamoDB configuration, refer to &lt;a href="https://opensearch.org/docs/latest/data-prepper/pipelines/configuration/sources/dynamo-db/"&gt;OpenSearch data-prepper documentation&lt;/a&gt;.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
// OpenSearch template definition  
interface OpenSearchConfig {
  tableArn: string;
  bucketName: string;
  region: string;
  tableName: string;
  pipelineRoleArn: string;
  openSearchEndpoint: string;
}

function createOpenSearchTemplate(config: OpenSearchConfig): string {
  const template = {
    version: "2",
    "dynamodb-pipeline": {
      source: {
        dynamodb: {
          acknowledgments: true,
          tables: [
            {
              table_arn: config.tableArn,
              stream: { start_position: "LATEST" },
              export: {
                s3_bucket: config.bucketName,
                s3_region: config.region,
                s3_prefix: `${config.tableName}/`,
              },
            },
          ],
          aws: { sts_role_arn: config.pipelineRoleArn, region: config.region },
        },
      },
      sink: [
        {
          opensearch: {
            hosts: [config.openSearchEndpoint],
            index: "movie",
            index_type: "custom",
            document_id: '${getMetadata("primary_key")}',
            action: '${getMetadata("opensearch_action")}',
            document_version: '${getMetadata("document_version")}',
            document_version_type: "external",
            flush_timeout: -1,
            aws: {
              sts_role_arn: config.pipelineRoleArn,
              region: config.region,
              serverless: true,
            },
          },
        },
      ],
    },
  };
  return YAML.stringify(template);
}


const openSearchTemplate = createOpenSearchTemplate({
  tableArn: tableArn,
  bucketName: backend.storage.resources.bucket.bucketName,
  region: region,
  tableName: tableName,
  pipelineRoleArn: openSearchIntegrationPipelineRole.roleArn,
  openSearchEndpoint: openSearchServerlessCollection.attrCollectionEndpoint,
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;&lt;strong&gt;Pipeline Configuration Breakdown&lt;/strong&gt;&lt;/h4&gt; 
&lt;p&gt;&lt;strong&gt;Source Configuration&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Specifies DynamoDB as the data source&lt;/li&gt; 
 &lt;li&gt;Defines target table for ingestion&lt;/li&gt; 
 &lt;li&gt;Sets stream starting point&lt;/li&gt; 
 &lt;li&gt;Configures S3 backup bucket&lt;/li&gt; 
 &lt;li&gt;Assigns IAM role for pipeline permissions&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;Sink Configuration&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Specifies OpenSearch domain cluster&lt;/li&gt; 
 &lt;li&gt;Sets index name and type&lt;/li&gt; 
 &lt;li&gt;Defines index mapping template&lt;/li&gt; 
 &lt;li&gt;Configures document metadata handling&lt;/li&gt; 
 &lt;li&gt;Sets bulk request size&lt;/li&gt; 
 &lt;li&gt;Assigns IAM role for sink operations&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;Advanced Configuration Options&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Sink configuration supports multiple indexes&lt;/li&gt; 
 &lt;li&gt;Multiple tables can be configured through separate pipelines&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;For further guidance, please consult the &lt;a href="https://opensearch.org/docs/latest/data-prepper/pipelines/pipelines/"&gt;pipeline section of the OpenSearch documentation&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;Now, create the OSIS pipeline resource:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/backend.ts
import * as osis from "aws-cdk-lib/aws-osis";
import { LogGroup } from "aws-cdk-lib/aws-logs";

// Create a CloudWatch log group
const logGroup = new LogGroup(openSearchStack, "LogGroup", {
  logGroupName: "/aws/vendedlogs/OpenSearchServerlessService/pipelines/dev",
  removalPolicy: RemovalPolicy.DESTROY,
});


// Create an OpenSearch Integration Service pipeline
const cfnPipeline = new osis.CfnPipeline(
  openSearchStack,
  "OpenSearchIntegrationPipeline",
  {
    maxUnits: 4,
    minUnits: 1,
    pipelineConfigurationBody: openSearchTemplate,
    pipelineName: "dynamodb-integration",
    logPublishingOptions: {
      isLoggingEnabled: true,
      cloudWatchLogDestination: {
        logGroup: logGroup.logGroupName,
      },
    },
  }
);&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;After deploying your resources, you’ll want to confirm that your data pipeline is set up correctly before adding items to your &lt;code&gt;Movie&lt;/code&gt; table. Here’s how to verify your pipeline configuration:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Open the AWS Management Console Navigate to the Amazon OpenSearch Service section.&lt;/li&gt; 
 &lt;li&gt;Check the Pipelines Section Look for the pipeline you just created. Specifically, you’ll want to: 
  &lt;ul&gt; 
   &lt;li&gt;Confirm the pipeline name matches your configuration.&lt;/li&gt; 
   &lt;li&gt;Verify the source and destination settings.&lt;/li&gt; 
   &lt;li&gt;Check the current status of the pipeline.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The image shows a configured pipeline with details including pipeline name, status, source, and destination settings.&lt;/p&gt; 
&lt;div id="attachment_13755" style="width: 761px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13755" loading="lazy" class=" wp-image-13755" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/01/24/blog_figure4-1024x492.png" alt="Figure 4 Ingestion Pipeline configuration" width="751" height="361"&gt;
 &lt;p id="caption-attachment-13755" class="wp-caption-text"&gt;Figure 4: Ingestion Pipeline configuration&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Step 5: OpenSearch Query Implementation&lt;/h2&gt; 
&lt;h3&gt;Create Resolver and attach to query&lt;/h3&gt; 
&lt;p&gt;Let’s create the search resolver. Create a new file named &lt;code&gt;amplify/data/searchMovieResolver.js&lt;/code&gt; and paste the following code. For additional details please refer to &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-http-resolvers-js.html"&gt;Amazon OpenSearch Service Resolvers&lt;/a&gt;.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/data/searchMovieResolver.js
export function request(ctx) {
  return {
    version: "2018-05-29",
    method: "GET",
    params: {
      headers: {
        "Content-Type": "application/json",
      },
      body: {
        from: 0,
        size: 50,
        query: { match: { title: ctx.args.title } },
      },
    },
    resourcePath: `/movie/_search`,
  };
}
/**
 * Returns the fetched items
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */

export function response(ctx) {
  const { statusCode, body } = ctx.result;
  if (statusCode === 200) {
    return JSON.parse(body).hits.hits.map((hit) =&amp;gt; hit._source);
  }
}&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Navigate to the &lt;code&gt;amplify/data/resource.ts&lt;/code&gt; file and modify the schema to include the search query:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;// amplify/data/resource.ts

   searchMovie: a
    .query()
    .arguments({
      content: a.string(),
    })
    .returns(a.ref("Movie").array())
    .authorization((allow) =&amp;gt; [allow.publicApiKey()])
    .handler(
      a.handler.custom({
        entry: "./searchMovieResolver.js",
        dataSource: "OpenSearchServerlessDataSource",
      })
    ),&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Step 6: Deployment and Verification&lt;/h2&gt; 
&lt;p&gt;1. Open your terminal and run the following command to start your personal cloud sandbox:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npx ampx sandbox&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;2. Open a separate terminal and run the following command to start a local development server.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npm run dev&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;The Movie Search Solution interface displays movie search results, presenting a list of titles and descriptions retrieved from OpenSearch.&lt;/p&gt; 
&lt;div id="attachment_13776" style="width: 1034px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13776" loading="lazy" class="wp-image-13776 size-large" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/01/25/figure9-1024x769.png" alt="Figure 5 Web application interface for movie search, displaying a search input field and a list of movie results with titles and descriptions retrieved from OpenSearch" width="1024" height="769"&gt;
 &lt;p id="caption-attachment-13776" class="wp-caption-text"&gt;Figure 5: Interfacing with the Movie Search Solution&lt;/p&gt;
&lt;/div&gt; 
&lt;h2&gt;Clean up&lt;/h2&gt; 
&lt;p&gt;Now that you’ve completed this walkthrough, be sure to delete the backend resources to avoid any unexpected costs. You can remove the app as outlined below and verify that all resources are terminated in the AWS Console.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npx ampx sandbox delete&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;Congratulations! You’ve successfully created a scalable, serverless movie collection search solution using AWS Amplify and Amazon OpenSearch Serverless. You’ve also validated the search results through an Amplify-powered frontend. This approach delivers efficient, cost-effective search capabilities with minimal infrastructure management. Explore further about Amplify by trying our &lt;a href="https://docs.amplify.aws/react/start/quickstart/"&gt;Quickstart tutorial&lt;/a&gt;, and join our &lt;a href="http://www.discord.gg/amplify"&gt;Community Discord&lt;/a&gt; to provide feedback or feature requests.&lt;/p&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/04/11/IMG_0566-1.jpg" alt="" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Anil Maktala&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;Anil Maktala is a Developer Experience Engineer (Solutions Architect) at AWS, bringing extensive experience in developing a diverse range of applications, products, and architectural solutions. He is deeply committed to assisting customers in designing scalable solutions tailored to their unique needs.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Building a Gen AI-Powered Manufacturing Search Engine with AWS Amplify Gen 2</title>
		<link>https://aws.amazon.com/blogs/mobile/building-a-gen-ai-powered-manufacturing-search-engine-with-aws-amplify-gen-2/</link>
		
		<dc:creator><![CDATA[Ben-Amin York Jr.]]></dc:creator>
		<pubDate>Thu, 16 Jan 2025 18:10:45 +0000</pubDate>
				<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Industries]]></category>
		<category><![CDATA[Manufacturing]]></category>
		<category><![CDATA[Amplify]]></category>
		<guid isPermaLink="false">a3102ea917eb340489273e01932d4a36b098e5be</guid>

					<description>AWS Amplify, Amazon Bedrock, and Amazon OpenSearch can be combined to create an AI-powered search engine for manufacturing that breaks down data silos, enabling faster and more accurate access to critical information across defect logs, operational data, and cost analyses, improving decision-making and operational efficiency.</description>
										<content:encoded>&lt;p&gt;In the manufacturing industry, the ability to quickly and accurately access relevant information across various systems is critical. However, traditional search engines often struggle with siloed data, making it difficult to connect the dots between defect logs, operational data, and cost analyses. This fragmentation can hinder decision-making and operational efficiency.&lt;/p&gt; 
&lt;p&gt;By leveraging Artificial Intelligence (AI) and advanced search technologies, manufacturers can break down data silos and centralize access to critical insights. With AI-powered search engines that utilize natural language processing (NLP), manufacturing teams can query defect types, severity, trends, and repair costs in a more intuitive and human-like manner. This approach enables manufacturers to:&lt;/p&gt; 
&lt;ul style="list-style-type: disc; padding-left: 20px;"&gt; 
 &lt;li&gt;Improve response times in identifying and addressing defects.&lt;/li&gt; 
 &lt;li&gt;Optimize maintenance workflows by surfacing relevant insights from dispersed data sources.&lt;/li&gt; 
 &lt;li&gt;Reduce downtime by quickly locating actionable insights to mitigate critical issues.&lt;/li&gt; 
 &lt;li&gt;Streamline quality assurance by uncovering patterns and trends in defect data.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;This blog walks you through building a search engine using &lt;a href="https://aws.amazon.com/amplify/" target="_blank" rel="noopener"&gt;AWS Amplify&lt;/a&gt;, &lt;a href="https://aws.amazon.com/bedrock/?gclid=Cj0KCQjwkdO0BhDxARIsANkNcrfJ2aktVSotZ4_YUwGD4nl-YknaGIqqlPMWeK0TYuJBBYbZRpsT6IQaAgmZEALw_wcB&amp;amp;trk=36201f68-a9b0-45cc-849b-8ab260660e1c&amp;amp;sc_channel=ps&amp;amp;ef_id=Cj0KCQjwkdO0BhDxARIsANkNcrfJ2aktVSotZ4_YUwGD4nl-YknaGIqqlPMWeK0TYuJBBYbZRpsT6IQaAgmZEALw_wcB:G:s&amp;amp;s_kwcid=AL!4422!3!692006004850!e!!g!!amazon%20bedrock!21048268689!159639953975" target="_blank" rel="noopener"&gt;Amazon Bedrock&lt;/a&gt;, &lt;a href="https://aws.amazon.com/opensearch-service/" target="_blank" rel="noopener"&gt;Amazon OpenSearch&lt;/a&gt;, focusing on a manufacturing dataset involving defect tracking across various products. Using this data, you’ll learn how to query defect types, severity, and associated repair costs. AWS Amplify Gen 2, powered by the AWS Cloud Development Kit (CDK), enhances flexibility, allowing you to &lt;a href="https://docs.amplify.aws/react/build-a-backend/add-aws-services/custom-resources/" target="_blank" rel="noopener"&gt;customize your backend resources&lt;/a&gt; and integrate a variety of AWS services to build robust applications. Whether it’s detecting defect trends or optimizing inspection methods, you’ll see how generative AI can transform manufacturing workflows.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/22/defects.gif" alt="Defect Search App defect query. " width="1430" height="1200"&gt;&lt;em&gt;Figure 1 Trends in defects over time or by location query.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;To create your AI-powered search engine, you’ll utilize the following AWS technologies:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;&lt;b&gt;AWS Amplify&lt;/b&gt;: A comprehensive set of tools and services that help developers build, deploy, and manage secure full-stack applications. It integrates with AWS services including Amazon Cognito for user authentication and authorization. AWS Amplify also supports seamless integration with other services like Amazon S3 and AWS AppSync for storage and API management.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;Amazon Bedrock&lt;/b&gt;: An AI service that offers high-performing foundation models (FMs). Amazon Bedrock &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/07/knowledge-bases-amazon-bedrock-additional-data-sources-preview/" target="_blank" rel="noopener"&gt;connects with multiple data sources&lt;/a&gt; to form a knowledge base (KB). For this app, you’ll use Amazon S3 to form the by storing defect data, which is then processed and indexed by Amazon Bedrock, allowing for intelligent retrieval and response.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;Amazon OpenSearch Service&lt;/b&gt;: : A flexible, scalable search engine that enables full-text search and analytics. After Amazon Bedrock retrieves the relevant data from the knowledge base, it sends the vectorized results to OpenSearch Service for optimized querying, enabling efficient and accurate defect detection, analysis, and troubleshooting.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;Amazon Simple Storage (S3)&lt;/b&gt;: A scalable storage solution used to store data, such as defect logs. Amazon S3 acts as the primary storage solution for the manufacturing defect data, feeding information into Amazon Bedrock to create the knowledge base used by the search engine.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;By combining these AWS services, you’ll be able to create a search engine that not only indexes and retrieves data but also understands the intent behind user queries, providing more accurate and contextually relevant results. AI-powered search engines can help manufacturing customers reduce downtime, optimize defect detection, and improve product quality. With AWS Amplify’s scalability, you can build custom, real-time applications tailored to manufacturing workflows, making it easier to trace defects and streamline production processes.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/22/architecture-3.png" alt="Defect Search App defect query. " width="1430" height="1200"&gt;&lt;em&gt;Figure 2 Generative AI Defect Manufacturing Search Engine architecture diagram. &lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Prerequisites&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li style="padding-left: 20px;"&gt;&lt;b&gt;An &lt;a href="https://signin.aws.amazon.com/signup?request_type=register"&gt;AWS account&lt;/a&gt;&lt;/b&gt;: Note that AWS Amplify is part of the &lt;a href="https://aws.amazon.com/amplify/pricing/"&gt;AWS Free Tier&lt;/a&gt;.&lt;/li&gt; 
 &lt;li style="padding-left: 20px;"&gt;&lt;b&gt;Install&lt;/b&gt;: &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt; (v9 or later), and &lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt; (v2.14.1 or later).&lt;/li&gt; 
 &lt;li style="padding-left: 20px;"&gt;&lt;b&gt;A Text Editor&lt;/b&gt;: For this guide we will use VSCode, but you can use your preferred IDE.&lt;/li&gt; 
 &lt;li style="padding-left: 20px;"&gt;&lt;b&gt;Example Dataset&lt;/b&gt;: You’ll be utilizing manufacturing defect data in the provided &lt;a href="https://www.kaggle.com/datasets/fahmidachowdhury/manufacturing-defects"&gt;manufacturing defect data&lt;/a&gt; file from Kaggle. However, feel free to explore other datasets available on the site that best fit your needs.&lt;/li&gt; 
 &lt;li style="padding-left: 20px;"&gt;&lt;b&gt;Amazon Bedrock Model Access&lt;/b&gt;: This guide requires access to Amazon Titan Text Embeddings V2 and the Anthropic Claude 3 Haiku model. Visit &lt;a href="https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/modelaccess"&gt;Amazon Bedrock Model access&lt;/a&gt; to request access to the listed models.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Cloning the Repo&lt;/h2&gt; 
&lt;p&gt;&lt;b&gt;Step 1&lt;/b&gt;: Navigate to the &lt;a href="https://github.com/aws-samples/amplify-genai-manufacturing-search"&gt;repository&lt;/a&gt; on AWS Samples and fork it to your GitHub repositories.&lt;/p&gt; 
&lt;p&gt;&lt;b&gt;Step 2&lt;/b&gt;: Clone the app by running the command below in your terminal.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;git clone https://github.com/&amp;lt;YOUR_GITHUB&amp;gt;/amplify-genai-manufacturing-search.git
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;b&gt;Step 3&lt;/b&gt;: Access the newly cloned repository in VSCode by executing the commands below in your terminal.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;cd amplify-genai-manufacturing-search
code . -r
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;VSCode will open the repository folder, including the Amplify folder, which contains the app code that you’ll review in the next section.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/22/ide.png" alt="Query search engine for price of defects." width="1430" height="1200"&gt;&lt;em&gt;Figure 3 Opening code in VSCode.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;&lt;b&gt;Step 4&lt;/b&gt;: Install the required packages, including the Amplify Gen2 packages, by running the following command:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npm i
&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;The Amplify Backend&lt;/h2&gt; 
&lt;p&gt;In the final app (as seen in the gif at the beginning of the post), users type in their query and click a button to request information relevant to their query from Amazon Bedrock’s knowledge base. The code for this is in the repository you cloned. Here, you’ll go over the key steps for creating your Amplify-developed and hosted search engine app. In the repository, you’ll find an Amplify folder containing a data directory.&lt;/p&gt; 
&lt;h6 style="text-align: center;"&gt;&lt;img class="alignnone size-full wp-image-12994" style="width: 35%; height: auto;" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/22/folder.png" alt="Amplify Gen 2 Project Folder Structure."&gt;&lt;br&gt; &lt;em&gt;Figure 4 Amplify Gen 2 Project Folder Structure.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;In the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;amplify/auth/resource.ts&lt;/code&gt;&lt;/span&gt; file,authentication is configured to require users to log in with their email to access the application and upload files. By enabling email-based login, you ensure only verified users can interact with sensitive data and functionalities.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { defineAuth } from '@aws-amplify/backend';
 
export const auth = defineAuth({
    loginWith: {
        email: true,
    },
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Next, in the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;amplify/data/resource.ts&lt;/code&gt;&lt;/span&gt; file, you’ll find a GraphQL query capable of receiving a list of sensor readings or defect data and linking to Amazon Bedrock to produce a contextually relevant response to the user. The GraphQL API schema consists of two main parts:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;b&gt;generateHaiku Query&lt;/b&gt;: This query takes a string argument called &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;prompt&lt;/code&gt;&lt;/span&gt; and returns the response generated by Claude Haiku as a string.You restrict access to authenticated users using &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;.authorization((allow) =&amp;gt; [allow.authenticated()])&lt;/code&gt;&lt;/span&gt;, ensuring only signed-in users can query the search engine. This improves security by leveraging Amazon Cognito user pools for stronger access control, rather than relying on a public API key. The &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;.handler(a.handler.function(generateHaikuFunction))&lt;/code&gt;&lt;/span&gt; processes the response based on the provided prompt.&lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;API Schema Configuration&lt;/b&gt;: The schema is defined using &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;a.schema&lt;/code&gt;&lt;/span&gt; schema where the generateHaiku query is configured. The API’s default authorization mode is set to use userPools, specified in the authorizationModes configuration.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { type ClientSchema, a, defineData } from "@aws-amplify/backend";
import { generateHaikuFunction } from "../functions/generateFunction/resource";

const schema = a.schema({
  generateHaiku: a.query()
    .arguments({ prompt: a.string().required() })
    .returns(a.string())
    .authorization((allow) =&amp;gt; =&amp;gt; [allow.authenticated()])
    .handler(a.handler.function(generateHaikuFunction)),
});

export type Schema = ClientSchema&amp;lt;typeof schema&amp;gt;;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: "userPool",
  },
});
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;The &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;amplify/functions/generateFunction/resource.ts&lt;/code&gt;&lt;/span&gt; file defines a Lambda function that contains a &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;handler.ts&lt;/code&gt;&lt;/span&gt; function to resolve your custom GraphQL queries.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { defineFunction } from '@aws-amplify/backend';

export const generateHaikuFunction = defineFunction({
    name: 'generate-haiku',
    entry: './handler.ts',
    timeoutSeconds: 60
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;In the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;handler.ts&lt;/code&gt;&lt;/span&gt; function, you use the &lt;b&gt;RetrieveAndGenerateCommand&lt;/b&gt; from the AWS SDK’s &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/bedrock-agent-runtime/command/RetrieveAndGenerateCommand/"&gt;Agent Runtime client&lt;/a&gt; to retrieve relevant data from an Amazon Bedrock knowledge base, structure the user’s input with a predefined template, and generate a response based on the retrieved information. The session is secured with an AWS Key Management Service (&lt;a href="https://aws.amazon.com/kms/"&gt;KMS&lt;/a&gt;) ) key, and the function parses the response to extract the generated data and citations for a structured output.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import type { Schema } from "../../data/resource";
 
import {
    BedrockAgentRuntimeClient,
    RetrieveAndGenerateCommand,
    RetrieveAndGenerateCommandInput,
    PromptTemplate
} from "@aws-sdk/client-bedrock-agent-runtime";
 
const client = new BedrockAgentRuntimeClient();
 
export const handler: Schema["generateHaiku"]["functionHandler"] = async (
    event,
) =&amp;gt; {
    const prompt = event.arguments.prompt;
 
    const promptTemplate: PromptTemplate = {
        textPromptTemplate: `
            Based on the following search results:
            $search_results$
            Respond to the specific query, focusing on the relevant information based on the search results.
            If the query asks about repair costs, format the repair cost as follows: include a $ sign before the amount.
            Example for repair cost:
            Defect ID: 176, Product ID: 13, Defect Type: Structural, Date: 3/9/2024, Location: Surface, Severity: Critical, Inspection Method: Manual Testing, Repair Cost: $952.49
            Respond directly to the query, and only include relevant fields such as defect severity, inspection type, or trends if applicable.
            Respond only if the search results are relevant. Do not respond to queries outside of the search results.
            Do not include introductory phrases like 'Here is a haiku' or 'From haiku'. Respond directly to the query.`,
    };
 
    const input: RetrieveAndGenerateCommandInput = {
        input: {
            text: prompt,
        },
 
        retrieveAndGenerateConfiguration: {
            type: "KNOWLEDGE_BASE",
            knowledgeBaseConfiguration: {
                knowledgeBaseId: process.env.KNOWLEDGE_BASE_ID!,
                modelArn: process.env.MODEL_ARN!,
                retrievalConfiguration: {
                    vectorSearchConfiguration: {
                        numberOfResults: 10
                    },
                },
                generationConfiguration: {
                    promptTemplate,
                    inferenceConfig: {
                        textInferenceConfig: {
                            temperature: 0.7,
                            maxTokens: 150,
                        },
                    },
                },
            },
        },
        sessionConfiguration: {
            kmsKeyArn: process.env.KMS_KEY!,
        },
    };
 
    const command = new RetrieveAndGenerateCommand(input);
 
    const response = await client.send(command);
 
    const generatedText = response.output?.text || "The query does not match any known results.";
 
    const citations = response.citations?.map((citation, index) =&amp;gt; {
        const location = citation.retrievedReferences?.[0]?.location;
        if (location) {
            const s3Uri = location.s3Location?.uri || "";
            return `${index + 1}. ${s3Uri}`;
        } else {
            return `${index + 1}.`;
        }
    }).join("\n") || "No citations available";
 
    return `${generatedText}\n\nCitations:\n${citations}`;
};
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;In the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;amplify/storage/resource.ts&lt;/code&gt;&lt;/span&gt; file, you configure &lt;a href="https://docs.amplify.aws/react/build-a-backend/storage/"&gt;Amplify Storage&lt;/a&gt;, built on Amazon S3, to manage file storage and enable essential operations like uploads. You use the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;defineStorage&lt;/code&gt;&lt;/span&gt; function to instantiate the storage with a user-friendly name for internal reference. Although AWS Amplify generates a unique identifier for the bucket, providing a bucket name helps identify it within your app’s backend configuration. The bucket is set up to allow authenticated users to read and write to the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;public/*&lt;/code&gt;&lt;/span&gt; path, for storing sample search data.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { defineStorage } from "@aws-amplify/backend";
 
export const storage = defineStorage({
    name: "amplify-search-tool-storage",
    access: (allow) =&amp;gt; ({
        'public/*': [
            allow.authenticated.to(['read', 'write'])
        ]
    })
});
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;In the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;amplify/backend.ts&lt;/code&gt;&lt;/span&gt; you import the CDK libraries to configure various aspects of your application. The &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;aws-iam&lt;/code&gt;&lt;/span&gt; library is used to manage permissions, &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;aws-kms&lt;/code&gt;&lt;/span&gt; handles session management, &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;aws-opensearchserverless&lt;/code&gt;&lt;/span&gt; configures Amazon OpenSearch Service, and &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;aws-bedrock&lt;/code&gt;&lt;/span&gt; facilitates response generation. Each library serves a distinct purpose, ensuring your application is properly configured.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import * as kms from 'aws-cdk-lib/aws-kms';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as opensearchserverless from 'aws-cdk-lib/aws-opensearchserverless'

// /* AWS CDK LIBRARIES FOR 2ND DEPLOYMENT */
// import * as bedrock from 'aws-cdk-lib/aws-bedrock';
// import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Next, use the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;backend.createStack()&lt;/code&gt;&lt;/span&gt; method to direct the backend to generate a new CloudFormation Stack to house custom resources. With AWS Amplify Gen 2, you can create custom resources using the CDK, enabling the use of services beyond the Amplify library, with stacks backed by CloudFormation templates for scalability. For example, you could create a Generative AI stack for AI-related services and an authentication stack for managing Cognito resources, ensuring logical organization before adding custom AWS resources. Now, you can begin defining custom AWS resources!&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;const customResourceStack = backend.createStack('MyCustomResources');&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Within the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;customResourceStack&lt;/code&gt;&lt;/span&gt;, define an Amazon OpenSearch Serverless access policy (&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_opensearchserverless.CfnAccessPolicy.html"&gt;CfnAccessPolicy&lt;/a&gt;) that outlines the permissions required to manage both indices and collections in Amazon OpenSearch Serverless. The &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;Principal&lt;/code&gt;&lt;/span&gt; in this policy is set to &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;`arn:aws:iam::${customResourceStack.account}:role/Admin`&lt;/code&gt;&lt;/span&gt; to ensure that only authorized entities with the specified IAM role can perform critical operations like creating, updating, and deleting indices and collections.&lt;/p&gt; 
&lt;p&gt;In addition to the access policy, define a security policy (&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_opensearchserverless.CfnSecurityPolicy.html"&gt;CfnSecurityPolicy&lt;/a&gt;) that specifies encryption settings for the OpenSearch Service collection. This policy ensures that all data stored in the amplify-aoss-collection collection is encrypted using an AWS-owned key, which simplifies key management while ensuring data is protected at rest.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;const accessPolicy = new opensearchserverless.CfnAccessPolicy(customResourceStack, 'OpenSearchAccessPolicy', {
  name: 'collection-policy',
  type: 'data',
  description: 'Access policy for collection',
  policy: JSON.stringify([{
    Description: 'Access for test-user',
    Rules: [
      {
        ResourceType: 'index',
        Resource: ['index/amplify-aoss-collection/*'],
        Permission: [
          'aoss:CreateIndex',
          'aoss:DeleteIndex',
          'aoss:UpdateIndex',
          'aoss:DescribeIndex',
          'aoss:ReadDocument',
          'aoss:WriteDocument'
        ]
      },
      {
        ResourceType: 'collection',
        Resource: ['collection/amplify-aoss-collection'],
        Permission: [
          'aoss:CreateCollectionItems',
          'aoss:DeleteCollectionItems',
          'aoss:UpdateCollectionItems',
          'aoss:DescribeCollectionItems'
        ]
      }
    ],
    /* REPLACE  WITH THE DEFAULT PRINCIPAL */
    Principal: [permissions.roleArn,`arn:aws:iam::${customResourceStack.account}:role/Admin`],
  }])
});
 
/* DEFINE AMAZON OPENSEARCH SECURITY POLICY */
const securityPolicy = new opensearchserverless.CfnSecurityPolicy(customResourceStack, 'OpenSearchSecurityPolicy', {
  description: 'Security policy for my-collection',
  name: 'amplify-security-policy',
  type: 'encryption',
  policy: JSON.stringify({
    Rules: [
      {
        ResourceType: 'collection',
        Resource: ['collection/amplify-aoss-collection'],
      }
    ],
    AWSOwnedKey: true,
  })
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;A collection is a grouping of one or more indexes, representing an analytics workload. This collection will serve as a scalable and efficient storage solution for your search engine data, enabling rapid search operations and ensuring high availability. By specifying the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_opensearchserverless.CfnCollection.html"&gt;CfnCollection&lt;/a&gt; construct, you lay the foundation for data storage and retrieval capabilities.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;const collection = new opensearchserverless.CfnCollection(customResourceStack, 'OpenSearchCollection', {
  name: 'amplify-aoss-collection',
  type: 'VECTORSEARCH',
  description: 'Collection for amplify search tool',
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;When you run the app (as demonstrated in the next section), a file named &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;amplify_outputs.json&lt;/code&gt;&lt;/span&gt; is generated automatically. This file holds your API’s endpoint and Auth metadata details. In the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;src/app/App.tsx&lt;/code&gt;&lt;/span&gt;, you initialize and configure the AWS Amplify client library, then create a data client to facilitate fully-typed API requests to the AWS Amplify backend. The query is made in the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;handleSearch&lt;/code&gt;&lt;/span&gt; function using the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;client.queries.generateHaiku&lt;/code&gt;&lt;/span&gt; method.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';
import { generateClient } from "aws-amplify/api";
import { Schema } from "../amplify/data/resource";
 
Amplify.configure(outputs);
 
const client = generateClient();
 
const App: React.FC = () =&amp;gt; {
  const [messages, setMessages] = useState([]);
 
  const handleSearch = async (query: string) =&amp;gt; {
    if (!query.trim()) {
      setMessages((prevMessages) =&amp;gt; [...prevMessages, { user: 'bot', text: 'Query cannot be empty.' }]);
      return;
    }
 
    try {
      const { data, errors } = await client.queries.generateHaiku({ prompt: query });
 
      if (errors) {
        throw new Error(`Error from Bedrock: ${errors[0].message}`);
      }
 
      setMessages((prevMessages) =&amp;gt; [...prevMessages, { user: 'bot', text: data || "No response from AI" }]);
    } catch (error) {
      let errorMessage = 'Error fetching data.';
      if (error instanceof Error) {
        errorMessage = error.message;
      }
      setMessages((prevMessages) =&amp;gt; [...prevMessages, { user: 'bot', text: `Error fetching data: ${errorMessage}` }]);
    }
  };&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;In &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;src/components/Uploader.tsx&lt;/code&gt;&lt;/span&gt; the &lt;a href="https://docs.amplify.aws/react/build-a-backend/storage/upload-files/#storage-manager-react-ui-component"&gt;Amplify Storage Manager UI component&lt;/a&gt; streamlines file upload operations directly from your user interface to your Amazon S3 bucket. This component simplifies the process, enabling you to integrate robust file storage functionality with minimal setup. Whether you’re handling images, documents, or other file types, the Amplify Storage Manager offers flexible configuration options, such as setting upload limits, handling resumable uploads, and customizing file paths.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import React from 'react';
import { StorageManager } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';
 
const Uploader: React.FC = () =&amp;gt; {
    return (
        &amp;lt;div class="uploader"&amp;gt;
            &amp;lt;StorageManager
                acceptedFileTypes={['.csv']}
                path="public/"
                maxFileCount={10}
                isResumable
            /&amp;gt;
        &amp;lt;/div&amp;gt;
    );
};
 
export default Uploader;
&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Running the App&lt;/h2&gt; 
&lt;p&gt;&lt;b&gt;Step 1&lt;/b&gt;: Amplify provides each developer with a personal cloud sandbox environment, offering isolated development spaces for rapid building, testing, and iteration. To initiate a cloud sandbox environment, open a new terminal window and execute the following command:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npx ampx sandbox&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;b&gt;Step 2&lt;/b&gt;: Execute the command below to start a localhost development server.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npm run dev&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;After running the previous command to start the application, use the ‘&lt;b&gt;Create Account&lt;/b&gt;‘ feature of the &lt;a href="https://ui.docs.amplify.aws/react/connected-components/authenticator/configuration"&gt;Amplify Authenticator component&lt;/a&gt;, to provide an email address and password. After finalizing user setup via a confirmation email, log in to gain access to the application.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/12/08/signin.gif" alt="Query search engine for price of defects." width="1430" height="1200"&gt;&lt;em&gt;Figure 5 Signing into the app, after creating an account.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;After interacting with the app on the development server, stop the sandbox environment by pressing &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;Ctrl + C&lt;/code&gt;&lt;/span&gt; in the terminal. Make sure to enter ‘&lt;b&gt;Y&lt;/b&gt;’ when prompted to delete resources in the sandbox environment.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/22/stopsandbox.png" alt="Stop AWS Amplify Sandbox and Delete Resources" width="1430" height="1200"&gt;&lt;em&gt;Figure 6 Delete cloud sandbox resources.&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Deploying backend resources&lt;/h2&gt; 
&lt;p&gt;With the app functioning as expected, deploy your backend resources by following the steps in ‘&lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/getting-started.html"&gt;Getting Started with Deploying an App to Amplify Hosting&lt;/a&gt;’. Ensure that GitHub is selected as the repository for deployment.&lt;/p&gt; 
&lt;h2&gt;Upload Sample Data&lt;/h2&gt; 
&lt;p&gt;After deploying and signing into the application, upload sample data by accessing the app domain URL generated by Amplify. After uploading files, confirm the upload by checking the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;public/&lt;/code&gt;&lt;/span&gt; folder within the Storage section of the AWS Amplify console.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/25/upload.gif" alt="Uploading defect data to S3 bucket." width="1430" height="1200"&gt;&lt;em&gt;Figure 7 Uploading Defects data file.&lt;/em&gt;&lt;/h6&gt; 
&lt;h1&gt;Creating a Vector Index in Amazon OpenSearch Serverless&lt;/h1&gt; 
&lt;p&gt;Before defining a Bedrock knowledge base, in the &lt;a href="https://us-east-1.console.aws.amazon.com/aos/home?region=us-east-1#opensearch/collections"&gt;Amazon OpenSearch&lt;/a&gt; console, &lt;b&gt;Create a vector index&lt;/b&gt; for the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;amplify-aoss-collection&lt;/code&gt;&lt;/span&gt;. (Figure 5)&lt;/p&gt; 
&lt;p&gt;This index will &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-setup.html"&gt;store and search vector embeddings&lt;/a&gt;, which are numerical representations of text, images, or other data. Define the vector field name (for storing embeddings), vector dimensions (up to 16,000), and the distance metric (e.g., Euclidean, cosine, or dot product) to optimize your OpenSearch Service collection for efficient and accurate search operations.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/25/Create_Vector_Index.png" alt="Creating Amazon OpenSearch vector index." width="1430" height="1200"&gt;&lt;em&gt;Figure 8 Create vector index in OpenSearch Serverless collection.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;From the &lt;strong&gt;Create Vector Index&lt;/strong&gt; page, define the following values:&lt;/p&gt; 
&lt;ul style="list-style-type: disc; padding-left: 20px;"&gt; 
 &lt;li&gt;&lt;strong&gt;Vector index name:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;bedrock-knowledge-base-default-index&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Vector fields, &lt;em&gt;Add vector field&lt;/em&gt;:&lt;/strong&gt; 
  &lt;ul style="list-style-type: disc; padding-left: 20px;"&gt; 
   &lt;li&gt;&lt;strong&gt;Vector field name:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;bedrock-knowledge-base-default-vector&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
   &lt;li&gt;&lt;strong&gt;Engine:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;faiss&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
   &lt;li&gt;&lt;strong&gt;Dimensions:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;1024&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
   &lt;li&gt;&lt;strong&gt;Distance metric:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;Euclidiean&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Metadata management:&lt;/strong&gt; 
  &lt;ul style="list-style-type: disc; padding-left: 20px;"&gt; 
   &lt;li&gt;&lt;strong&gt;Mapping field 1:&lt;/strong&gt; 
    &lt;ul style="list-style-type: disc; padding-left: 20px;"&gt; 
     &lt;li&gt;&lt;strong&gt;Mapping field:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;AMAZON_BEDROCK_TEXT_CHUNK&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
     &lt;li&gt;&lt;strong&gt;Data type:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;String&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
     &lt;li&gt;&lt;strong&gt;Filterable:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;True&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
    &lt;/ul&gt; &lt;/li&gt; 
   &lt;li&gt;&lt;strong&gt;Mapping field 2:&lt;/strong&gt; 
    &lt;ul style="list-style-type: disc; padding-left: 20px;"&gt; 
     &lt;li&gt;&lt;strong&gt;Mapping field:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;AMAZON_BEDROCK_METADATA&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
     &lt;li&gt;&lt;strong&gt;Data type:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;String&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
     &lt;li&gt;&lt;strong&gt;Filterable:&lt;/strong&gt; &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;False&lt;/code&gt;&lt;/span&gt;&lt;/li&gt; 
    &lt;/ul&gt; &lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Lastly, select &lt;strong&gt;Create&lt;/strong&gt; as shown in Figure 9.&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/25/vector-index-fields.png" alt="Creating Amazon OpenSearch vector index." width="1430" height="1200"&gt;&lt;em&gt;Figure 9 Select Create for vector index in OpenSearch Serverless collection.&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Defining an Amazon Bedrock Knowledgebase&lt;/h2&gt; 
&lt;p&gt;Next, in the &lt;span style="color: red; font-weight: bold;"&gt;&lt;code&gt;backend.ts&lt;/code&gt;&lt;/span&gt; file, the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_bedrock.CfnKnowledgeBase.html"&gt;CfnKnowledgeBase&lt;/a&gt; construct creates a vector knowledge base for storing and managing structured and unstructured data. Here you’ll integrate the knowledge base with the OpenSearch Serverless collection to provide advanced vectorization and search capabilities. This knowledge base offers a repository of information that a &lt;a href="https://aws.amazon.com/what-is/foundation-models/"&gt;foundational model&lt;/a&gt; can query to deliver accurate and contextually relevant responses.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;const knowledgeBase = new bedrock.CfnKnowledgeBase(customResourceStack, 'BedrockKB', {
  knowledgeBaseConfiguration: {
    type: 'VECTOR',
    vectorKnowledgeBaseConfiguration: {
      embeddingModelArn: 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v2:0',
    },
  },
  name: 'amplify-search-tool-kb',
  roleArn: permissions.roleArn,
  storageConfiguration: {
    type: 'OPENSEARCH_SERVERLESS',
    opensearchServerlessConfiguration: {
      collectionArn: collection.attrArn,
      vectorIndexName: 'bedrock-knowledge-base-default-index', 
      fieldMapping: {
        vectorField: 'bedrock-knowledge-base-default-vector',
        textField: 'AMAZON_BEDROCK_TEXT_CHUNK', 
        metadataField: 'AMAZON_BEDROCK_METADATA', 
    },
  }
});
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;After creating the knowledge base, define the Amazon S3 bucket created as the data source for the Amazon Bedrock knowledge base. To achieve this, retrieve the Amazon Resource Name (ARN) of the bucket by accessing its properties using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors"&gt;dot notation&lt;/a&gt;. Configuring an Amazon S3 bucket to store sample search data facilitates efficient data ingestion and management. Using the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_bedrock.CfnDataSource.html"&gt;CfnDataSource&lt;/a&gt; construct, you create a link from Amazon S3 to the knowledge base, allowing the knowledge base to access and process uploaded data.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;new bedrock.CfnDataSource(customResourceStack, 'BedrockDataSource', {
  name: 'amplify-search-tool-data-source',
  knowledgeBaseId: knowledgeBase.attrKnowledgeBaseId,
  dataSourceConfiguration: {
    type: 'S3',
    s3Configuration: {
        bucketArn: backend.storage.resources.bucket.bucketArn,
    },
  },
});&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Deploying the App Updates&lt;/h2&gt; 
&lt;p&gt;Finalize updates by adding, committing, and pushing changes to your repository. This action automatically triggers a deployment in AWS Amplify, ensuring the app is up-to-date and live.&lt;/p&gt; 
&lt;h2&gt;Sync S3 Data with Bedrock Knowledge Base&lt;/h2&gt; 
&lt;p&gt;After deploying the app, sync the Amazon S3 bucket with the Amazon Bedrock Knowledge base. From the &lt;a href="https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/" target="_blank" rel="noopener"&gt;Amazon Bedrock&lt;/a&gt; console, navigate to &lt;b&gt;Knowledge bases&lt;/b&gt;, select &lt;b&gt;amplify-search-tool-kb&lt;/b&gt;. Then, scroll to &lt;b&gt;Data source&lt;/b&gt; and select &lt;b&gt;Sync&lt;/b&gt; (Figure 10).&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/25/Sync_Data_Source.png" alt="Creating Amazon OpenSearch vector index." width="1430" height="1200"&gt;&lt;em&gt;Figure 10 Syncing Amazon S3 bucket data with Amazon Bedrock knowledge base.&lt;/em&gt;&lt;/h6&gt; 
&lt;p&gt;Once the data source syncs successfully and the status shows “&lt;b&gt;Available&lt;/b&gt;“, start querying the search engine directly through the application!&lt;/p&gt; 
&lt;h6&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-12994" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/22/price.gif" alt="Query search engine for price of defects." width="1430" height="1200"&gt;&lt;em&gt;Figure 11 Interfacing with Defect Search app.&lt;/em&gt;&lt;/h6&gt; 
&lt;h2&gt;Cleaning Up&lt;/h2&gt; 
&lt;p&gt;Navigate to the &lt;a href="https://console.aws.amazon.com/amplify/home#/home" target="_blank" rel="noopener"&gt;AWS Amplify console&lt;/a&gt;, click on “&lt;b&gt;View app&lt;/b&gt;” for the application created during this blog. Then, go to “&lt;b&gt;App Settings&lt;/b&gt;” followed by “&lt;b&gt;General Settings&lt;/b&gt;.” Finally, select “&lt;b&gt;Delete app&lt;/b&gt;” to remove the application and associated backend resources. Note, AWS Amplify will &lt;i&gt;delete all backend resources&lt;/i&gt; created a part of your project.&lt;/p&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;Now that you’ve learned how to configure and deploy a generative AI-powered search engine using AWS Amplify Gen 2, Amazon Bedrock, Amazon OpenSearch Service, and other AWS services, it’s time to take the next step. Start building your own custom applications with the &lt;a href="https://catalog.workshops.aws/amplify-core/en-US" target="_blank" rel="noopener"&gt;Amplify Gen 2 Workshop&lt;/a&gt;, and consider leveraging a generative AI-powered coding assistant, &lt;a href="https://aws.amazon.com/q/developer/" target="_blank" rel="noopener"&gt;Amazon Q Developer&lt;/a&gt;, for your development efforts.&lt;/p&gt; 
&lt;p&gt;Looking for a quick way to start building AI-powered applications with AWS Amplify? Check out the &lt;a href="https://aws.amazon.com/amplify/ai/" target="_blank" rel="noopener"&gt;Amplify AI Kit&lt;/a&gt; to bring your ideas to life faster than ever. By utilizing these advanced AI services, you can create and deploy sophisticated search capabilities that deliver enhanced user experiences.&lt;/p&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/19/ybenamin.jpg" alt="Photo of author" width="101" height="134"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ben-Amin York Jr.&lt;/h3&gt; 
  &lt;p style="text-align: left;"&gt;Ben-Amin, an AWS Solutions Architect specializing in Frontend Web &amp;amp; Mobile technologies, supports Automotive and Manufacturing enterprises drive digital transformation.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/10/19/jordmle.jpg" alt="Photo of author" width="101" height="134"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Miles Jordan&lt;/h3&gt; 
  &lt;p style="text-align: left;"&gt;Miles is a Solutions Architect at AWS, specializing in Analytic and Search technologies. He focuses on utilizing data effectively and provides technical guidance to enterprise customers across all sectors to achieve their business goals.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>AWS AppSync field-level resolvers: Enhancing GraphQL API development</title>
		<link>https://aws.amazon.com/blogs/mobile/aws-appsync-field-level-resolvers-enhancing-graphql-api-development/</link>
		
		<dc:creator><![CDATA[Kim Wendt]]></dc:creator>
		<pubDate>Tue, 14 Jan 2025 14:52:48 +0000</pubDate>
				<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<category><![CDATA[AWS Mobile]]></category>
		<category><![CDATA[aws-appsync]]></category>
		<category><![CDATA[graphql]]></category>
		<guid isPermaLink="false">bfc220043759d15601f0af9e81cad6e3bc6bab32</guid>

					<description>Introduction In this post, we’ll explore AWS AppSync field-level resolvers and how they can enhance your GraphQL API development. Field-level resolvers are powerful units of code that determine how data is fetched, processed, and returned for specific fields in your schema types. By leveraging field-level resolvers in AWS AppSync, you’ll learn how to efficiently handle […]</description>
										<content:encoded>&lt;h1&gt;Introduction&lt;/h1&gt; 
&lt;p&gt;In this post, we’ll explore &lt;a href="https://aws.amazon.com/appsync/"&gt;AWS AppSync&lt;/a&gt; field-level resolvers and how they can enhance your GraphQL API development. Field-level resolvers are powerful units of code that determine how data is fetched, processed, and returned for specific fields in your schema types. By leveraging field-level resolvers in AWS AppSync, you’ll learn how to efficiently handle complex data operations, integrate multiple data sources, and create more flexible and performant APIs. We’ll cover the basics of field resolvers and how they can help you build scalable and responsive applications. Whether you’re new to GraphQL or looking to optimize your existing AWS AppSync implementations, this post will provide you with practical insights to make the most of field-level resolvers in your projects.&lt;/p&gt; 
&lt;h2&gt;Resolver patterns with AWS AppSync&lt;/h2&gt; 
&lt;p&gt;AWS AppSync offers various resolver strategies to accommodate different data access patterns and complexity levels. For straightforward operations involving a single data source, unit resolvers are the go-to solution. These resolvers handle simple queries or mutations, mapping directly to a specific data source like &lt;a href="https://aws.amazon.com/dynamodb/"&gt;Amazon DynamoDB&lt;/a&gt; or &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt;. As applications grow more complex, pipeline resolvers become invaluable for orchestrating multiple data operations. These allow developers to chain together a series of functions, each performing a specific task such as data validation, transformation, or aggregation from multiple sources. This modular approach enhances code reusability and maintainability.&lt;/p&gt; 
&lt;p&gt;Field-level resolvers in GraphQL provide fine-grained control over individual fields within a type, allowing customized data fetching or computation for each field independently. They are particularly useful when different fields in the same type need to be resolved from various data sources or require unique processing. The choice between field-level resolvers and pipeline resolvers depends on specific application requirements, with field-level resolvers offering granular control and pipeline resolvers providing a structured approach for complex, multi-step operations.&lt;/p&gt; 
&lt;p&gt;Field-level resolvers provide a different method to reuse query logic in your code. In a pipeline resolver, you can reuse code through AWS AppSync functions, while field-level resolvers allow a field associated with a type to resolve using the same logic from multiple query patterns.&lt;/p&gt; 
&lt;h2&gt;Restaurant scenario&lt;/h2&gt; 
&lt;p&gt;To better understand field-level resolver implementation, we’ll discuss a practical scenario. This scenario explores a common use case in the restaurant industry where a query operation retrieves comprehensive information about a restaurant, including its menu items.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="lang-gql"&gt;type MenuItem {
    item: String!
    price: Float!
    description: String
}

type Restaurant {
    id: ID!
    name: String!
    address: String
    menu_id: ID!
    menu: [MenuItem!]!
}

type Query {
    getRestaurantDetails(id: ID!): Restaurant
}&lt;/code&gt;&lt;/pre&gt; 
 &lt;p&gt;The challenge lies in the data architecture: while the general restaurant information is stored in one data source, the menu details are housed in a separate data source. To resolve this, we implement a field-level resolver for the menu field within the restaurant type. This resolver utilizes the &lt;code&gt;menu_id&lt;/code&gt; from the parent restaurant object to fetch the corresponding menu items from the separate data source. The query operation demonstrated here showcases how clients can request restaurant details and associated menu items in a single, efficient GraphQL query, highlighting the power of AWS AppSync’s resolver system in handling complex data relationships.&lt;/p&gt; 
&lt;/div&gt; 
&lt;h2&gt;Creating the resolvers&lt;/h2&gt; 
&lt;p&gt;For this example, we will use an Amazon DynamoDB table to store information about the restaurants and an &lt;a href="https://aws.amazon.com/rds/aurora/"&gt;Amazon Aurora PostgreSQL&lt;/a&gt; database for storing menu information.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-sql"&gt;// Restaurant DynamoDB table design
Primary key: id (String)
Attributes:
- name (String)
- address (String)
- menu_id (String)

// Menu table schema
CREATE TABLE menu (
  id VARCHAR(36),
  menu_id VARCHAR(36),
  item TEXT NOT NULL,
  description TEXT NOT NULL,
  price NUMERIC(10,2) NOT NULL,
  PRIMARY KEY (id)
);&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;Creating the &lt;code&gt;getRestaurantDetails&lt;/code&gt; field resolver&lt;/h3&gt; 
&lt;p&gt;To retrieve the restaurant details for a single restaurant, we can attach the following unit resolver to the &lt;code&gt;getRestaurantDetails&lt;/code&gt; query operation. In the resolver below, we are able to simplify the interaction to the DynamoDB table using the &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/built-in-modules-js.html"&gt;AWS AppSync DynamoDB module&lt;/a&gt;.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-js"&gt;import { util } from '@aws-appsync/utils';
import { get } from '@aws-appsync/utils/dynamodb';

export function request(ctx) {    
    return get({ key: { id: ctx.args.id } });
}

export function response(ctx) {
    if (ctx.error) {
        util.error(ctx.error.message, ctx.error.type);
    }
    return ctx.result;
}&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;Resolving the &lt;code&gt;menu&lt;/code&gt; field&lt;/h3&gt; 
&lt;p&gt;The previous resolver allows us to retrieve the &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, and &lt;code&gt;menu_id&lt;/code&gt; for a specific restaurant. Now we need a way to retrieve the menu items as part of the same query operation. The unit resolver below is attached to the &lt;code&gt;menu&lt;/code&gt; field in the Restaurant type so that whenever the &lt;code&gt;Restaurant&lt;/code&gt; type is returned using a query operation, this resolver code executes to resolve the &lt;code&gt;menu&lt;/code&gt; field. Similar to the resolver above, we are able to simplify the interaction to the PostgreSQL database using the &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/built-in-modules-js.html#built-in-rds-modules"&gt;AWS AppSync RDS module&lt;/a&gt;.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-js"&gt;import { util } from "@aws-appsync/utils";
import { select, sql, createPgStatement, toJsonObject, typeHint } from "@aws-appsync/utils/rds";

export function request(ctx) {
    const menuId = ctx.source.menu_id;

    const fetchMenu = select({
        table: "menus",
        columns: "*",
        where: {
            menu_id: { eq: menuId },
        }
    });
    return createPgStatement(fetchMenu);
}


export function response(ctx) {
    const { error, result } = ctx;
    if (error) {
        return util.appendError(error.message, error.type, result);
    }
    return toJsonObject(result)[0];
}&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Notice that on line 4 in the resolver above, we’re able to reference the &lt;code&gt;menu_id&lt;/code&gt; which was resolved as part of the parent &lt;code&gt;Restaurant&lt;/code&gt; type. In the resolver, you’re able to access any previously resolved fields from the parent type using the &lt;code&gt;ctx.source&lt;/code&gt; object. For more information on the structure of the &lt;code&gt;ctx&lt;/code&gt; object, please refer to the &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt; 
&lt;h3&gt;Setting up data&lt;/h3&gt; 
&lt;p&gt;Before executing the query, let’s seed the databases with some sample data. For the PostgreSQL database, execute the following statement:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-sql"&gt; INSERT INTO 
  menus (id, menu_id, item, description, price) 
 VALUES ('39a4e192-92ce-4665-b874-7ffe7ba2d757', '9ce86066-1a49-4f41-b817-8fa605f680d5', 'Strawberry Milkshake', 'Made with 100% artificial ingredients', 10.21);
  INSERT INTO 
  menus (id, menu_id, item, description, price) 
 VALUES ('0f75668f-3014-4c42-bf82-3da8543b59ad', '9ce86066-1a49-4f41-b817-8fa605f680d5', 'Apple Pie', 'Contains zero apples', 7.11);&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;For the DynamoDB table, insert the following item:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-json"&gt;{
  "id": {
    "S": "0cbf6478-d2c3-4549-a2d6-b4b8a3ddb582"
  },
  "address": {
    "S": "1234 Main Avenue"
  },
  "menu_id": {
    "S": "9ce86066-1a49-4f41-b817-8fa605f680d5"
  },
  "name": {
    "S": "Sam's Diner"
  }
}&lt;/code&gt;&lt;/pre&gt; 
&lt;h3&gt;Example query&lt;/h3&gt; 
&lt;p&gt;Below is an example query that illustrates the field resolution we described above. To resolve the requested data for this query operation, AWS AppSync will first resolve the &lt;code&gt;Restaurant&lt;/code&gt; type and then subsequently will resolve the &lt;code&gt;menu&lt;/code&gt; field in the parent &lt;code&gt;Restaurant&lt;/code&gt; type. This operation results in 1 network call from the client while the backed resolution complexity is completely abstracted away from the client.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-gql"&gt;query MyQuery {
  getRestaurantDetails(id: '0cbf6478-d2c3-4549-a2d6-b4b8a3ddb582') {
    name
    id
    menu_id
    address
    menu {
      item
      price
      description
    }
  }
}&lt;/code&gt;&lt;/pre&gt; 
&lt;div id="attachment_13729" style="width: 1290px" class="wp-caption alignnone"&gt;
 &lt;img aria-describedby="caption-attachment-13729" loading="lazy" class="wp-image-13729 size-full" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/01/09/Screenshot-2024-11-27-at-2.52.42-PM.png" alt="AWS AppSync query console interface showing a sample query and response" width="1280" height="531"&gt;
 &lt;p id="caption-attachment-13729" class="wp-caption-text"&gt;AWS AppSync query editor&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;When dealing with relatively static information, such as menu items that don’t change often, leveraging caching can significantly boost your query operations’ performance. AWS AppSync offers two caching modes: per-resolver caching and full request caching. For expensive overall requests with many resolvers, the full-operation cache minimizes load and increasing performance across the entire request, while per-resolver caching minimizes load on a resolver, even across multiple request types. By enabling this feature, you can dramatically reduce response times and minimize unnecessary data fetches, resulting in a more responsive and efficient application. To implement caching in AWS AppSync, simply activate the “Per-resolver caching” or “Full request caching” option in the caching section of the AWS AppSync service menu. Per resolver caching allows fine-tune your caching strategy by configuring the caching keys and TTL for your cached data&lt;/p&gt; 
&lt;h1&gt;Conclusion&lt;/h1&gt; 
&lt;p&gt;In conclusion, AWS AppSync field-level resolvers offer a powerful and flexible way to enhance your GraphQL API development. By leveraging field resolvers, you can efficiently handle complex data operations, integrate multiple data sources, and create more responsive and scalable applications. Whether you’re dealing with straightforward queries or more intricate data relationships, field-level resolvers provide the granular control needed to resolve individual fields independently. This approach not only optimizes performance but also promotes code reusability and maintainability. As your API evolves, you can split the implementation into different APIs that can be merged into an AWS AppSync Merged API, further enhancing modularity and scalability. As demonstrated in the restaurant scenario, field resolvers can seamlessly fetch data from different sources and aggregate it into a single, efficient GraphQL query. By understanding and implementing field-level resolvers, you can unlock the full potential of AWS AppSync to build more dynamic and robust APIs.&lt;/p&gt; 
&lt;p&gt;Amazon Aurora MySQL-Compatible Edition &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/09/amazon-aurora-mysql-rds-data-api/"&gt;now supports a redesigned RDS Data API&lt;/a&gt; for Aurora Serverless v2 and Aurora provisioned database instances. Data API enables access to Aurora databases via AWS AppSync GraphQL APIs through direct data sources which means you can use this same blog with a MySQL Aurora database with some minor changes to the resolver code.&lt;/p&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/f1f836cb4ea6efb2a0b1b99f41ad8b103eff4b59/2022/06/09/f64791af189fc2565f9c0ef2537ed67b.jpg" alt="Kim Wendt profile picture" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Kim Wendt&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;Kim Wendt is a Senior Solutions Architect at Amazon Web Services (AWS), responsible for helping global Media &amp;amp; Entertainment companies on their journey to the cloud. Prior to AWS, she was a Software Developer and uses her development skills to build solutions for customers. She has a passion for continuous learning and holds a Master’s degree from Georgia Tech in Computer Science with a Machine Learning specialization. In her free time, she enjoys reading and exploring the PNW with her husband and dog.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="wp-image-11636 alignleft" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2025/01/09/profile.jpg" alt="David Gibb profile picture" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;David Gibb&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;David Gibb is a Senior Prototype Solutions Developer at Amazon Web Services (AWS), specializing in cutting-edge technologies like Generative AI, robotics, and IoT. With a strong focus on addressing complex business challenges, he works closely with customers to create impactful, transformative solutions. Driven by curiosity and a passion for innovation, David is dedicated to helping businesses unlock new opportunities and achieve lasting success.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>The most visited Front-end Web and Mobile blog posts in 2024</title>
		<link>https://aws.amazon.com/blogs/mobile/the-most-visited-front-end-web-and-mobile-blog-posts-in-2024/</link>
		
		<dc:creator><![CDATA[Brian Beach]]></dc:creator>
		<pubDate>Thu, 02 Jan 2025 14:23:09 +0000</pubDate>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[AWS Device Farm]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Amplify]]></category>
		<category><![CDATA[AppSync]]></category>
		<guid isPermaLink="false">ac64697d710c859f1c8ed7b7b35643233ae363f7</guid>

					<description>As we kick off 2025, I wanted to take a moment to highlight the top posts from 2024. Without further ado, here are the 10 front-end web and mobile blog posts of 2024. Fullstack TypeScript Reintroducing AWS Amplify – This blog post announces the general availability of AWS Amplify Gen 2, a fullstack TypeScript experience […]</description>
										<content:encoded>&lt;p&gt;As we kick off 2025, I wanted to take a moment to highlight the top posts from 2024. Without further ado, here are the 10 front-end web and mobile blog posts of 2024.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/amplify-gen2-ga/"&gt;Fullstack TypeScript Reintroducing AWS Amplify&lt;/a&gt; – This blog post announces the general availability of AWS Amplify Gen 2, a fullstack TypeScript experience for building cloud-connected apps. It highlights key features including zero-config authentication, type-safe cloud data integration, and PubSub APIs for real-time multiplayer use cases. The post walks through building a real-time multiplayer application, demonstrating how to deploy a frontend, set up authentication, create a cloud API, and implement real-time cursor sharing between users. It emphasizes Amplify’s integration with AWS services, improved developer experience with TypeScript, and the ability to create isolated development environments. The post also addresses migration concerns for existing Gen 1 users and positions Amplify within the broader fullstack TypeScript ecosystem.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/announcing-aws-appsync-events-serverless-websocket-apis/"&gt;Announcing AWS AppSync Events serverless WebSocket APIs to power real time web and mobile experiences at any scale&lt;/a&gt; – This blog post announces AWS AppSync Events, a new feature that enables developers to create serverless WebSocket APIs for real-time event broadcasting at scale. The service simplifies the process of building and managing WebSocket infrastructure, allowing developers to easily publish events to millions of subscribers. Key features include easy setup, serverless architecture, support for event-driven architectures, and integration with existing AWS services like Amazon EventBridge. The post explains how to create an API, define channel namespaces, and use event handlers for custom logic. It also demonstrates how to integrate AWS AppSync Events with web applications and highlights future plans for the service, including bi-directional WebSockets and additional data source support.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/build-fullstack-ai-apps-in-minutes-with-the-new-amplify-ai-kit/"&gt;Build fullstack AI apps in minutes with the new Amplify AI Kit&lt;/a&gt; – This blog post announces the general availability of the AWS Amplify AI kit, a tool designed to help fullstack developers quickly build web apps with AI capabilities such as chat, conversational search, and summarization. The kit allows developers to create AI functionality without extensive cloud architecture or machine learning experience. It uses TypeScript for backend definition and offers two types of AI routes: conversation and generation. The kit integrates seamlessly with Amplify’s existing features, providing type-safe clients and React hooks for easy implementation. It also allows for connecting AI functionality to existing data models and includes features for generative UI. The post emphasizes the serverless nature of the architecture and the ease of deployment.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/amazon-linux-2023-hosting/"&gt;Deploy Next js 14 SSR apps with AWS Amplify Hosting s Amazon Linux 2023 Support&lt;/a&gt; – This blog post announces that AWS Amplify Hosting now defaults to Amazon Linux 2023 for newly deployed applications. This update enables the use of newer versions of Node.js, Ruby, and Python. The post highlights that Node.js 18 and 20, as well as Python 3.10 and 3.11, are pre-installed in the default build image. It provides a step-by-step guide on how to upgrade existing applications from Amazon Linux 2 to Amazon Linux 2023. The update particularly benefits developers using Next.js 14, allowing easier deployment without custom configuration. The post concludes by encouraging users to deploy their SSR or static apps using Amplify Hosting and join the Community Discord for feedback.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/use-generative-ai-and-next-js-with-aws-amplify-to-build-a-fullstack-recipe-generator/"&gt;Use Generative AI and Next js with AWS Amplify to build a Fullstack Recipe Generator&lt;/a&gt; – This blog post guides readers through creating an AI-powered recipe generator using AWS Amplify Gen 2, Next.js, and Amazon Bedrock with Claude 3. The app allows users to input ingredients and receive AI-generated recipes. The tutorial covers prerequisites, setting up Amazon Bedrock access, cloning the repository, and implementing the backend using GraphQL and custom handlers. It explains how to connect Amplify with Amazon Bedrock, handle user inputs, and generate recipes. The post also walks through running the app locally and deploying it using Amplify Hosting. It concludes by showing how to clean up resources and encourages readers to explore Amplify Gen 2 further.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/amplify-functions-create-serverless-functions-using-typescript-powered-by-aws-lambda/"&gt;Amplify Functions Create serverless functions using TypeScript powered by AWS Lambda&lt;/a&gt; – AWS Amplify has launched Functions for Gen 2, enabling developers to create serverless functions using TypeScript, powered by AWS Lambda. This feature allows for seamless integration with other Amplify resources and custom AWS services. Functions can be defined and authored in TypeScript, with automatic bundling via esbuild. Key benefits include quick iteration through hot-swapping, typed environment variables and handlers, simplified secret management, and easy extension to other AWS services via CDK. The post guides developers through defining functions, configuring access to resources, handling custom queries, and interacting with services like Amazon Bedrock. This release streamlines serverless development within the Amplify ecosystem, enhancing productivity and flexibility for front-end developers.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/new-in-aws-amplify-integrate-with-sql-databases-oidc-saml-providers-and-the-aws-cdk/"&gt;New in AWS Amplify Integrate with SQL databases OIDC SAML providers and the AWS CDK&lt;/a&gt; – This blog post announces four new capabilities in AWS Amplify Gen 2, focusing on extensibility. These include integration with existing SQL databases (PostgreSQL or MySQL), authentication with any OpenID Connect or SAML provider, customization of Amplify-generated AWS service resources, and the ability to add any of the 200+ AWS services to an app. The post provides examples of integrating with a PostgreSQL database, setting up OIDC authentication, customizing S3 bucket lifecycle rules, and adding Amazon Bedrock for AI capabilities. These features aim to give developers more flexibility and power in building and customizing their applications while leveraging AWS services.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/custom-ssl-amplify-hosting/"&gt;Bring your own SSL certificate to AWS Amplify Hosting&lt;/a&gt; – This blog post announces the general availability of Custom SSL Certificates on AWS Amplify Hosting, allowing users to configure their Amplify domains with their own SSL certificates from AWS Certificate Manager (ACM). The feature enables users to use certificates from third-party authorities, configure TLS versions and encryption algorithms, and share certificates across multiple domains. The post provides a step-by-step walkthrough on how to request or import an ACM certificate and associate it with an Amplify domain. It covers the process of provisioning a certificate in the US East (N. Virginia) Region, either by requesting a new one or importing an existing one, and then using it with an Amplify domain.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/building-a-secure-graphql-api-with-aws-amplify-and-aws-appsync/"&gt;Building a Secure GraphQL API with AWS Amplify and AWS AppSync&lt;/a&gt; – This blog post describes how to build a secure GraphQL API using AWS Amplify and AWS AppSync, addressing Cross-Origin Resource Sharing (CORS) challenges in client-side web development. It outlines a high-level architecture integrating Amazon CloudFront with AWS AppSync to enforce domain-specific access to GraphQL APIs. The post provides step-by-step instructions for setting up a new AWS CDK project, configuring Amplify and AppSync, and implementing CloudFront distribution settings. It also covers updating the Amplify app configuration and testing the implementation. The solution aims to simplify CORS configuration, enhance security, and improve user experience for developers building robust web applications.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/best-practices-for-aws-appsync-graphql-apis/"&gt;Best practices for AWS AppSync GraphQL APIs&lt;/a&gt; – This blog post discusses best practices for building GraphQL APIs using AWS AppSync. It covers four main areas: security, performance, coding standards, and deployment. Key recommendations include choosing appropriate authentication methods, implementing caching strategies, using pipeline resolvers and JavaScript for coding, and adopting Infrastructure as Code for deployment. The post emphasizes the benefits of AWS AppSync as a fully managed service that integrates well with other AWS services. It also highlights the importance of using AWS Amplify for simplified deployment and CI/CD pipelines. Overall, the article provides a comprehensive guide for developers to optimize their AWS AppSync GraphQL APIs.&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>re:Invent 2024 Front-end Web and Mobile Playlist</title>
		<link>https://aws.amazon.com/blogs/mobile/reinvent-2024-front-end-web-and-mobile-playlist/</link>
		
		<dc:creator><![CDATA[Brian Beach]]></dc:creator>
		<pubDate>Thu, 02 Jan 2025 14:22:42 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">9779ff512114d0317850e26b5345d3a89ed07560</guid>

					<description>The dust has settled after another re:Invent. I once again had the privilege of organizing the Front-end Web and Mobile (FWM) track along with Ali Spittel and countless others. For 2024, the FWM track included 29 sessions. If you weren’t able to attend, I have compiled a list of the on-demand sessions for you below. […]</description>
										<content:encoded>&lt;p&gt;The dust has settled after another re:Invent. I once again had the privilege of organizing the Front-end Web and Mobile (FWM) track along with Ali Spittel and countless others. For 2024, the FWM track included 29 sessions. If you weren’t able to attend, I have compiled a list of the on-demand sessions for you below.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=IP2-saNpYsQ"&gt;FWM202 Fullstack AI with AWS Amplify&lt;/a&gt; Generative AI is transforming fullstack development. In this session, learn how AWS Amplify, Amazon Q, and Amazon Bedrock work together to streamline building generative AI apps. See how Amplify enables developers to build AI-driven features like intelligent search, summarization, content generation, and interactive chatbots backed by Amazon Bedrock. The session also demonstrates how Amplify with Amazon Q can accelerate your development process.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=eBbfuwb_bzY"&gt;FWM203 I didn’t know AWS AppSync could do that!&lt;/a&gt;AWS AppSync is a managed service for developers to connect their apps to cloud data, events, and AI models. No longer just a GraphQL API service, with AWS AppSync you can leverage serverless WebSockets to create real-time event APIs, build AI gateways to simplify and secure application access to Amazon Bedrock, and more. Join this session to learn about all the exciting new AWS AppSync features released this year.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=oOBKnkE8w0Y"&gt;FWM204 Optimizing the world’s top apps: How Meta tests using AWS Device Farm&lt;/a&gt; In this session, learn how you can improve the quality of your mobile and web apps by running tests on real devices at scale with AWS Device Farm. Hear from Meta, a leading social media company, about how it uses Device Farm to streamline its mobile app testing process, improve the quality of its mobile apps and SDKs, run automated and manual tests on real devices in the cloud, identify and fix issues faster, and release updates more frequently.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=eeNDCzL7O7A"&gt;FWM205 Startup to enterprise: Build frontend and fullstack apps that scale&lt;/a&gt; AWS Amplify offers professional development teams an end-to-end experience and proven patterns to accelerate productivity and build with the speed and reliability of AWS infrastructure. Learn how to use these built-in patterns—along with Amplify’s flexibility to customize your AWS backend with CDK—to build apps that your organization can’t outgrow. This session provides a walkthrough of real-world scenarios and introduces new enterprise-focused capabilities in Amplify that make it easier than ever to build, deploy, and host secure, scalable, cloud-connected applications on AWS.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=mc27pPLDFAw"&gt;FWM302 Real-time event patterns with WebSockets and AWS AppSync&lt;/a&gt; AWS AppSync makes it easier for developers to build applications that consume real-time data updates and events, like live sports scores and stats, group chat messages, prices, or location and schedule updates. With AWS AppSync managed WebSocket channels, you can easily scale to connect millions of users and deliver billions of messages. In this session, learn how organizations like PGA Tour are using AWS AppSync to deliver real-time event updates to their application users. Additionally, get an overview of new features like enhanced filtering options and native integration with Amazon EventBridge.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=iW7OWwct-Ww"&gt;FWM310 Build an AI gateway for Amazon Bedrock with AWS AppSync&lt;/a&gt; Integrating generative AI into applications can be complex, often requiring developers to deal with complex permissions, exposed credentials, and identity management challenges when accessing AI backends. This session discusses how to use AWS AppSync to simplify integration with AI backends like Amazon Bedrock, implement tailored queries for your use cases, take advantage of security features to deploy a production-ready API, and control access to your generative AI resources. It covers both synchronous and asynchronous use cases.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=vHY2fcC0d6M"&gt;FWM311 What’s new in AWS for frontend developers&lt;/a&gt; AWS Amplify makes it easy for frontend web and mobile developers to build full-stack applications in hours with minimal cloud expertise. This session walks through Amplify’s capabilities, including how you can easily configure a backend with authentication, data, and storage; how you can create a frontend UI; and how you can host server-side rendered web apps with Next.js. Learn about exciting new features that make it even easier for developers and teams to accelerate the pace of their innovation, leverage data to build differentiated application experiences, and give individual engineers the power of a virtual team.&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>AWS Amplify Hosting Adds Web Application Firewall Protection – Public Preview</title>
		<link>https://aws.amazon.com/blogs/mobile/aws-amplify-hosting-adds-web-application-firewall-protection-public-preview/</link>
		
		<dc:creator><![CDATA[Nikhil Swaminathan]]></dc:creator>
		<pubDate>Tue, 24 Dec 2024 17:25:45 +0000</pubDate>
				<category><![CDATA[Amazon CloudFront]]></category>
		<category><![CDATA[AWS WAF]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[AWS Amplify Hosting]]></category>
		<category><![CDATA[Hosting]]></category>
		<guid isPermaLink="false">ff2d3ec046564d1e5a3a6c179b4436ae339268ac</guid>

					<description>Today, AWS Amplify Hosting is launching new Firewall capabilities that will allow developers to protect and further secure their web applications. This is a direct integration with AWS WAF, allowing Amplify developers to connect a Web ACL directly to their Amplify hosted application. A web firewall is essential for professional developers to protect their applications […]</description>
										<content:encoded>&lt;p&gt;Today, &lt;a href="https://aws.amazon.com/amplify/hosting/" target="_blank" rel="noopener"&gt;AWS Amplify Hosting&lt;/a&gt; is launching new Firewall capabilities that will allow developers to protect and further secure their web applications. This is a direct integration with AWS WAF, allowing Amplify developers to connect a Web ACL directly to their Amplify hosted application. A web firewall is essential for professional developers to protect their applications from common web exploits, enhance security, and ensure compliance. It offers features like IP blocking/allowlisting, geo-restrictions, and protection against bot traffic. By providing WAF, Amplify customers can significantly improve their application’s security posture, mitigate risks, and maintain the integrity of their data and user experience.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/waf/" target="_blank" rel="noopener"&gt;AWS WAF&lt;/a&gt; is a full service offering that lets customers configure a set of rules, called a web access control list (web ACL), that allow, block, or monitor (count) web requests based on customizable web security rules and conditions that you define. When you integrate your Amplify Hosting app with AWS WAF, you gain more control over incoming traffic. To learn more about AWS WAF, see &lt;a href="https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works.html" target="_blank" rel="noopener"&gt;How AWS WAF Works&lt;/a&gt; in the Developer Guide.&lt;/p&gt; 
&lt;p&gt;If you are new to the AWS WAF service, Amplify Hosting has made it easy for developers to set up—&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;IP Blocking – Restrict web traffic by allowing or blocking requests from specified IP address ranges&lt;/li&gt; 
 &lt;li&gt;Geo Restriction – allow or block access based on specific countries&lt;/li&gt; 
 &lt;li&gt;Firewall protections – general firewall protections to protect against the most common vulnerabilities found in web applications, block IP addresses from potential threats based on Amazon internal threat intelligence, and protect against malicious actors discovering application vulnerabilities.&lt;/li&gt; 
 &lt;li&gt;Disable Amplify URL – restrict access to the default Amplify generated amplifyapp.com domain (useful once you add a custom domain to prevent bots and search engines from crawling the domain)&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;As a reminder, protections enabled through the Amplify console, will be applied to an underlying Web ACL in your AWS account. For fine-grained rulesets, developers can leverage the WAF console rule builder.&lt;/p&gt; 
&lt;h2&gt;Getting Started&lt;/h2&gt; 
&lt;p&gt;To get started with associating WAF to your app, please follow our &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/WAF-integration.html#getting-started-using-waf" target="_blank" rel="noopener"&gt;public documentation.&lt;/a&gt;&lt;/p&gt; 
&lt;h2&gt;Availability and pricing&lt;/h2&gt; 
&lt;p&gt;Firewall support is available today, in preview, in all AWS Regions that Amplify Hosting operates &lt;strong&gt;&lt;span style="text-decoration: underline"&gt;except for the opt-in regions&lt;/span&gt;&lt;/strong&gt;. This integration falls under a WAF global resource, similar to CloudFront. Web ACLs can be attached to multiple Amplify Hosting apps, but they must reside in the same region.&lt;/p&gt; 
&lt;p&gt;During the preview, you will only incur utilization-based charges from the WAF service. At a glance, WAF charges $5/month per web ACL and $1 per rule among other charges. See &lt;a href="https://aws.amazon.com/waf/pricing/" target="_blank" rel="noopener"&gt;WAF pricing&lt;/a&gt;. At a minimum, you will pay $7 for this integration assuming the 1 web ACL with 2 rules. In addition to the WAF usage charges, this integration will incur a flat fee per app from the Amplify service once it becomes generally available (GA). During the preview period, there will be no Amplify-specific charges. Pricing details will be announced at GA.&lt;/p&gt; 
&lt;h2&gt;Next steps&lt;/h2&gt; 
&lt;p&gt;Add firewall protections to your app today, by visiting the &lt;a href="https://console.aws.amazon.com/amplify/apps" target="_blank" rel="noopener"&gt;AWS Amplify console&lt;/a&gt;.&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Working with AWS AppSync Events: Real-time Web Games with Chat</title>
		<link>https://aws.amazon.com/blogs/mobile/working-with-aws-appsync-events-real-time-web-games-with-chat/</link>
		
		<dc:creator><![CDATA[Michael Liendo]]></dc:creator>
		<pubDate>Tue, 26 Nov 2024 16:14:00 +0000</pubDate>
				<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[AppSync]]></category>
		<category><![CDATA[aws-appsync]]></category>
		<guid isPermaLink="false">eb0f4eccd3e0ca15377e9700424055b21ec5f807</guid>

					<description>In this post, we’ll look at the core concepts needed to create an online version of a game where players try to match four of their tokens in a row. We’ll also see how AWS Amplify Gen 2 enables us to quickly connect to an AWS backend, and how AWS AppSync events allows players to […]</description>
										<content:encoded>&lt;p&gt;In this post, we’ll look at the core concepts needed to create an online version of a game where players try to match four of their tokens in a row. We’ll also see how AWS Amplify Gen 2 enables us to quickly connect to an AWS backend, and how AWS AppSync events allows players to send game updates in real-time through the use of WebSocket connections. By the end of this post, you’ll feel more confident in using AppSync events with authorization handled by Identity and Access Management (IAM), and better understand its role in today’s application development.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;This post is the second post in a series on AppSync events. While the &lt;a href="https://aws.amazon.com/blogs/mobile/announcing-aws-appsync-events-serverless-websocket-apis/"&gt;announcement post&lt;/a&gt; contains an overview of the service, the first post in this series on building with AppSync events can be found &lt;a href="https://aws.amazon.com/blogs/mobile/working-with-aws-appsync-events-serverless-websockets-for-pub-sub/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;h2&gt;Application Overview&lt;/h2&gt; 
&lt;p&gt;I remember first teaching my kids how to win at the popular game tic-tac-toe. For those that don’t know, it’s what’s known as a &lt;a href="https://en.wikipedia.org/wiki/Solved_game#:~:text=Many%20algorithms%20rely%20on%20a,(a%20result%20manually%20determinable)"&gt;“solved”&lt;/a&gt; game—there’s a way to guarantee you either win or draw. This was a great way for them to learn about algorithms, but admittedly, it made our time playing the game much less fun.&lt;/p&gt; 
&lt;p&gt;Fortunately, it was at that moment in their young age that I introduced them to the game four-in-a-row. While this is still a solved game, it’s more difficult to track and much easier to simply have a good time playing.&lt;/p&gt; 
&lt;p&gt;Instead of carrying around the game pieces and being within talking distance from the person we’re playing against, our application is fully online and supports chat functionality. However, instead of requiring players to login, we’ll instead ask for their username and generate a unique game code that they can share with the person they’re playing with.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13666" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/26/intro-4-1024x742.png" alt="screenshot of game creation page" width="1024" height="742"&gt;&lt;/p&gt; 
&lt;p&gt;By creating a game, you are player one (red), and by joining a game, you are the player two (yellow). To make sure the other player is there, players can chat with each other throughout the game.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13667" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/26/gameplay-1024x563.png" alt="screenshot of two seperate screens where players are playing agains one another and chatting" width="1024" height="563"&gt;&lt;/p&gt; 
&lt;p&gt;The game will automatically stop once four of the same colored pieces are placed in a row, whether that’s vertically, horizontally, or diagonally. From there, players can choose to play a new game, or exit the game all together.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;To view the code, along with a readme with instructions for hosting and running the code locally, &lt;a href="https://github.com/build-on-aws/fullstack-appsync-events-connect-4"&gt;view the repository&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;Due to games being short-lived, there isn’t a need to persist data in a database. In addition, having real-time capabilities as the heart of this application, means there isn’t an API.&lt;/p&gt; 
&lt;p&gt;In fact, the entire backend is comprised of two core AWS services:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon Cognito&lt;/strong&gt;: A Cognito Identity pool provides scoped down permissions for unauthenticated access to our event API&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS AppSync events&lt;/strong&gt;: Standalone WebSocket endpoints that scale to millions of subscribers.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13668" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/26/archoverview-1024x465.png" alt="architecture diagram of how the app flows" width="1024" height="465"&gt;&lt;/p&gt; 
&lt;h2&gt;Creating an AWS AppSync event API with IAM Authorization&lt;/h2&gt; 
&lt;p&gt;An AppSync event API can authorize calls using an API key, Cognito userpools, AWS Lambda, OIDC, or IAM. In the previous post, we saw how an API key can be configured, and in future posts we’ll showcase both Lambda and Cognito. However, for applications that need secure unauthenticated access, the IAM auth mode is a great choice and what we’ll discuss in this section.&lt;/p&gt; 
&lt;p&gt;With Amplify a thin layer over the AWS CDK is used when an Amplify project is scaffolded.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;While this project uses AWS Amplify to create a fullstack application, it is not a required part of using AWS AppSync events&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;In the &lt;code&gt;amplify/backend.ts&lt;/code&gt; file, you’ll notice that the we have &lt;code&gt;auth&lt;/code&gt; configured:&lt;/p&gt; 
&lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typesript"&gt;const backend = defineBackend({ auth })&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This one line of code setups ups our Cognito user pool and Cognito identity pool. Since the user pool keeps track of logged in users, we won’t make use of it, however, we the identity pool is key for our application since it will authorize our users that visit our app but haven’t logged in. To showcase how that comes together, we create a separate CDK stack to house services that extend Amplify:&lt;/p&gt; 
&lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;const customResources = backend.createStack('custom-resources-connect4')&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This is simply a logical way to group services together and nest them in our main &lt;code&gt;backend&lt;/code&gt; stack. The items that we’ll add to this stack will all related to our event API:&lt;/p&gt; 
&lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;const cfnEventAPI = new CfnApi(customResources, 'cfnEventAPI', {
    name: 'serverless-connect4',
    eventConfig: {
        authProviders: [{ authType: AuthorizationType.IAM }],
        connectionAuthModes: [{ authType: AuthorizationType.IAM }],
        defaultPublishAuthModes: [{ authType: AuthorizationType.IAM }],
        defaultSubscribeAuthModes: [{ authType: AuthorizationType.IAM }],
    },
})

new CfnChannelNamespace(customResources, 'cfnEventAPINamespace', {
    apiId: cfnEventAPI.attrApiId,
    name: 'game',
})&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;As shown above, we first create our event API by leveraging the L1 construct from the AWS CDK. In doing so, we provide it with the name of our API, and pass in an config that represents the auth providers we’ll accept, and which providers to allow for connecting, publishing, and subscribing.&lt;/p&gt; 
&lt;p&gt;Additionally, we create a root namespace called &lt;code&gt;game&lt;/code&gt;. Client apps can connect to this namespace, and segments off of the root like &lt;code&gt;/game/gameId/chat&lt;/code&gt;, to further specify the connection data they are interested in receiving.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;Setting up an event API as infrasture-as-code (IAC), currently involves using an L1 construct. These directly correspond to their &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appsync-api.html"&gt;CloudFormation reference&lt;/a&gt;. The posts in this series will be updated once the higher-level L2 constructs are available.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;By specifying, &lt;code&gt;IAM&lt;/code&gt; as the authorization mode, we’ll need to attach a policy that allows calling our event API to an AWS service. The service we’ll use is our Cognito Identity pool:&lt;/p&gt; 
&lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy(
    new Policy(customResources, 'AppSyncEventPolicy', {
        statements: [
            new PolicyStatement({
                actions: [
                    'appsync:EventConnect',
                    'appsync:EventPublish',
                    'appsync:EventSubscribe',
                ],
                resources: [`${cfnEventAPI.attrApiArn}/*`, `${cfnEventAPI.attrApiArn}`],
            }),
        ],
    })
)&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Above, we simply use the &lt;code&gt;unauthenticatedUserIamRole&lt;/code&gt; from our auth resource (Cognito) to directly attach a policy. Note that Cognito comes available with two roles: One for when users are logged in and stored in a user pool (&lt;code&gt;authenticatedRole&lt;/code&gt;), and another for when they are not logged in and we want to use permissions from the identity pool (&lt;code&gt;unauthenticatedRole&lt;/code&gt;).&lt;/p&gt; 
&lt;p&gt;With our event API configured and connected to Cognito, we’ll pass the related values to our frontend application so we can make use of them to connect, publish, and subscribe:&lt;/p&gt; 
&lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;backend.addOutput({
    custom: {
        events: {
            url: `https://${cfnEventAPI.getAtt('Dns.Http').toString()}/event`,
            aws_region: customResources.region,
            default_authorization_type: AuthorizationType.IAM,
        },
    },
})&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;The format here is important as the Amplify JavaScript libraries have been updated to work with event API’s so long as &lt;code&gt;custom&lt;/code&gt; data in the &lt;code&gt;events&lt;/code&gt; object contains the &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;aws_region&lt;/code&gt;, and &lt;code&gt;default_authorization_type&lt;/code&gt; items.&lt;/p&gt; 
&lt;p&gt;Following the readme for this project, with our backend configured, a developer can now run &lt;code&gt;npx ampx sandbox&lt;/code&gt; to deploy these resources to their AWS account.&lt;/p&gt; 
&lt;h2&gt;Connecting to an AppSync event API with AWS Amplify&lt;/h2&gt; 
&lt;p&gt;In our NextJS application, we have a home page located at &lt;code&gt;app/page.tsx&lt;/code&gt;, and our game page at &lt;code&gt;app/game/[code]/page.tsx&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;As shown in the earlier screenshot, the home page simply captures the username of the user and in the event the user is creating a new game, it will generate a game code. From there, the user is taken to a &lt;a href="https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes"&gt;dynamic route&lt;/a&gt; where the &lt;code&gt;code&lt;/code&gt; is the game code.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;As shown in the &lt;code&gt;app/layout.tsx&lt;/code&gt; file, our Next.js application is already configured to use our AWS backend. The setup for this can be found in the &lt;code&gt;components/configureAmplify.tsx&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;In the &lt;code&gt;app/game/[code]/page.tsx&lt;/code&gt; we can see our application take shape. We connect to our WebSocket endpoint using the &lt;code&gt;events.connect&lt;/code&gt; method from the &lt;code&gt;aws-amplify/data&lt;/code&gt; library. The best place to do this is in a &lt;code&gt;useEffect&lt;/code&gt; call since we want it to happen when the page first loads:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-typescript"&gt;useEffect(() =&amp;gt; {
    const subscribeToGameState = async (gameCode: string) =&amp;gt; {
        const channel = await events.connect(`/game/${gameCode}`)
        const sub = channel.subscribe({
            next: (data) =&amp;gt; {
                dispatch({ type: 'UPDATE_GAME_STATE', newState: data.event })
            },
            error: (err) =&amp;gt; console.error('uh oh spaghetti-o', err),
        })
        return sub
    }

    const subPromise = subscribeToGameState(gameCode)
    return () =&amp;gt; {
        Promise.resolve(subPromise).then((sub) =&amp;gt; {
            console.log('closing the connection')
            sub.unsubscribe()
        })
    }
}, [gameCode])&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Once we establish a connection to a particular channel, we can begin publishing and subscribing to it. For this use case, every time we receive a message from another player, we send the data to a &lt;a href="https://react.dev/reference/react/useReducer"&gt;reducer&lt;/a&gt; so that the new state can be rendered in our UI. This can be viewed in the &lt;code&gt;app/game/[code]/GameState.ts&lt;/code&gt; file.&lt;/p&gt; 
&lt;p&gt;The last part of this &lt;code&gt;useEffect&lt;/code&gt; file involves cleaning up any connections once the page is closed or navigated away from. This is connection by calling a subscriptions &lt;code&gt;unsubscribe&lt;/code&gt; method. This helps avoid memory leaks that would otherwise slow down our application.&lt;/p&gt; 
&lt;p&gt;Publishing an event is how we pass data from one event source to a client. In our app, anytime a player places a piece on the board, clicks the “New Game” or “Reset Game” buttons, we publish and event containing those details to the &lt;code&gt;/game/${gameCode}&lt;/code&gt; segment on our channel:&lt;/p&gt; 
&lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;await events.post(`/game/${gameCode}`, {
    board: newState.board,
    currentPlayer: newState.currentPlayer,
    winner: newState.winner,
    gameOver: newState.gameOver,
})&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;As you can see, making use of an event API in your fullstack applications requires very little code!&lt;/p&gt; 
&lt;p&gt;When it comes to sending chat messages, the process is the same. Another &lt;code&gt;useEffect&lt;/code&gt; call sets up a connection on the &lt;code&gt;/game/${gameCode}/chat&lt;/code&gt; segment of our channel and whenever a user hits enter on the message input, a call to &lt;code&gt;await events.post(`/game/${gameCode}/chat`,newMessage)&lt;/code&gt; is sent.&lt;/p&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this post, we discussed how an application can be brought to life when combining a modern frontend framework like Next.js, with the power of Amplify, and the ease and scalability of AppSync events. We also saw how Amplify can be extended beyond its core capabilities to use L1 constructs in the CDK.&lt;/p&gt; 
&lt;p&gt;AWS AppSync event API’s offer 250,000 event operations as part of its free tier and scales to millions of subscribers. As a fully managed service, developers now have a simple way to bring real-time capabilities to their apps without being locked to a specific API service.&lt;/p&gt; 
&lt;p&gt;To learn more about AWS AppSync events visit the &lt;a href="https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-welcome.html"&gt;documentation page&lt;/a&gt;. Also, if you’re interested in building games on AWS, there is currently an &lt;a href="https://awsdevchallenge.devpost.com/"&gt;AWS Game Builder Challenge&lt;/a&gt; going on with $150,000 being awarded in prizes!&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Working with AWS AppSync Events: Serverless WebSockets for Pub/Sub</title>
		<link>https://aws.amazon.com/blogs/mobile/working-with-aws-appsync-events-serverless-websockets-for-pub-sub/</link>
		
		<dc:creator><![CDATA[Michael Liendo]]></dc:creator>
		<pubDate>Mon, 25 Nov 2024 22:56:19 +0000</pubDate>
				<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[AppSync]]></category>
		<category><![CDATA[aws-appsync]]></category>
		<guid isPermaLink="false">7926b8ac3a86f1d6c1c091e23ba20da8db381457</guid>

					<description>AWS AppSync simplifies and manages connecting applications to events, data, and AI models. Now, with the new addition of AWS AppSync events, developers can create real-time experiences by publishing updates from any event source to subscribed clients over serverless WebSockets. AWS AppSync events offers a standalone Pub/Sub service that is not bound to GraphQL. In […]</description>
										<content:encoded>&lt;p&gt;AWS AppSync simplifies and manages connecting applications to events, data, and AI models. Now, with the new addition of AWS AppSync events, developers can create real-time experiences by publishing updates from any event source to subscribed clients over serverless WebSockets. AWS AppSync events offers a standalone Pub/Sub service that is not bound to GraphQL.&lt;/p&gt; 
&lt;p&gt;In this post we’ll start small by discussing a practical-yet-simple use case: a leaderboard. However, in future posts, we’ll showcase different authorization modes, data enrichment, and other scenarios where an event API would be useful.&lt;/p&gt; 
&lt;h2&gt;Application Overview&lt;/h2&gt; 
&lt;p&gt;Ephemeral leaderboards, as the name suggests, are simply leaderboards that do not persist data. While often used in fast, short-lived games, this architecture also applies to high-volume chat apps where what was said previously, is less important than what is said in the moment.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;In case you’d like to implement this solution yourself, the code along with installation instructions can be cloned from &lt;a href="https://github.com/build-on-aws/fullstack-leaderboard-awsamplify-appsync-events"&gt;this repo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;As seen in the screenshot below, our application is a single page that allows users to subscribe to all player position updates in realtime. To simulate an external source posting events, there’s an included “Simulate Scoring” button that will publish a series of updates over a quick interval.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13659" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/25/leaderboard-overview-1024x555.png" alt="screenshot of leaderboard web app" width="1024" height="555"&gt;&lt;/p&gt; 
&lt;p&gt;It’s important to callout the simplicity of our architecture. There isn’t a need for an authZ service, an API, or a database. We’re simply using our event API inside of our web application.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13660" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/25/learderboard-arch-1024x465.png" alt="" width="1024" height="465"&gt;&lt;/p&gt; 
&lt;h2&gt;Creating a Fullstack Application&lt;/h2&gt; 
&lt;p&gt;The easiest way to set this up is in the AWS Console, however, this post will show how to create an Event API using &lt;a href="https://aws.amazon.com/cdk/"&gt;AWS CDK&lt;/a&gt; L1 constructs.&lt;/p&gt; 
&lt;p&gt;Additionally, when it comes to building fullstack applications on AWS, AWS Amplify Gen 2 excels by offering a TypeScript-first experience that allows developers to drop down to CDK L2 and L1 constructs when needed.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;AWS Amplify is used for simplification of publishing and subscribing, but is in no way a requirement for working with an event API&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;Get started initializing the frontend repository with Amplify Gen 2 by running the following command:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-bash"&gt;npm create amplify&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Along with automatically installing the AWS CDK and Amplify JavaScript libraries, that command also creates an &lt;code&gt;amplify&lt;/code&gt; directory where developers can add/modify their AWS backend.&lt;/p&gt; 
&lt;p&gt;Next, feel free to delete the &lt;code&gt;auth&lt;/code&gt; and &lt;code&gt;data&lt;/code&gt; folders since those reference Amazon Cognito and AWS AppSync GraphQL configurations that won’t be needed in this project.&lt;/p&gt; 
&lt;p&gt;Finally, we’ll cleanup the references to those services by updating the &lt;code&gt;amplify/backend.ts&lt;/code&gt; so that it only contains the following:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;import { defineBackend } from '@aws-amplify/backend'

const backend = defineBackend({})&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2&gt;Creating an event API using the AWS CDK&lt;/h2&gt; 
&lt;p&gt;AWS Amplify is being used as our way of creating a fullstack CDK project. While Amplify abstracts several AWS services, the breadth of AWS offerings means there are many that fallback to using a CDK construct. This is enables developers to start using AWS services in their code as soon as those constructs are added.&lt;/p&gt; 
&lt;p&gt;We’ll take advantage of this feature by importing the L1 constructs used for to create an &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.CfnApi.html"&gt;event API&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;To get started, we’ll first create a separate stack of resources that will contain all of L1 constructs for our app. In the &lt;code&gt;amplify/backend.ts&lt;/code&gt; file, add the following line of code:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-typescript"&gt;const customResources = backend.createStack('custom-resources-leaderboard')&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This is simply a logical container that we can use to group services.&lt;/p&gt; 
&lt;p&gt;The event API for our purposes will be broken up into 3 parts:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;The API itself&lt;/li&gt; 
 &lt;li&gt;The namespace(s) that the API can use for publishing and subscribing&lt;/li&gt; 
 &lt;li&gt;The authorization mechanism needed to secure our API&lt;/li&gt; 
&lt;/ol&gt; 
&lt;pre&gt;&lt;code class="lang-typescript"&gt;import {
    AuthorizationType,
    CfnApi,
    CfnApiKey,
    CfnChannelNamespace,
} from 'aws-cdk-lib/aws-appsync'

// previous code...

// new code
const cfnEventAPI = new CfnApi(customResources, 'cfnEventAPI', {
    name: 'realtime-leaderboard',
    eventConfig: {
        authProviders: [{ authType: AuthorizationType.API_KEY }],
        connectionAuthModes: [{ authType: AuthorizationType.API_KEY }],
        defaultPublishAuthModes: [{ authType: AuthorizationType.API_KEY }],
        defaultSubscribeAuthModes: [{ authType: AuthorizationType.API_KEY }],
    },
})

new CfnChannelNamespace(customResources, 'cfnEventAPINamespace', {
    name: 'default',
    apiId: cfnEventAPI.attrApiId,
})

const cfnApiKey = new CfnApiKey(customResources, 'cfnEventAPIKey', {
    apiId: cfnEventAPI.attrApiId,
    description: 'realtime leaderboard demo',
})&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Note that due to the recent release of AWS AppSync events, only L1 constructs are available. A less verbose construct (L2), is currently in development. This post will be updated once that becomes available.&lt;/p&gt; 
&lt;p&gt;Worth calling out is how this event API is currently using an API key for authorization. This is great for development purposes and testing, how &lt;a href="https://docs.aws.amazon.com/appsync/"&gt;the docs&lt;/a&gt; demonstrate how to use authorization modes that include AWS Identity and Access Management (IAM), Amazon Cognito, OIDC, and AWS Lambda permissions as well.&lt;/p&gt; 
&lt;p&gt;The last step in creating our backend resources, is to pass the resolved values to our frontend application. Fortunately the Amplify JavaScript libraries have been updated to easy connect, publish, and subscribe to events so long as the required values are passed in a specified format.&lt;/p&gt; 
&lt;p&gt;At the bottom of the &lt;code&gt;amplify/backend.ts&lt;/code&gt; file, paste in the following:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;backend.addOutput({
    custom: {
        events: {
            url: `https://${cfnEventAPI.getAtt('Dns.Http').toString()}/event`,
            api_key: cfnApiKey.attrApiKey,
            aws_region: customResources.region,
            default_authorization_type: AuthorizationType.API_KEY,
        },
    },
})&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;We’re now ready to deploy our backend. This will create an &lt;code&gt;amplify_outputs.json&lt;/code&gt; file in the root of our project that contains the output needed to configure our frontend.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;If you do not have an AWS Account set up, follow &lt;a href="https://docs.amplify.aws/react/start/account-setup/"&gt;this guide&lt;/a&gt; from Amplify to setup your credentials.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;Run the following command to deploy:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-bash"&gt;npx ampx sandbox
# npx ampx sandbox --profile your-profile-name&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2&gt;Connecting, Publishing, and Subscribing to an event API using AWS Amplify&lt;/h2&gt; 
&lt;p&gt;This &lt;a href="https://github.com/build-on-aws/fullstack-leaderboard-awsamplify-appsync-events"&gt;project’s repository&lt;/a&gt; is already configured with Amplify. This is shown in the &lt;code&gt;components/configureAmplify.tsx&lt;/code&gt; file and made use of in the &lt;code&gt;app/layout.tsx&lt;/code&gt; file.&lt;/p&gt; 
&lt;p&gt;All that’s left to do is test that our backend works by calling the related methods in our frontend.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;By design, an AWS AppSync event does not need the Amplify libraries. It’s possible to &lt;a href="https://docs.aws.amazon.com/appsync/latest/eventapi/configure-event-api-auth.html"&gt;configure a client&lt;/a&gt; using native WebSocket protocols. Amplify simply offers a convenient solution to prevent boilerplate.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;In the &lt;code&gt;app/page.tsx&lt;/code&gt; file, we’ll want to setup a connection to the channel we created in our backend called &lt;code&gt;default&lt;/code&gt;. We also have the ability to create segments on our channel. To view this in action, inside of a &lt;code&gt;useEffect&lt;/code&gt; method, we’ll connect to our application with the following code:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;const channelConnect = async () =&amp;gt; {
    try {
        const channel = await events.connect('/default/leaderboard')
        channelRef.current = channel

        channel.subscribe({
            next: handleNewData,
            error: (err) =&amp;gt; console.log(err),
        })
    } catch (e) {
        console.log('Error connecting to channel: ', e)
    }
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The &lt;code&gt;events.connect&lt;/code&gt; function from Amplify is used to establish the connection to the &lt;code&gt;default/leaderboard&lt;/code&gt; channel. This allows us to subscribe to any incoming data from that particular channel.&lt;/p&gt; 
&lt;p&gt;Whenever a user clicks the “Simulate Scoring” button, we publish events. While this is occurring from the same page, this could easily be added to an AWS Lambda function, or other source.&lt;/p&gt; 
&lt;p&gt;Publishing data is used with the &lt;code&gt;events.post&lt;/code&gt; function, as shown with the following code in the &lt;code&gt;app/page.tsx&lt;/code&gt; file where a random player is selected and given a random score update before posting to the event API:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre class="unlimited-height-code"&gt;&lt;code class="lang-typescript"&gt;const handlePublish = async () =&amp;gt; {
    for (let i = 0; i &amp;lt;= 10; i++) {
        const randomItem =
            leaderboardData[Math.floor(Math.random() * leaderboardData.length)].id
        const randomScore = Math.floor(Math.random() * 20)

        await events.post('/default/leaderboard', {
            id: randomItem,
            score: randomScore,
        })
        await new Promise((resolve) =&amp;gt; setTimeout(resolve, 100))
    }
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this post, we discussed how AWS AppSync events offer a simple Pub/Sub solution that can be used in both greenfield and brownfield applications. We also showcased how Amplify Gen 2 can be used to bring in L1 constructs, enabling developers to use newly announced features as infrastructure-as-code sooner.&lt;/p&gt; 
&lt;p&gt;While this application was a simple demonstration, it’s important to note that AWS AppSync events are applicable to high-throughput applications that can scale to millions of subscribers such as gaming, sports-betting, and live-auctioning.&lt;/p&gt; 
&lt;p&gt;Get started with 250,000 real-time event API operations per month for free. To learn more about AWS AppSync events, &lt;a href="https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-welcome.html"&gt;visit the documentation page&lt;/a&gt;. For more information on pricing, see &lt;a href="https://aws.amazon.com/appsync/pricing/"&gt;the pricing page&lt;/a&gt;.&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Building RAG-based applications with AWS Amplify AI Kit and Neon Postgres</title>
		<link>https://aws.amazon.com/blogs/mobile/building-rag-based-applications-with-aws-amplify-ai-kit-and-neon-postgres/</link>
		
		<dc:creator><![CDATA[Michael Liendo]]></dc:creator>
		<pubDate>Fri, 22 Nov 2024 20:07:15 +0000</pubDate>
				<category><![CDATA[*Post Types]]></category>
		<category><![CDATA[Amazon Bedrock]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[AWS AppSync]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[AI/ML]]></category>
		<category><![CDATA[Amplify]]></category>
		<category><![CDATA[AppSync]]></category>
		<category><![CDATA[aws-appsync]]></category>
		<guid isPermaLink="false">b5487d3f2fa8e0ac457d8f4bc2b5d978cca9dec8</guid>

					<description>Modern application development has shifted to include not just tooling that offers a great developer experience (DX), but a sensible balance between simply getting started and the path to production. It is this very sentiment that inspired the release of Amplify AI kit. This abstraction over common AI tasks like conversing with a large language […]</description>
										<content:encoded>&lt;p&gt;Modern application development has shifted to include not just tooling that offers a great developer experience (DX), but a sensible balance between simply getting started and the path to production. It is this very sentiment that inspired the release of &lt;a href="https://docs.amplify.aws/react/ai/"&gt;Amplify AI kit&lt;/a&gt;. This abstraction over common AI tasks like conversing with a large language model (LLM) and generating content from a prompt means developers have a faster time to market and avoid writing boilerplate code.&lt;/p&gt; 
&lt;p&gt;In this post, we’ll move beyond the getting-started experience and use a serverless Postgres database from &lt;a href="https://neon.tech/"&gt;Neon&lt;/a&gt; to retrieve product data instead of the default database model from Amplify. In doing so, we’ll simplify the code needed to converse with an LLM using retrieval-augmented generation (RAG).&lt;/p&gt; 
&lt;h2&gt;Application Overview&lt;/h2&gt; 
&lt;p&gt;A common appeal to application consumers is how AI is used to enhance an existing product, instead of competing with it. A simple and effective way of demonstrating that is to create a chatbot that customers can interact with. In a real-world scenario, this wouldn’t prevent a customer from shopping on their own, but provides guides them towards a purchase using natural language.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13645" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/chat-overview-1024x832.png" alt="chat conversation showing conversation between llm and user." width="1024" height="832"&gt;&lt;/p&gt; 
&lt;p&gt;Architecturally speaking, whenever a user visits the application, they can chat with our LLM-powered bot. These models are trained on general data, though in our use case, we’d like it to know about our product data as well. Product information can change at any time, so it’s important to pull from the information in our database. This idea of choosing between general information or accessing specific data is powerful and accomplished with a &lt;a href="https://docs.amplify.aws/react/ai/concepts/tools/"&gt;tool&lt;/a&gt; (also known as “function-calling”).&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13646" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/diagram-1-1024x786.png" alt="diagram showing how a typical conversation wtih an llm interact" width="1024" height="786"&gt;&lt;/p&gt; 
&lt;p&gt;An important concept to keep in mind, is that when an LLM chooses to use a tool, it is not making a call to your data on your behalf. It is simply passing along what tool would work best given the user’s prompt. From there, applications decide what function they’d like invoke.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13647" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/diagram-2-1024x786.png" alt="diagram flow of data being sent to an llm that responds with a tool" width="1024" height="786"&gt;&lt;/p&gt; 
&lt;p&gt;The response from that function is then sent back to the LLM, and formatted as natural language to the end-user.&lt;/p&gt; 
&lt;p&gt;As one might imagine, orchestrating this pattern yourself can not only be tedious, but can lead to errors. Fortunately, this undifferentiated heavy-lifting is what Amplify AI kit provides by default.&lt;/p&gt; 
&lt;p&gt;Specifically, our project will use &lt;a href="https://aws.amazon.com/bedrock/?gclid=Cj0KCQiA0fu5BhDQARIsAMXUBOLnvCA39BdhL8SiYc4sR7x5z7kkRoXDtFL6kY4hTUzgHuvcte2UHyoaAnFcEALw_wcB&amp;amp;trk=0eaabb80-ee46-4e73-94ae-368ffb759b62&amp;amp;sc_channel=ps&amp;amp;ef_id=Cj0KCQiA0fu5BhDQARIsAMXUBOLnvCA39BdhL8SiYc4sR7x5z7kkRoXDtFL6kY4hTUzgHuvcte2UHyoaAnFcEALw_wcB:G:s&amp;amp;s_kwcid=AL!4422!3!692006004688!p!!g!!amazon%20bedrock!21048268554!159639952935"&gt;Amazon Bedrock&lt;/a&gt; with the Claude 3.5 Haiku LLM since this model comes with tool support. Amplify will allow us to specify a tool that corresponds to one of our databases. In our case, that will be our Neon Postgres database containing our product information.&lt;/p&gt; 
&lt;h2&gt;Creating Serverless Postgres Databases with Neon&lt;/h2&gt; 
&lt;p&gt;The ability to &lt;a href="https://docs.amplify.aws/react/build-a-backend/data/connect-to-existing-data-sources/connect-postgres-mysql-database/"&gt;connect to existing datasources&lt;/a&gt;means developers can use the power of Amplify’s schema introspection outside of &lt;a href="https://aws.amazon.com/dynamodb/"&gt;Amazon DynamoDB&lt;/a&gt;, the default database, to have CRUD operations generated on their behalf. Setting up a Neon database is straightforward. After creating an account, you’re prompted to create a project.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13648" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/neon1-1024x592.png" alt="project creation in Neon" width="1024" height="592"&gt;&lt;/p&gt; 
&lt;p&gt;Neon supports branch-based projects, similar to git. These are copies of the &lt;code&gt;main&lt;/code&gt; branch. In my case, I have the option to create a branch called &lt;code&gt;dev/mtliendo&lt;/code&gt; . This is recommended, but not required. In either case, you’ll want to make sure to copy the connection string for that branch as it will be important in the following sections.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13649" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/neon-2-1024x691.png" alt="screenshot of neon interface that shows a partially hidden connection string" width="1024" height="691"&gt;&lt;/p&gt; 
&lt;p&gt;Our default database is now set up, however it doesn’t yet contain any tables. More specifically, our table schema has yet to be defined. You’ll be saddened to find out that I don’t know how to write SQL. Fortunately, Neon solves this with their “Generate with AI” feature. By chatting with their LLM about what I’d like to do and a response will be generated for me.&lt;/p&gt; 
&lt;p&gt;In their SQL editor, I write the following:&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;Create a table schema called “Products”. Each product has a random id, an “updated at” field that is a date-time, a “created at” field that is a date-time, a “price in cents” field that is a number, a “name”, and a “description”.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13650" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/neon-3-1024x590.png" alt="screenshot of neon interface that displays the SQL Editor and using an LLM to generate a query" width="1024" height="590"&gt;&lt;/p&gt; 
&lt;p&gt;After running the prompt, I’m presented with the following response:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;CREATE TABLE Products (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    price_in_cents INTEGER NOT NULL,
    name VARCHAR(255) NOT NULL,
    description TEXT
);
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;From there, I have option to make changes to the code, and run the command once everything once I’m comfortable with the syntax.&lt;/p&gt; 
&lt;p&gt;To verify everything is setup as expected, clicking on the “Tables” link in the sidebar will allow us to verify the schema, as well as &lt;a href="https://aws.amazon.com/blogs/mobile/new-in-aws-amplify-integrate-with-sql-databases-oidc-saml-providers-and-the-aws-cdk/"&gt;populating our database&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13651" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/neon-4-1024x593.png" alt="screenshot of Neon interface that allows records to be added" width="1024" height="593"&gt;&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;For this project, I’ve added several items to the database. Also, be sure to copy the connection string to our database since we’ll be needing that in following section. The connection string can be found in the “Overview” section of the sidebar.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;h2&gt;Enhancing Amplify Gen 2 with AI Kit&lt;/h2&gt; 
&lt;p&gt;AWS Amplify is the easiest way to connect your frontend applications to a backend powered by AWS. Assuming an app using a JavaScript framework like NextJS has already been created, scaffold Amplify files by running the following command in the root of the project:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;npm create amplify
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This will install Amplify’s dependencies as well as create an &lt;code&gt;amplify&lt;/code&gt; directory. Before modifying the code in that directory, we’ll install a few other dependencies needed for Amplify AI kit:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;npm i @aws-amplify/ui-react @aws-amplify/ui-react-ai
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;These are UI components that we’ll make use of shortly.&lt;/p&gt; 
&lt;p&gt;Before doing so, we’ll let Amplify introspect our database with the &lt;code&gt;products&lt;/code&gt; table so we can use that in our backend. The first step is to store our connection string as a secret. This secret is stored in a Parameter Store in &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html"&gt;AWS Systems Manager&lt;/a&gt;. Fortunately, Amplify provides a simple way to doing so.&lt;/p&gt; 
&lt;p&gt;In your terminal, run the following command:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;npx ampx sandbox secret set SQL_CONNECTION_STRING
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This sets a secret value of &lt;code&gt;SQL_CONNECTION_STRING&lt;/code&gt; and prompts for the value. From here, paste in the connection string copied from Neon and press enter.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;This section assumes you already have AWS Amplify configured on your local machine. If you need to configuration Amplify, please refer to the &lt;a href="https://docs.amplify.aws/react/start/account-setup/"&gt;documentation&lt;/a&gt; for instructions.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;Once the secret is stored, we can instruct Amplify to introspect our database and create CRUD operations that we can make use of in our frontend application:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;npx ampx generate schema-from-database --connection-uri-secret SQL_CONNECTION_STRING --out amplify/data/schema.sql.ts 
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Running this command will create a &lt;code&gt;schema.sql.ts&lt;/code&gt; file in the &lt;code&gt;amplify/data&lt;/code&gt; folder. It’s important to not modify this file as it’s managed by Amplify. Once that command is ran, the file should look similar to the below screenshot:&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="aligncenter size-large wp-image-13652" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/codeshot-1-1024x795.png" alt="screenshot of code editor that display an examle schema configuration" width="1024" height="795"&gt;&lt;/p&gt; 
&lt;p&gt;Amplify uses this file to map our Postgres database to a format that works with its &lt;code&gt;a.model&lt;/code&gt; method.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;Check out the documentation if you want to learn &lt;a href="https://docs.amplify.aws/react/build-a-backend/data/connect-to-existing-data-sources/connect-postgres-mysql-database/#how-does-it-work"&gt;what Amplify does behind-the-scenes to make this work&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;pre&gt;&lt;code&gt;import { type ClientSchema, defineData, a } from '@aws-amplify/backend'
import { schema as generatedSqlSchema } from './schema.sql'

const sqlSchema = generatedSqlSchema.setAuthorization((models) =&amp;gt; [
    models.items.authorization((allow) =&amp;gt; [allow.authenticated().to(['read'])]),
])
const schema = a.schema({
    chat: a
        .conversation({
            aiModel: a.ai.model('Claude 3.5 Haiku'),
            systemPrompt:
                'You are a helpful assistant, that focuses on selling and upselling merchandise',
            tools: [
                a.ai.dataTool({
                    name: 'MerchQuery',
                    description:
                        'Search for questions regarding merchandise, shopping apparel, and item prices.',
                    model: a.ref('items'), //! This refers to the name of our table
                    modelOperation: 'list',
                }),
            ],
        })
        .authorization((allow) =&amp;gt; allow.owner()),
})

const combinedSchema = a.combine([sqlSchema, schema])

export type Schema = ClientSchema&amp;lt;typeof combinedSchema&amp;gt;

export const data = defineData({ schema: combinedSchema })
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Now that our Neon database is in our application, we can import that into the &lt;code&gt;amplify/data/resource.ts&lt;/code&gt; file and combine it with the conversation capabilities of Amplify AI kit. Let’s breakdown what is happening in this file:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Line 4:&lt;/strong&gt; Here we are assigning authorization rules on our &lt;code&gt;products&lt;/code&gt; table from Neon. In this case, only signed in users can perform &lt;code&gt;read&lt;/code&gt; operations against it.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Line 8:&lt;/strong&gt; We create an identifier called &lt;code&gt;chat&lt;/code&gt;. This is a &lt;a href="https://docs.amplify.aws/react/ai/conversation/"&gt;conversation&lt;/a&gt; bot that takes in, at a minimum, the name of the LLM, and a prompt on how it should behave. Worth nothing that the model names are typed and are available in Intellisense.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Line 13:&lt;/strong&gt; We enhance our bot by giving it a tool. The name and description are up to us to define, whereas the &lt;code&gt;model&lt;/code&gt; must refer to the name of our Neon database. Currently the only supported &lt;code&gt;modelOperation&lt;/code&gt; is &lt;code&gt;list&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Line 22:&lt;/strong&gt; This line offers insight into what is happening behind the scenes. A DynamoDB table keeps track of a signed in users conversation history.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;By putting together all of these elements, we have a fully-managed solution for securely conversing with an LLM that is aware of the items in our database.&lt;/p&gt; 
&lt;p&gt;To test our solution, we’ll first deploy our AWS backend by running the following command:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;npx ampx sandbox
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Once deployed, we can import our Amplify configuration and configure our client-side application to make use of the config, UI components, and hooks provided by Amplify:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;import { generateClient } from 'aws-amplify/api'
import { Schema } from '@/amplify/data/resource'
import { useEffect } from 'react'
import { Amplify } from 'aws-amplify'
import awsconfig from '@/amplify_outputs.json'
import { withAuthenticator } from '@aws-amplify/ui-react'
import { AIConversation, createAIHooks } from '@aws-amplify/ui-react-ai'
import '@aws-amplify/ui-react/styles.css'

Amplify.configure(awsconfig)

const client = generateClient&amp;lt;Schema&amp;gt;()
const { useAIConversation } = createAIHooks(client)
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Once configured, the entire frontend with chat, conversational awareness, streaming, loading states and authentication, can be set up in around 20 lines of code:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;function Home() {
    const [
        {
            data: { messages },
            isLoading,
        },
        handleSendMessage,
    ] = useAIConversation('chat')

    return (
        &amp;lt;div className="flex justify-center items-center m-10 max-w-screen-md"&amp;gt;
            &amp;lt;AIConversation
                avatars={{ user: { username: 'Focus Otter' } }}
                messages={messages}
                handleSendMessage={handleSendMessage}
                isLoading={isLoading}
                variant="bubble"
                welcomeMessage="Hello! I'm your helpful storefront assistant. Feel free to ask me questions about my merch!"
            /&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

export default withAuthenticator(Home)
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Feel free to compare the code above, with the first screenshot in this post. The new &lt;code&gt;AIConversation&lt;/code&gt; component of Amplify AI kit provides a full chat UI while still exposing various props to further customize to your specific needs.&lt;/p&gt; 
&lt;h2&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this post we discussed some of the complexities of building out fullstack applications that support conversing to an LLM using retrieval-augmented generation (RAG). We then saw how AWS Amplify’s new AI kit greatly simplifies this experience by abstracting away boilerplate allowing developers to focus on the parts that truly make the application different. As we saw, this ease in setup doesn’t come at the expense of extensibility. We proved that by creating a Postgres database from Neon and using that alongside our tool.&lt;/p&gt; 
&lt;p&gt;Amplify AI kit is yet another step forward in building scalable, secure fullstack applications. To get stared with Amplify AI kit in your own application, visit the &lt;a href="https://docs.amplify.aws/react/ai/set-up-ai/"&gt;documentation&lt;/a&gt;and get started today.&lt;/p&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Build a Travel Planner with React Native, AWS Amplify, and Amazon Bedrock Knowledge Base</title>
		<link>https://aws.amazon.com/blogs/mobile/building-a-personalized-travel-planner-with-amazon-bedrocks-knowledge-bases-and-rag/</link>
		
		<dc:creator><![CDATA[Muhammed Salih Güler]]></dc:creator>
		<pubDate>Fri, 22 Nov 2024 17:46:12 +0000</pubDate>
				<category><![CDATA[Amazon Bedrock]]></category>
		<category><![CDATA[Amazon Bedrock Agents]]></category>
		<category><![CDATA[Amazon Bedrock Knowledge Bases]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Amplify]]></category>
		<category><![CDATA[Front-End Web & Mobile]]></category>
		<category><![CDATA[AI/ML]]></category>
		<category><![CDATA[Amplify]]></category>
		<category><![CDATA[General Availability]]></category>
		<guid isPermaLink="false">3a2fe49521fa6170ebc59fada8f019e8cbdd9ad1</guid>

					<description>With the announcement of the Amplify AI kit, we learned how to build custom UI components, conversation history and add external data to the conversation flow. In this blog post, we will learn how to build a travel planner application using React Native. The application will generate responses using Retrieval Augmented Generation (RAG) and Large […]</description>
										<content:encoded>&lt;p&gt;&lt;img loading="lazy" class="alignnone size-large wp-image-13639" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/travel-planner-1-1024x576.png" alt="Cover image with image of phone" width="1024" height="576"&gt;&lt;/p&gt; 
&lt;p&gt;With the announcement of the Amplify AI kit, we learned how to build &lt;a href="https://aws.amazon.com/blogs/mobile/create-a-customized-ai-based-chat-interface-with-your-application-data"&gt;custom UI components&lt;/a&gt;, &lt;a href="https://github.com/aws-samples/amplify-ai-examples/tree/main/claude-ai"&gt;conversation history&lt;/a&gt; and &lt;a href="https://aws.amazon.com/blogs/mobile/add-a-conversational-interface-to-any-data-source"&gt;add external data to the conversation flow&lt;/a&gt;. In this blog post, we will learn how to build a travel planner application using React Native. The application will generate responses using Retrieval Augmented Generation (RAG) and Large Language Models (LLMs) based on knowledge bases.&lt;/p&gt; 
&lt;p&gt;To equip large language models (LLMs) with up-to-date and proprietary information, you can use RAG, a technique that fetches data from company data sources and enriches the prompt to provide more relevant and accurate responses. With Amazon Bedrock Knowledge Bases, you can give FMs and agents contextual information from your company’s private data sources for RAG to deliver more relevant, accurate, and customized responses.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;You can use the backend building and creating Knowledge Bases and RAG parts of this article with any web framework. However, this tutorial assumes you are building your applications with React Native and will explain the frontend code accordingly.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;h2&gt;Building an Amplify App&lt;/h2&gt; 
&lt;p&gt;For creating an Amplify application, we have to run the create-amplify command at the root folder of your application:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npm create amplify@latest -y&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This will install the necessary dependencies for our project on the AWS Amplify. If we open our project now on our IDE, we can see a new folder called &lt;code&gt;amplify&lt;/code&gt;:&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-full wp-image-13621" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/amplify-structure.jpg" alt="Structure of the Amplify project with auth and data." width="734" height="622"&gt;&lt;/p&gt; 
&lt;p&gt;The folder has a simple to-do application with email authentication. We will define the resources for the related categories under its own folders. E.g. For authentication we will update the &lt;code&gt;auth/resource.ts&lt;/code&gt; file.&lt;/p&gt; 
&lt;p&gt;Let’s add the authentication flow for our users to sign up for a personalized experience. First open the &lt;code&gt;auth/resource.ts&lt;/code&gt; file and update it like the following:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { defineAuth } from "@aws-amplify/backend";

export const auth = defineAuth({
  loginWith: {
    email: {
      verificationEmailSubject: "Welcome to Travel Advisor! Verify your email!",
      verificationEmailBody: (code) =&amp;gt; `Here is your verification code: ${code()}`,
      verificationEmailStyle: "CODE",
    },
  },
  userAttributes: {
    preferredUsername: {
      mutable: true,
      required: true,
    },
  },
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This will customize the user confirmation email and ask users to create a username for registration. Now we will do the initial deployment through an Amplify sandbox. You can use a personal cloud sandbox environment that provides an isolated development space to rapidly build, test, and iterate on a fullstack app. Each developer on your team can use their own disposable sandbox environment connected to cloud resources. Let’s do our first deployment. Before we do that, update the &lt;code&gt;backend.ts&lt;/code&gt; with the following:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { defineBackend } from "@aws-amplify/backend";
import { auth } from "./auth/resource";
import { data } from "./data/resource";

/**
 * @see https://docs.amplify.aws/react/build-a-backend/ to add storage, functions, and more
 */
defineBackend({
  auth,
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Commenting out the data resource it will prevent it from deploying for now. Run the following command to start a sandbox environment for auth:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npx ampx sandbox&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Next step is to implement the frontend. For this, we will be using &lt;a href="https://ui.docs.amplify.aws/"&gt;Amplify UI Components&lt;/a&gt;. Amplify UI is a collection of accessible, themeable, performant components that can connect directly to the cloud. With a few lines of code, we can turn complicated tasks to trivial tasks.&lt;/p&gt; 
&lt;p&gt;First install the necessary libraries for using the UI libraries.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npm install --force @aws-amplify/ui-react-native aws-amplify @aws-amplify/react-native react-native-safe-area-context @react-native-community/netinfo @react-native-async-storage/async-storage react-native-get-random-values&lt;/code&gt;&lt;/pre&gt; 
&lt;blockquote&gt;
 &lt;p&gt;The force flag is added due to the conflict of the ui library with latest version of react native.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;Install the pods for iOS to bind the libraries to native libraries.&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npx pod-install&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;After that update the &lt;code&gt;App&lt;/code&gt; component in the &lt;code&gt;App.tsx&lt;/code&gt; with the following:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import outputs from "./amplify_outputs.json";
Amplify.configure(outputs);

const SignOutButton = () =&amp;gt; {
  const { signOut } = useAuthenticator();
  return (
    &amp;lt;TouchableOpacity onPress={signOut}&amp;gt;
      &amp;lt;MaterialIcons name="exit-to-app" size={32} /&amp;gt;
    &amp;lt;/TouchableOpacity&amp;gt;
  );
};

export default function App() {
  const [username, setUsername] = useState("");
  useEffect(() =&amp;gt; {
    const fetchUsername = async () =&amp;gt; {
      const attributes = await fetchUserAttributes();
      const username = attributes.preferred_username ?? "";
      setUsername(username);
    };
    fetchUsername();
  }, []);
  return (
    &amp;lt;Authenticator.Provider&amp;gt;
      &amp;lt;Authenticator&amp;gt;
        &amp;lt;SafeAreaView style={styles.container}&amp;gt;
          &amp;lt;KeyboardAvoidingView behavior={"height"} style={styles.container}&amp;gt;
            &amp;lt;View style={styles.header}&amp;gt;
              &amp;lt;Text style={styles.headerIcon}&amp;gt;✈&amp;lt;/Text&amp;gt;
              &amp;lt;Text style={styles.headerText}&amp;gt;Travel Advisor&amp;lt;/Text&amp;gt;
              &amp;lt;SignOutButton /&amp;gt;
            &amp;lt;/View&amp;gt;
          &amp;lt;/KeyboardAvoidingView&amp;gt;
        &amp;lt;/SafeAreaView&amp;gt;
      &amp;lt;/Authenticator&amp;gt;
    &amp;lt;/Authenticator.Provider&amp;gt;
  );
}&lt;/code&gt;&lt;code class="lang-ts"&gt;
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Here are the major changes that happened:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Through the &lt;code&gt;fetchUserAttributes&lt;/code&gt; we are getting the extra attributes we defined.&lt;/li&gt; 
 &lt;li&gt;With the &lt;code&gt;useAuthenticator&lt;/code&gt; hook, we are calling the signOut button.&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;Authenticator&lt;/code&gt; and &lt;code&gt;Authenticator.Provider&lt;/code&gt; components will create the UI for authentication and control the authentication flow.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Now the authentication flow is ready to be tested. The next step is to implement the AI capabilities.&lt;/p&gt; 
&lt;p&gt;Amplify’s new AI capabilities make it easier to work with generative AI. For example, if we want to generate our components, let’s use the generation capability to see what it would look like. Open the &lt;code&gt;data/resource.ts&lt;/code&gt; file and update it with the following:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { type ClientSchema, a, defineData } from "@aws-amplify/backend";

const schema = a.schema({
  generateDestination: a
    .generation({
      aiModel: a.ai.model("Claude 3.5 Sonnet"),
      systemPrompt: `
You are an advanced travel assistant with comprehensive knowledge about global destinations, including their geography, climate patterns, historical significance, tourist attractions, and cost of living. Your task is to analyze the following information: {{input}}
Based solely on this input, determine the single best city on Earth for the user. Your response should be concise and definitive, presenting only the chosen city along with comprehensive information about their geography, climate patterns, historical significance, tourist attractions, and cost of living and why it's the ideal match. Do not ask for additional information or clarification. Provide your recommendation based exclusively on the given details.
      `,
    })
    .arguments({
      input: a.string().required(),
    })
    .returns(a.string().required())
    .authorization((allow) =&amp;gt; [allow.authenticated()]),
});

export type Schema = ClientSchema&amp;lt;typeof schema&amp;gt;;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: "userPool",
  },
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Thanks to this, we can generate an answer for our questions. If we open our &lt;code&gt;App.tsx&lt;/code&gt; file and call the generateDestination like the following from anywhere we can now generate a destination:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;const { data, errors } = await client.generations.generateDestination({
    input: inputText,
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;iframe loading="lazy" title="Amplify AI Generation without RAG" width="500" height="281" src="https://www.youtube-nocookie.com/embed/ggbuoyL902w?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-scripts allow-same-origin"&gt;&lt;/iframe&gt;&lt;/p&gt; 
&lt;h2&gt;Creating a Knowledge Base&lt;/h2&gt; 
&lt;p&gt;The information is highly depending on the strength of our prompt. Also the information is around the AI Model and it’s properties. However, Amazon Bedrock Knowledge Bases can help us give more information to our prompt.&lt;/p&gt; 
&lt;p&gt;We will create a basic knowledge base like the following:&lt;/p&gt; 
&lt;table&gt; 
 &lt;thead&gt; 
  &lt;tr&gt; 
   &lt;th&gt;City&lt;/th&gt; 
   &lt;th&gt;Country&lt;/th&gt; 
   &lt;th&gt;Population&lt;/th&gt; 
   &lt;th&gt;Description&lt;/th&gt; 
   &lt;th&gt;Financial Hub&lt;/th&gt; 
   &lt;th&gt;Is Capital?&lt;/th&gt; 
   &lt;th&gt;Ranking&lt;/th&gt; 
  &lt;/tr&gt; 
 &lt;/thead&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Tokyo&lt;/td&gt; 
   &lt;td&gt;Japan&lt;/td&gt; 
   &lt;td&gt;9.73 Million&lt;/td&gt; 
   &lt;td&gt;Tokyo is the capital of Japan. It blends ultramodern skyscrapers with historic temples and gardens. Known for its unique pop culture, advanced technology, and exquisite cuisine. Home to the world’s largest fish market and busiest pedestrian crossing.&lt;/td&gt; 
   &lt;td&gt;Yes&lt;/td&gt; 
   &lt;td&gt;Yes&lt;/td&gt; 
   &lt;td&gt;1&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Istanbul&lt;/td&gt; 
   &lt;td&gt;Turkey&lt;/td&gt; 
   &lt;td&gt;15.46 Million&lt;/td&gt; 
   &lt;td&gt;Istanbul straddles Europe and Asia across the Bosphorus Strait. Rich in history, it showcases Byzantine and Ottoman architecture. Famous for its bazaars, Turkish baths, and diverse culinary scene. The Hagia Sophia and Blue Mosque are iconic landmarks.&lt;/td&gt; 
   &lt;td&gt;Yes&lt;/td&gt; 
   &lt;td&gt;No&lt;/td&gt; 
   &lt;td&gt;5&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Berlin&lt;/td&gt; 
   &lt;td&gt;Germany&lt;/td&gt; 
   &lt;td&gt;3.7 Million&lt;/td&gt; 
   &lt;td&gt;Berlin is Germany’s capital, known for its vibrant arts scene, modern architecture, and complex history. Home to world-class museums, diverse neighborhoods, and remnants of the Berlin Wall. Famous for its techno clubs, street art, and multicultural cuisine.&lt;/td&gt; 
   &lt;td&gt;No&lt;/td&gt; 
   &lt;td&gt;Yes&lt;/td&gt; 
   &lt;td&gt;2&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;New York&lt;/td&gt; 
   &lt;td&gt;USA&lt;/td&gt; 
   &lt;td&gt;8.8 Million&lt;/td&gt; 
   &lt;td&gt;New York City is a global hub for finance, arts, and culture. Home to iconic landmarks like the Statue of Liberty and Empire State Building. Known for its diverse neighborhoods, Broadway theaters, world-class museums, and culinary diversity. Rich in history from colonial times to present.&lt;/td&gt; 
   &lt;td&gt;Yes&lt;/td&gt; 
   &lt;td&gt;No&lt;/td&gt; 
   &lt;td&gt;4&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Prague&lt;/td&gt; 
   &lt;td&gt;Czech Republic&lt;/td&gt; 
   &lt;td&gt;1.3 Million&lt;/td&gt; 
   &lt;td&gt;Prague is the capital of the Czech Republic, known as “The City of a Hundred Spires.” Famous for its medieval architecture, including Prague Castle and Charles Bridge. Renowned for its beer culture, classical music heritage, and well-preserved Old Town Square.&lt;/td&gt; 
   &lt;td&gt;No&lt;/td&gt; 
   &lt;td&gt;Yes&lt;/td&gt; 
   &lt;td&gt;3&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;Go to &lt;a href="https://console.aws.amazon.com/s3/"&gt;Amazon S3 Console&lt;/a&gt; now and click the &lt;strong&gt;Create bucket&lt;/strong&gt; button:&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-large wp-image-13625" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/anthropic5-1024x305.jpg" alt="Creating an S3 bucket from console" width="1024" height="305"&gt;&lt;/p&gt; 
&lt;p&gt;Select a unique name for our bucket and leave all as default selection. Next upload our file to our S3 bucket:&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-large wp-image-13626" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/anthropic8-1024x531.jpg" alt="Uploaded file is indicated through S3" width="1024" height="531"&gt;&lt;/p&gt; 
&lt;p&gt;Now we can create our knowledge bases. First, open the AWS Console and go to &lt;a href="https://console.aws.amazon.com/bedrock/"&gt;Amazon Bedrock&lt;/a&gt; page. Once we land on the page, select the &lt;strong&gt;Knowledge bases&lt;/strong&gt; from the left menu.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-large wp-image-13628" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/anthropic2-1024x627.jpg" alt="Create Knowledge Base button over AWS Console" width="1024" height="627"&gt;&lt;/p&gt; 
&lt;p&gt;Click on the &lt;strong&gt;Create knowledge base&lt;/strong&gt; button. Leave all of the default values (double check that S3 is selected) and click on next. In the next page, select the data source from our S3 buckets:&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-large wp-image-13629" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/anthropic10-1024x627.jpg" alt="Selecting the correct S3 bucket" width="1024" height="627"&gt;&lt;/p&gt; 
&lt;p&gt;Select an embedding model to convert our data:&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-large wp-image-13630" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/anthropic11-1024x627.jpg" alt="List of embedding models" width="1024" height="627"&gt;&lt;/p&gt; 
&lt;p&gt;We will also let Amazon create a vector store on our behalf or select a previously created store to allow Bedrock to store, update and manage embeddings of our data. Now we can start creating our knowledge base.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;The default setup for an Amazon Bedrock Knowledge Base is OpenSearch Serverless which has a default cost whether or not you use it. You can get an AWS bill if you are not careful. If you are just testing this out make sure to turn off the OpenSearch Serverless instance when you are done.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;We can test our knowledge base already in the console and see if the data is working as expected.&lt;/p&gt; 
&lt;p&gt;&lt;img loading="lazy" class="alignnone size-large wp-image-13632" src="https://d2908q01vomqb2.cloudfront.net/0a57cb53ba59c46fc4b692527a38a87c78d84028/2024/11/22/anthropic15-1024x627.jpg" alt="result of the knowledge base" width="1024" height="627"&gt;&lt;/p&gt; 
&lt;p&gt;Now it is time to use the knowledge base in our application.&lt;/p&gt; 
&lt;h2&gt;Using the Created Knowledge Base&lt;/h2&gt; 
&lt;p&gt;First let’s do some clean-up. We have to create our knowledge base aware conversation and connect that to a AppSync Resolver to communicate with the database. Update the &lt;code&gt;data/resource.ts&lt;/code&gt; file with the following:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { type ClientSchema, a, defineData } from "@aws-amplify/backend";

const schema = a.schema({
  chat: a
    .conversation({
      aiModel: a.ai.model("Claude 3 Haiku"),
      systemPrompt: `You are a helpful assistant.`,
      tools: [
        a.ai.dataTool({
          name: "DestinationKnowledgeBase",
          description:
            "A knowledge base to be checked about everything related to the cities.",
          query: a.ref("searchDestination"),
        }),
      ],
    })
    .authorization((allow) =&amp;gt; allow.owner()),
  searchDestination: a
    .query()
    .arguments({ input: a.string() })
    .handler(
      a.handler.custom({
        dataSource: "DestinationKnowledgeBaseDataSource",
        entry: "./bedrockresolver.js",
      })
    )
    .returns(a.string())
    .authorization((allow) =&amp;gt; [allow.authenticated()]),
});

export type Schema = ClientSchema&amp;lt;typeof schema&amp;gt;;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: "userPool",
  },
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This will add the earlier created knowledge base to the conversation as tool. The description will be the explanatory text for LLM to interact with the knowledge base. For adding a js resolver, create a file called bedrockresolver.js and paste the following:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-js"&gt;export function request(ctx) {
  const { input } = ctx.args;
  return {
    resourcePath: "/knowledgebases/&amp;lt;knowledge-base-id&amp;gt;/retrieve",
    method: "POST",
    params: {
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        retrievalQuery: {
          text: input,
        },
      }),
    },
  };
}

export function response(ctx) {
  return JSON.stringify(ctx.result.body);
}&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This will get the knowledge base with the provided id to the conversation’s context through AppSync. Lastly, we have to update the policies from lambda to data source in the &lt;code&gt;backend.ts&lt;/code&gt; file:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import { defineBackend } from "@aws-amplify/backend";
import { auth } from "./auth/resource";
import { data } from "./data/resource";
import * as iam from "aws-cdk-lib/aws-iam";

const backend = defineBackend({
  auth,
  data,
});

const KnowledgeBaseDataSource =
  backend.data.resources.graphqlApi.addHttpDataSource(
    "DestinationKnowledgeBaseDataSource",
    "https://bedrock-agent-runtime.&amp;lt;region&amp;gt;.amazonaws.com",
    {
      authorizationConfig: {
        signingRegion: "&amp;lt;region&amp;gt;",
        signingServiceName: "bedrock",
      },
    }
  );

KnowledgeBaseDataSource.grantPrincipal.addToPrincipalPolicy(
  new iam.PolicyStatement({
    resources: [
      "arn:aws:bedrock:&amp;lt;region&amp;gt;:&amp;lt;user-id&amp;gt;:knowledge-base/&amp;lt;knowledge-base-id&amp;gt;",
    ],
    actions: ["bedrock:Retrieve"],
  })
);&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Lastly update your UI like the following to handle the streaming:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-ts"&gt;import React, { useEffect, useState } from "react";
import {
  StyleSheet,
  Text,
  View,
  TextInput,
  TouchableOpacity,
  SafeAreaView,
  KeyboardAvoidingView,
  FlatList,
  ActivityIndicator,
} from "react-native";
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import { Authenticator, useAuthenticator } from "@aws-amplify/ui-react-native";
import { fetchUserAttributes } from "aws-amplify/auth";
import { Amplify } from "aws-amplify";
import { Schema } from "./amplify/data/resource";
import { generateClient } from "aws-amplify/data";

import outputs from "./amplify_outputs.json";
import { createAIHooks } from "@aws-amplify/ui-react-ai";

Amplify.configure(outputs);
const client = generateClient&amp;lt;Schema&amp;gt;();
const { useAIConversation } = createAIHooks(client);

const HomePage = () =&amp;gt; {
  const [username, setUsername] = useState("");
  const [inputText, setInputText] = useState("");

  const [
    {
      data: { messages },
      isLoading,
    },
    handleSendMessage,
  ] = useAIConversation("chat");

  const handleSend = () =&amp;gt; {
    handleSendMessage({
      content: [{ text: inputText }],
    });
    setInputText("");
  };
  useEffect(() =&amp;gt; {
    const fetchUsername = async () =&amp;gt; {
      const attributes = await fetchUserAttributes();
      const fetchedUsername = attributes.preferred_username ?? "";
      setUsername(fetchedUsername);
    };
    fetchUsername();
  }, []);
  return (
    &amp;lt;SafeAreaView style={styles.container}&amp;gt;
      &amp;lt;KeyboardAvoidingView behavior={"height"} style={styles.container}&amp;gt;
        &amp;lt;View style={styles.header}&amp;gt;
          &amp;lt;Text style={styles.headerIcon}&amp;gt;✈&amp;lt;/Text&amp;gt;
          &amp;lt;Text style={styles.headerText}&amp;gt;Travel Advisor&amp;lt;/Text&amp;gt;
          &amp;lt;TouchableOpacity
            onPress={() =&amp;gt; {
              const { signOut } = useAuthenticator();
              signOut();
            }}
          &amp;gt;
            &amp;lt;MaterialIcons name="exit-to-app" size={32} /&amp;gt;
          &amp;lt;/TouchableOpacity&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;FlatList
          data={messages}
          keyExtractor={(message) =&amp;gt; message.id}
          renderItem={({ item }) =&amp;gt;
            item.content
              .map((content) =&amp;gt; content.text)
              .join("")
              .trim().length == 0 ? (
              &amp;lt;View style={styles.loadingContainer}&amp;gt;
                &amp;lt;ActivityIndicator /&amp;gt;
              &amp;lt;/View&amp;gt;
            ) : (
              &amp;lt;ChatMessage
                text={item.content
                  .map((content) =&amp;gt; content.text)
                  .join("")
                  .trim()}
                isUser={item.role == "user"}
                userName={item.role == "user" ? username : "Travel Advisor"}
              /&amp;gt;
            )
          }
          contentContainerStyle={styles.chatContainer}
          ListEmptyComponent={() =&amp;gt; (
            &amp;lt;View style={styles.emptyContainer}&amp;gt;
              &amp;lt;Text style={styles.emptyText}&amp;gt;
                Start a conversation by sending a message below!
              &amp;lt;/Text&amp;gt;
            &amp;lt;/View&amp;gt;
          )}
        /&amp;gt;
        &amp;lt;View style={styles.inputContainer}&amp;gt;
          &amp;lt;TextInput
            style={styles.input}
            value={inputText}
            onChangeText={setInputText}
            placeholder="Describe your dream travel..."
            multiline={true}
            numberOfLines={3}
          /&amp;gt;
          &amp;lt;TouchableOpacity
            style={[styles.sendButton, isLoading &amp;amp;&amp;amp; styles.sendButtonDisabled]}
            onPress={handleSend}
            disabled={isLoading}
          &amp;gt;
            &amp;lt;Text style={styles.sendButtonText}&amp;gt;Send&amp;lt;/Text&amp;gt;
          &amp;lt;/TouchableOpacity&amp;gt;
        &amp;lt;/View&amp;gt;
      &amp;lt;/KeyboardAvoidingView&amp;gt;
    &amp;lt;/SafeAreaView&amp;gt;
  );
};

interface Message {
  text: string;
  isUser: boolean;
  userName: string;
}

const ChatMessage = ({ text, isUser, userName }: Message) =&amp;gt; (
  &amp;lt;View&amp;gt;
    &amp;lt;View
      style={[
        styles.messageBubble,
        isUser ? styles.userMessage : styles.aiMessage,
      ]}
    &amp;gt;
      &amp;lt;Text style={styles.messageText}&amp;gt;{text}&amp;lt;/Text&amp;gt;
    &amp;lt;/View&amp;gt;
    &amp;lt;Text style={[styles.nameText, isUser ? styles.userName : styles.aiName]}&amp;gt;
      {userName}
    &amp;lt;/Text&amp;gt;
  &amp;lt;/View&amp;gt;
);

export default function App() {
  return (
    &amp;lt;Authenticator.Provider&amp;gt;
      &amp;lt;Authenticator&amp;gt;
        &amp;lt;HomePage /&amp;gt;
      &amp;lt;/Authenticator&amp;gt;
    &amp;lt;/Authenticator.Provider&amp;gt;
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#FFFFFF",
  },
  header: {
    flexDirection: "row",
    alignItems: "center",
    padding: 15,
    borderBottomWidth: 1,
    borderBottomColor: "#E0E0E0",
  },
  headerIcon: {
    fontSize: 24,
    marginRight: 10,
  },
  headerText: {
    fontSize: 20,
    fontWeight: "bold",
    flex: 1,
  },
  signOutIcon: {
    fontSize: 24,
  },
  chatContainer: {
    padding: 10,
  },
  messageBubble: {
    maxWidth: "80%",
    padding: 10,
    borderRadius: 20,
    marginBottom: 5,
  },
  aiMessage: {
    alignSelf: "flex-start",
    backgroundColor: "#F0F0F0",
  },
  userMessage: {
    alignSelf: "flex-end",
    backgroundColor: "#DCF8C6",
  },
  messageText: {
    fontSize: 16,
  },
  nameText: {
    fontSize: 12,
    marginBottom: 10,
  },
  userName: {
    alignSelf: "flex-end",
    color: "#4CAF50",
  },
  aiName: {
    alignSelf: "flex-start",
    color: "#666666",
  },
  inputContainer: {
    flexDirection: "row",
    padding: 10,
    borderTopWidth: 1,
    borderTopColor: "#E0E0E0",
  },
  input: {
    flex: 1,
    backgroundColor: "#F0F0F0",
    borderRadius: 20,
    paddingHorizontal: 15,
    paddingVertical: 10,
    fontSize: 16,
  },
  sendButton: {
    backgroundColor: "#4CAF50",
    paddingHorizontal: 12,
    borderRadius: 20,
    justifyContent: "center",
    alignItems: "center",
    marginLeft: 10,
  },
  sendButtonDisabled: {
    backgroundColor: "#A5D6A7",
  },
  sendButtonText: {
    color: "#FFFFFF",
    fontSize: 24,
  },
  loadingContainer: {
    alignSelf: "flex-start",
    marginBottom: 10,
  },
  loadingText: {
    fontSize: 24,
    color: "#666666",
  },
  emptyContainer: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    height: 500,
  },
  emptyText: {
    fontSize: 16,
    color: "#666666",
    textAlign: "center",
  },
});&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;The most important part of the code above is the following:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code&gt;const { useAIConversation } = createAIHooks(client);&lt;/code&gt; 
  &lt;ul&gt; 
   &lt;li&gt;Creates a react hook to fetch conversation information, listen to messages and send messages to your conversation&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt; &lt;pre&gt;&lt;code class="lang-ts"&gt;const [
  {
    data: { messages },
    isLoading,
  },
  handleSendMessage,
] = useAIConversation("chat");&lt;/code&gt;&lt;/pre&gt; 
  &lt;ul&gt; 
   &lt;li&gt;Listens to the messages and sends messages&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Overall the app will use the retrieved information and show it on the screen as it is retrieved real-time.&lt;/p&gt; 
&lt;p&gt;Now if you deploy your sandbox again, you will see that your application will call the knowledge base with the provided information. Now run the application and see how it looks:&lt;/p&gt; 
&lt;p&gt;&lt;iframe loading="lazy" title="Simulator Screen Recording   iPhone 16   2024 11 19 at 23 05 11" width="500" height="281" src="https://www.youtube-nocookie.com/embed/AZGt7SKKdpE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-scripts allow-same-origin"&gt;&lt;/iframe&gt;&lt;/p&gt; 
&lt;h2&gt;Cleanup&lt;/h2&gt; 
&lt;p&gt;In this blog post you have learned how you can call an LLM through an Amazon Bedrock Knowledge Base. Before wrapping things up, make sure you delete your resources in the Amplify sandbox by running:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="lang-bash"&gt;npx ampx sandbox delete -y&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Also, the vector databases can be expensive, once you are done testing the application, make sure to delete your instances in the &lt;a href="https://console.aws.amazon.com/aos/home?#opensearch/get-started-serverless"&gt;Amazon OpenSearch Serverless&lt;/a&gt; dashboard.&lt;/p&gt; 
&lt;h2&gt;Wrapping Up&lt;/h2&gt; 
&lt;p&gt;In this blog post, you learned how you can create your knowledge base and use it with your application. If you would like to learn more take a look at our &lt;a href="https://docs.amplify.aws/react/ai/set-up-ai/"&gt;starting guide for AI&lt;/a&gt;. In it we go over in detail how to get started with Amplify AI kit.&lt;/p&gt;</content:encoded>
					
		
		
			</item>
	</channel>
</rss>