<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Guillaume Égée</title>
    <description>The latest articles on Forem by Guillaume Égée (@guiyome).</description>
    <link>https://forem.com/guiyome</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F809345%2F4b853497-49c4-42f3-a301-54a9c0e51b05.jpeg</url>
      <title>Forem: Guillaume Égée</title>
      <link>https://forem.com/guiyome</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/guiyome"/>
    <language>en</language>
    <item>
      <title>Free dynDNS for your NAS: auto-update DNS with your latest IP</title>
      <dc:creator>Guillaume Égée</dc:creator>
      <pubDate>Tue, 12 Mar 2024 12:38:00 +0000</pubDate>
      <link>https://forem.com/slsbytheodo/auto-update-dns-a-record-with-the-latest-ip-of-your-nas-5g81</link>
      <guid>https://forem.com/slsbytheodo/auto-update-dns-a-record-with-the-latest-ip-of-your-nas-5g81</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🎉 No more use a costly dynDNS service to update DNS A record when your IP changes!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've just set up a NAS at home and linked a domain name to my router IP. But one day I could no more access to it, because the router IP changed without any notice!&lt;br&gt;
Fortunately, a lot of dynDNS tools exist, but they are often paid services (even if quite cheap, I acknowledge). Why not building your own free dynDNS?&lt;/p&gt;

&lt;p&gt;I created a &lt;a href="https://github.com/guiyom-e/auto-update-ip-aws"&gt;&lt;strong&gt;repository&lt;/strong&gt;&lt;/a&gt; which rely on an AWS CDK stack to update your A record in Amazon Route 53 with an API endpoint. This endpoint can be called by a script whenever your IP changes.&lt;/p&gt;

&lt;p&gt;Suppose you host your own NAS server and want to access it at &lt;code&gt;my-server.com&lt;/code&gt; you have bought on &lt;a href="https://aws.amazon.com/getting-started/hands-on/get-a-domain/"&gt;AWS&lt;/a&gt;. You need to add a A record to make &lt;code&gt;my-server.com&lt;/code&gt; point to your server public IP. In general, if you rely on an Internet provider to get an IP, you are not guaranteed to have a one static, even if in practice they don't often change (at router reboot for instance). If you want to keep your A record up-to-date when your dynamic IP changes, this project is made for you!&lt;/p&gt;

&lt;p&gt;... and it is (almost) free!&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the stack
&lt;/h2&gt;

&lt;p&gt;Just follow the &lt;a href="https://github.com/guiyom-e/auto-update-ip-aws#readme"&gt;installation steps&lt;/a&gt;!&lt;br&gt;
You'll need Node.js, an AWS account and a domain name of course!&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works?
&lt;/h2&gt;

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

&lt;p&gt;The script installed on your NAS will first call an API to retrieve its current IP, on a frequency defined with a cron job.&lt;br&gt;
If this IP has changed, it will call another API endpoint (protected with an API key this time). An express step functions will then update the record in Route53 if the requested IP is the same as the source IP (the actual IP of the server calling the endpoint).&lt;br&gt;
That's all!&lt;/p&gt;

&lt;h2&gt;
  
  
  🤑 Estimated cost
&lt;/h2&gt;

&lt;p&gt;The deployed stack uses AWS resources at really low cost, supposing the &lt;a href="//./scripts/domain-auto-update/update-ip.sh"&gt;script&lt;/a&gt; is run every hour and the IP changes every day:&lt;/p&gt;

&lt;p&gt;Check the IP (every hour)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;API Gateway V2&lt;/em&gt;: ~ $0.00083 = 31 days x 24h x 1.11/million requests. NB: free tier the first year&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Lambda&lt;/em&gt;: ~ $0.00126 / month = 31 days x 24h x 1000 milliseconds x $0.0000000017 (architecture ARM, memory: 128 MB, region eu-west-1, Jan. 2024)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Update the IP (every day)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Step Functions&lt;/em&gt;: ~ $0.033 / month = 31 days x ($0.000001 (price per request, Jan. 2024) + 1000 milliseconds x 64 MB x \$0.00001667 )&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;CloudWatch&lt;/em&gt;: Free tier = 6 months x 31 days x (2 kB (Step Functions) + 500 B (Lambda)) = 0.5 MB &amp;lt; 5 GB of free tier&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;API Gateway V1&lt;/em&gt;: ~ $0.00003 = 31 x 3.50/million requests. NB: free tier the first year&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Route 53&lt;/em&gt; : ~ $0.00001 / month = 31 queries x $0.40 per million queries&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;CloudFormation&lt;/em&gt; : free&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;IAM&lt;/em&gt;: free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TOTAL COST: &lt;strong&gt;&amp;lt; $0.5 per year!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NB: you need to add the domain name cost (~$12 per year (depending on the name chosen) + $0.50 per hosted zone per month).&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dns</category>
      <category>aws</category>
      <category>route53</category>
      <category>stepfunctions</category>
    </item>
    <item>
      <title>Migrating from API Gateway V2 to V1 to get REST API features</title>
      <dc:creator>Guillaume Égée</dc:creator>
      <pubDate>Tue, 13 Feb 2024 17:04:31 +0000</pubDate>
      <link>https://forem.com/slsbytheodo/migrating-from-api-gateway-v2-to-v1-to-get-rest-api-features-nfp</link>
      <guid>https://forem.com/slsbytheodo/migrating-from-api-gateway-v2-to-v1-to-get-rest-api-features-nfp</guid>
      <description>&lt;p&gt;This guide aims to help you migrate a serverless or CloudFormation stack with API Gateway v2 (HTTP API) integrated with lambdas to the same stack using API Gateway v1 (REST API), with a focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handling API paths constraints&lt;/li&gt;
&lt;li&gt;Input and Output serialization (and in particular these breaking changes: JWT authentication, CORS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS REST API has a lot of features that HTTP API do not benefit from: cache at edge, WAF integration, API keys. Even if some features can be circumvented, like using CloudFront for cache at edge, some are not and a migration can be necessary. For a detailed comparison between the two versions, see this guide: &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡 TLDR&lt;/strong&gt;: have a look to this &lt;a href="https://github.com/guiyom-e/sls-apigateway-v2-to-v1" rel="noopener noreferrer"&gt;repo&lt;/a&gt; for Typescript types, helpers to format API responses and a serverless template with the use of both API versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  🗜️ API path constraints are different
&lt;/h2&gt;

&lt;h3&gt;
  
  
  In REST API, each part of an API path is a resource
&lt;/h3&gt;

&lt;p&gt;In REST API, paths must be defined once, because each path part is a resource. This is not an issue with HTTP API.&lt;/p&gt;

&lt;p&gt;If you need to deploy &lt;code&gt;/actions/list&lt;/code&gt; and &lt;code&gt;/actions/toggle&lt;/code&gt;, you need to deploy 3 &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-resource.html" rel="noopener noreferrer"&gt;ApiGateway Resource&lt;/a&gt;, &lt;code&gt;/action&lt;/code&gt;, &lt;code&gt;/list&lt;/code&gt; and &lt;code&gt;/toggle&lt;/code&gt;, one for each path.&lt;br&gt;
If you define &lt;code&gt;/action&lt;/code&gt; twice, you will get &lt;code&gt;409 Conflict "Another resource with the same parent already has this name: actions"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With Serverless framework, it is done for you, except if you deploy different services with a path in common: in this case, you have no choice but to rename it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Variables in paths are managed differently
&lt;/h3&gt;

&lt;p&gt;Let’s say you have defined two routes &lt;code&gt;/network/{networkId}/actions/list&lt;/code&gt; and &lt;code&gt;/network/{userId}/profile&lt;/code&gt; in the HTTP API. Even if we can challenge this api path structure, it is possible with HTTP API… but not with REST API, because only one variable path part name is allowed.&lt;/p&gt;

&lt;p&gt;In CloudFormation, you should encounter this error: &lt;code&gt;"A sibling ({networkId}) of this resource already has a variable path part -- only one is allowed"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two options: either rename &lt;code&gt;userId&lt;/code&gt; to &lt;code&gt;networkId&lt;/code&gt; (if it makes sense 🤔) or change the path!&lt;/p&gt;
&lt;h2&gt;
  
  
  ➡️ Input is different
&lt;/h2&gt;

&lt;p&gt;There are two versions of the input payload, &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html" rel="noopener noreferrer"&gt;Payload 1.0 and Payload 2.0&lt;/a&gt;. For Typescript users, I wrote the &lt;a href="https://github.com/guiyom-e/sls-apigateway-v2-to-v1/blob/main/src/apiPayload.ts" rel="noopener noreferrer"&gt;payload types&lt;/a&gt;, as they are not exposed in AWS SDK.&lt;/p&gt;
&lt;h3&gt;
  
  
  Handling headers and query strings with multiple values
&lt;/h3&gt;

&lt;p&gt;With API Gateway v2, query strings and headers can have multiple values for the same key. In this case, values are comma-separated in the input object &lt;code&gt;headers&lt;/code&gt; or &lt;code&gt;queryStringParameters&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Payload 1.0 (REST API)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;headers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;singleValueParam&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multiValueHeaders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multiValueParam&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Payload 2.0 (HTTP API)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;headers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;singleValueParam&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multiValueParam&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value1,value2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authentication is different... and Cognito groups are formatted differently!
&lt;/h3&gt;

&lt;p&gt;If you use a Cognito authorizer, you can find similar keys in both payloads... but they are not the same!&lt;/p&gt;

&lt;p&gt;For instance for a user in Cognito groups "group1" and "group2":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Payload 1.0 (REST API)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requestContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorizer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claims&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cognito:groups&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;group1,group2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cognito:username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bob@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bob@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Payload 2.0 (HTTP API)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requestContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorizer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jwt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claims&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cognito:groups&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[group1 group2]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cognito:username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bob@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bob@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, Cognito groups must be parsed differently!&lt;/p&gt;

&lt;h3&gt;
  
  
  Other parameters are not at the same place...
&lt;/h3&gt;

&lt;p&gt;.. but I won't go through other differences, but they are a lot that can be easily found in &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html" rel="noopener noreferrer"&gt;these types&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔀 In REST API, CORS are handled at method level
&lt;/h2&gt;

&lt;p&gt;In HTTP API, a global configuration can be set ; in REST API it is at method level.&lt;br&gt;
So when moving to REST API, you need to edit every method to add CORS for preflight requests. And for other requests, you need to update the output of the lambda manually (see next section).&lt;/p&gt;

&lt;h2&gt;
  
  
  ⬇️ Output is different, even if a Lambda integration is chosen
&lt;/h2&gt;

&lt;p&gt;With HTTP API, if no &lt;code&gt;statusCode&lt;/code&gt; key is defined, the output payload is automatically stringified to a response body in a HTTP response with  status 200 (see &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html#http-api-develop-integrations-lambda.v2" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;)&lt;br&gt;
. This is not the case in REST API, so you need to format your output manually. For an easier migration, you can set up a middleware based on this &lt;a href="https://github.com/guiyom-e/sls-apigateway-v2-to-v1/blob/main/src/apiResponseFormatter.ts" rel="noopener noreferrer"&gt;code&lt;/a&gt;, or if you use &lt;code&gt;@middy&lt;/code&gt; middleware, based on this &lt;a href="https://github.com/guiyom-e/sls-apigateway-v2-to-v1/blob/main/src/apiResponseFormatterMiddleware.ts" rel="noopener noreferrer"&gt;code&lt;/a&gt;.&lt;br&gt;
These helpers also add CORS headers, as this is not handled by REST API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally, should I move to REST API?
&lt;/h2&gt;

&lt;p&gt;In short, the REST API comes with more features, but is more complex whereas HTTP API is a &lt;a href="https://aws.amazon.com/api-gateway/pricing/" rel="noopener noreferrer"&gt;three-times cheaper&lt;/a&gt; and turnkey solution, especially if you use a JWT authentication and need CORS headers. Moving from one API to another is clearly possible, but painful for a big application with multiple services sharing the same API Gateway.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>apigateway</category>
      <category>serverless</category>
      <category>lambda</category>
    </item>
    <item>
      <title>6 guidelines for risk-less data migrations</title>
      <dc:creator>Guillaume Égée</dc:creator>
      <pubDate>Wed, 13 Dec 2023 13:25:19 +0000</pubDate>
      <link>https://forem.com/slsbytheodo/6-guidelines-for-risk-less-data-migrations-3idl</link>
      <guid>https://forem.com/slsbytheodo/6-guidelines-for-risk-less-data-migrations-3idl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“The majority of project issues I have seen come from databases, whatever is the technology”&lt;/em&gt; said one day one my experienced engineering manager...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why I have built 6 guidelines I try to follow when modifying data on tech projects. They are especially helpful when working with databases like DynamoDB with few tooling (no ORM, etc.).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Data is key in business… and it is where it often fails.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Almost every app has a &lt;strong&gt;database&lt;/strong&gt; with more or less structured data. These data items may evolve during app development, with the addition of new fields, changes to field structure, data updates, or removal of fields. These evolutions, often referred to as &lt;strong&gt;migrations&lt;/strong&gt;, are critical as they enable the app to evolve but can also introduce bugs if not executed properly.&lt;br&gt;
A data migration &lt;strong&gt;plan&lt;/strong&gt; is a perfect tool to mitigate the risks of these operations, as it avoids common pitfalls and ensures reliability.  &lt;/p&gt;

&lt;h2&gt;
  
  
  📋️ First thing: have a migration protocol defined
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Define a protocol to follow in advance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define &lt;strong&gt;who&lt;/strong&gt; is responsible of what. You can use a responsibility matrix (&lt;a href="https://en.wikipedia.org/wiki/Responsibility_assignment_matrix"&gt;&lt;strong&gt;RACI&lt;/strong&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Define &lt;strong&gt;when&lt;/strong&gt; the migration should be planned&lt;/li&gt;
&lt;li&gt;Define &lt;strong&gt;what&lt;/strong&gt; should be done in precise steps (manually deploy a function, trigger a Continuous Deployment workflow, etc.) ?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Follow the protocol
&lt;/h3&gt;

&lt;p&gt;A simple advice... to avoid acting in panic if something goes wrong…&lt;/p&gt;

&lt;h3&gt;
  
  
  Upgrade the protocol with your learnings
&lt;/h3&gt;

&lt;p&gt;There are often recurring patterns in migrations: making a field non nullable, updating a data type, etc. To capitalize on them, build a list of common migrations with explanations of the best implementation strategy based on past experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  ↔️ Have a strategy to handle the transition state
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Estimate the duration and load&lt;/strong&gt; (scalability) of the migration process, in all environments
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;What specific cases should be anticipated in production?

&lt;ul&gt;
&lt;li&gt;What differences should be taken into account when estimating the migration sizing (database size, environment configuration)?&lt;/li&gt;
&lt;li&gt;Are there API/database quotas that you should plan for?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Apply a margin for the manual parts (launching the migration, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Migration strategies
&lt;/h3&gt;

&lt;p&gt;Given that estimation and business concerns, choose between these &lt;strong&gt;2 main strategies&lt;/strong&gt;:&lt;/p&gt;

&lt;h4&gt;
  
  
  💥 &lt;strong&gt;“Big Bang” Migration&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Only one version of a migration set (pieces of data to modify) can exist at application uptime.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plan a &lt;strong&gt;service interruption&lt;/strong&gt; when data will be unavailable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✍️ Example of a user-friendly service interruption: during the migration process, the frontend application displays a "maintenance banner", and the backend is programmatically locked, to ensure no side-effect can corrupt data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✳️ Pros: quicker method, advised when building a project with low risks if some data is lost/corrupted&lt;/li&gt;
&lt;li&gt;⚠️ Risks:

&lt;ul&gt;
&lt;li&gt;possibly long downtime,&lt;/li&gt;
&lt;li&gt;risk of database throttling (in case of naive parallel read/write implementation for instance)&lt;/li&gt;
&lt;li&gt;more difficult to write migrations with a reliable rollback&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  🌊 &lt;strong&gt;Migration in two (or more) steps&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Handle old and new versions inside the migration set.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data migration and new code deployment must be uncoupled and done in the proper order.&lt;/li&gt;
&lt;li&gt;Examples :

&lt;ul&gt;
&lt;li&gt;if you &lt;strong&gt;remove&lt;/strong&gt; a column (or a table, etc.), remove the code using this column and then migrate the data&lt;/li&gt;
&lt;li&gt;if you &lt;strong&gt;add&lt;/strong&gt; a column, migrate the data first and then deploy the code using it&lt;/li&gt;
&lt;li&gt;if you &lt;strong&gt;update&lt;/strong&gt; a column, add a new column and, after it has been fulfilled progressively, remove the old column&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;💡 You can run integration tests with both versions to ensure the application works in both cases&lt;/li&gt;
&lt;li&gt;✳️ Pros: reliable method, that is error proof (if the migration fails, the system can continue running without interruption)&lt;/li&gt;
&lt;li&gt;⚠️ Risks:

&lt;ul&gt;
&lt;li&gt;more complex to develop&lt;/li&gt;
&lt;li&gt;longer to fulfill completely&lt;/li&gt;
&lt;li&gt;risk of maintaining multiple versions over a long period of time.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔂 Make a plan reproducible in multiple environments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Write idempotent migrations (i.e. a migration can be applied multiple times with the same result)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;There are many reasons for needing to re-apply a migration: migration partially completed, stopped or failed, etc.&lt;/li&gt;
&lt;li&gt;The migration script should work without any state-full input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✍️ Simple example of &lt;strong&gt;idempotent migrations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Bad&lt;/strong&gt;:   if you run the migration twice, the data will be corrupted.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform_migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;not_idem_potent_migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_version&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
          &lt;span class="nf"&gt;perform_migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅  &lt;strong&gt;Good&lt;/strong&gt;: even if the migration itself (incrementation) is not idem-potent, it is not possible to run it twice with this migration.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;idem_potent_migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;new_version&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nf"&gt;perform_migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Save the migration scripts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Committing migration scripts ensures the same process can be applied to another environment easily and with known and reproducible steps.&lt;/li&gt;
&lt;li&gt;In case of error, it will help understanding what happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Test your scenario
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Test the migration script (with unit tests) to verify its functionality.

&lt;ul&gt;
&lt;li&gt;Check that data items of the migration set are correctly selected&lt;/li&gt;
&lt;li&gt;Check all edge cases&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Use dedicated environments to test the migration plan

&lt;ul&gt;
&lt;li&gt;Non-production environments can help to find bugs, all the more if data is ISO-prod.&lt;/li&gt;
&lt;li&gt;If it is not possible to test a scenario with production data, you can at least run scripts to check for errors, or run migrations in dry-run&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📆 The plan includes communication with stakeholders
&lt;/h2&gt;

&lt;p&gt;Running a migration can have impacts on the underlying business, so it should not remain in the technical sphere, but be shared to stakeholders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communicate to business owners and stakeholders
&lt;/h3&gt;

&lt;p&gt;Explicit the risks of the migration and consider their concerns&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stakeholders should be involved in the decision to migrate data, as they know the system and are responsible of it.&lt;/li&gt;
&lt;li&gt;They may help find edge cases in the migration, like &lt;strong&gt;dependencies to other teams&lt;/strong&gt;, a &lt;strong&gt;specific business case&lt;/strong&gt; not handled, etc.&lt;/li&gt;
&lt;li&gt;✍️❌ &lt;strong&gt;Common pitfall&lt;/strong&gt;: Delete a deprecated column that is still used by another team&lt;/li&gt;
&lt;li&gt;✍️✅ &lt;strong&gt;Good practice&lt;/strong&gt;: Continue supporting legacy versions until they are no longer in use, and establish a deadline to cease support.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose the right moment to run the migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✍️❌ &lt;strong&gt;Common pitfall&lt;/strong&gt;: Run a resource-consuming migration during peak hours (example: adding a non-nullable column of a table in PostgreSQL that will lock it).&lt;/li&gt;
&lt;li&gt;✍️✅ &lt;strong&gt;Good practice&lt;/strong&gt;: Run a “big bang” migration just after the deployment to reduce the service interruption lead time. Run it during off-peak hours and when a development team is available in case of error.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ↩️ A rollback strategy is defined
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backup your data and practice restoring it
&lt;/h3&gt;

&lt;p&gt;If anything goes wrong, you should be &lt;em&gt;able to restore&lt;/em&gt; your database to a correct state.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud providers have services dedicated to backups (e.g. &lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/whatisbackup.html"&gt;AWS Backup&lt;/a&gt; if you are using AWS, &lt;a href="https://www.actifio.com/"&gt;Actifio&lt;/a&gt; on GCP, etc.) and database systems often come with their own backup solution.&lt;/li&gt;
&lt;li&gt;Practice restoring your data&lt;/li&gt;
&lt;li&gt;Be pragmatic. Can you afford to restore your data?

&lt;ul&gt;
&lt;li&gt;Some data items might have been updated during the process&lt;/li&gt;
&lt;li&gt;Is it worth to rollback, if only a few items have an issue?&lt;/li&gt;
&lt;li&gt;What is the risk of a data loss?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Write a “down” migration, as well as the “up” migration… or at least a Plan B
&lt;/h3&gt;

&lt;p&gt;How many times I've heard (or said) "It won't fail, no need of plan B"... and it finally failed ?&lt;br&gt;
I won't go through implementation details here (and a lot of ORMs have dedicated tools for this), the main point is "Don't be overconfident"!&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 The plan includes a check that everything is ok in the end
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Keep track of migration states with versioning
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If possible, save the changes and the version of items during the migration process.&lt;/li&gt;
&lt;li&gt;Keep track of migration applications in each environment.&lt;/li&gt;
&lt;li&gt;The versions must be strictly increasing (in a specific order relationship) and deterministic to be able to compare versions. For instance, with an incremented integer or a timestamp and a reference to the previous version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Remove data after complete validation only
&lt;/h3&gt;

&lt;p&gt;Although, this does not guarantee that you will end up with a successful migration after the removal operation, this allows you to detect potential issues you have not forecast and facilitate reverting the changes.&lt;/p&gt;

&lt;p&gt;✍️ E.g. only remove ‘address’ after you have correctly added ‘street_name’, ‘city’, ‘postcode’.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check that everything is ok &lt;strong&gt;after&lt;/strong&gt; the migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audit the database after migration&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Use a monitoring system to keep track of new errors.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Not seeing code or monitoring issue do not mean there is no issue ! Check it with &lt;strong&gt;business owners&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You can set-up end-to-end tests to avoid regressions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌟 What to remember?
&lt;/h2&gt;

&lt;p&gt;These 6 guidelines are just an attempt to sum-up what to care about when applying data migrations. They can also apply to application deployments or whatever operation that introduces a breaking change. But the main learning could also be &lt;strong&gt;capitalize on knowledge&lt;/strong&gt; to avoid reproducing the same mistakes !&lt;/p&gt;

&lt;p&gt;Want to share your own tips or tech convictions? Don't hesitate to comment on this post 😉&lt;/p&gt;

</description>
      <category>database</category>
      <category>dynamodb</category>
    </item>
    <item>
      <title>Deploying Next.js 13 with Amplify CDK</title>
      <dc:creator>Guillaume Égée</dc:creator>
      <pubDate>Thu, 12 Jan 2023 12:28:27 +0000</pubDate>
      <link>https://forem.com/slsbytheodo/deploying-nextjs-13-with-amplify-cdk-1dgd</link>
      <guid>https://forem.com/slsbytheodo/deploying-nextjs-13-with-amplify-cdk-1dgd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📜 How to deploy a Next.js app step by step with an Amplify CDK construct, avoiding you all the pains.&lt;/p&gt;

&lt;p&gt;🐝 TLDR: an example app has just been added in &lt;a href="https://github.com/swarmion/swarmion/pull/433"&gt;&lt;code&gt;swarmion&lt;/code&gt;&lt;/a&gt;, so you can now bootstrap a ready-to-use Next.js project!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; is a popular React framework to deploy web apps with advanced features like Server Side Rendering (SSR), Image optimization or Incremental Static Regeneration (ISR). In my last project, I chose this framework notably to ensure a &lt;a href="https://nextjs.org/learn/seo/introduction-to-seo"&gt;good SEO&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To deploy the app, I had the requirement to use AWS as a cloud-provider, with a limited budget, so neither Vercel (a safe choice, as it is the company which develops Next.js), nor a containerized solution like the managed AWS ECS Fargate service could satisfy my needs. Then, I started with a serverless hosting thanks to the &lt;a href="https://github.com/serverless-nextjs/serverless-next.js"&gt;serverless-next&lt;/a&gt; project, which provides a serverless plugin or a CDK construct to deploy a Next.js stack. However, this project is no more well-maintained and new Next.js features (like middlewares, etc.) are not supported.&lt;/p&gt;

&lt;p&gt;Here comes AWS Amplify which has just announced on November 17th to &lt;a href="https://aws.amazon.com/fr/about-aws/whats-new/2022/11/aws-amplify-hosting-support-next-js-12-13"&gt;support Next.js 12 and 13&lt;/a&gt; 🎉! Amplify is an AWS service to build and host full-stack applications. It uses serverless services under the hood. I tested it with &lt;a class="mentioned-user" href="https://dev.to/mamadoudicko"&gt;@mamadoudicko&lt;/a&gt; and &lt;a class="mentioned-user" href="https://dev.to/alexandreperni4"&gt;@alexandreperni4&lt;/a&gt; and we will explain you how to setup a production-ready app!&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Amplify's AWS CDK
&lt;/h2&gt;

&lt;p&gt;AWS CDK, for AWS Cloud Development Kit, is a multi-languages framework for writing infrastructure as code and deploying it through AWS CloudFormation. Our examples will be written in Typescript, but other languages like Python, Java, C# or Go can be used as well.&lt;/p&gt;

&lt;p&gt;To deploy with the AWS CDK, you would first need to declare a &lt;code&gt;cdk.json&lt;/code&gt; file at the root of your frontend repository. This allows you to tell the AWS CDK where is the entry point of the CDK app, &lt;code&gt;hosting/bin.ts&lt;/code&gt; here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm ts-node hosting/bin.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to install these dev dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; aws-cdk-lib
pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; @aws-cdk/aws-amplify-alpha
pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; constructs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will also need some configuration files to bootstrap the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.nvmrc&lt;/code&gt; file to define your node version&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tsconfig.json&lt;/code&gt; TypeScript configuration file, with &lt;code&gt;target&lt;/code&gt; version set to "es6" or more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;code&gt;hosting&lt;/code&gt; folder, you then need to add a &lt;code&gt;bin.ts&lt;/code&gt; file to declare the &lt;code&gt;cdk&lt;/code&gt; app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AmplifyStack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AmplifyStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NextJsSampleStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cloudformation stack containing the Amplify configuration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing fancy at this point, the interesting part is in the &lt;code&gt;./stack.ts&lt;/code&gt; configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-cdk/aws-amplify-alpha&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aws_iam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BuildSpec&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-codebuild&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AmplifyStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Define Amplify app&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amplifyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AmplifyAppResource&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NextJS app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My NextJS APP deployed with Amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="c1"&gt;// ⬇️ configuration items to be defined ⬇️&lt;/span&gt;
      &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;sourceCodeProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;buildSpec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;autoBranchCreation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;autoBranchDeletion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;environmentVariables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// ⬆️ end of configuration ⬆️&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Attach your main branch and define the branch settings (see below)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mainBranch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amplifyApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addBranch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;autoBuild&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// set to true to automatically build the app on new pushes&lt;/span&gt;
      &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PRODUCTION&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;appId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amplifyApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s go through what we need for the different variables within the configuration object.&lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Step-by-step guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Define a role to Amplify (&lt;code&gt;role&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This is needed to add a custom role that will be assumed by the Amplify resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ServicePrincipal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-iam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AmplifyRoleWebApp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amplify.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom role permitting resources creation from Amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;managedPolicies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AdministratorAccess-Amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connection to your Github repository (&lt;code&gt;sourceCodeProvider&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;sourceCodeProvider&lt;/code&gt; configuration allows Amplify to access the source code of your application. To connect a Github repository, you can use the declaration below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GitHubSourceCodeProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-cdk/aws-amplify-alpha/lib/source-code-providers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SecretValue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sourceCodeProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;GitHubSourceCodeProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// GitHub token should be saved in a secure place, we recommend AWS Secret Manager:&lt;/span&gt;
  &lt;span class="na"&gt;oauthToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SecretValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secretsManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GITHUB_TOKEN_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// replace GITHUB_TOKEN_KEY by the name of the Secrets Manager resource storing your GitHub token&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;user name of the GitHub repository owner&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;name of the Github repository&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get a Github token, go to your Github account develop settings and generate a personal access token. Amplify will need quite high access rights to your repository as it will need to generate ssh keys to clone the repository.&lt;/p&gt;

&lt;p&gt;To store the token in AWS, we recommend to use the &lt;a href="https://aws.amazon.com/secrets-manager"&gt;AWS Secret Manager&lt;/a&gt; service. Be careful to choose a plain text secret (in the AWS console select "Store a new secret", "Other type of secret" and finally "Plaintext"). Choose a key name that matches the key used in the &lt;code&gt;GitHubSourceCodeProvider&lt;/code&gt; construct (&lt;code&gt;GITHUB_TOKEN_KEY&lt;/code&gt; in this example).&lt;/p&gt;

&lt;h3&gt;
  
  
  Build settings (&lt;code&gt;buildSpec&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Then, you need to define how the app will be built. Below is an example with &lt;code&gt;pnpm&lt;/code&gt; package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BuildSpec&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-codebuild&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environmentVariables&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./environmentVariables&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildSpec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BuildSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromObjectToYaml&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;applications&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;preBuild&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="c1"&gt;// Install the correct Node version, defined in .nvmrc&lt;/span&gt;
              &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nvm install&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nvm use&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="c1"&gt;// Install pnpm&lt;/span&gt;
              &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;corepack enable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;corepack prepare pnpm@latest --activate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="c1"&gt;// Avoid memory issues with node&lt;/span&gt;
              &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;export NODE_OPTIONS=--max-old-space-size=8192&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="c1"&gt;// Ensure node_modules are correctly included in the build artifacts&lt;/span&gt;
              &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pnpm install&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="c1"&gt;// Allow Next.js to access environment variables&lt;/span&gt;
              &lt;span class="c1"&gt;// See https://docs.aws.amazon.com/amplify/latest/userguide/ssr-environment-variables.html&lt;/span&gt;
              &lt;span class="s2"&gt;`env | grep -E '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;environmentVariables&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;' &amp;gt;&amp;gt; .env.production`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="c1"&gt;// Build Next.js app&lt;/span&gt;
              &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pnpm next build --no-lint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;baseDirectory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example is using &lt;code&gt;pnpm&lt;/code&gt;, but you can choose to use any other package manager, such as &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt;. A few things to note here :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting the &lt;code&gt;--max-old-space-size&lt;/code&gt; node option is important to prevent out of memory (OOM) errors while building your application when it reaches a certain size.&lt;/li&gt;
&lt;li&gt;To let your server-side code access your environment variables, you have to include them in a .env.production file. This allows you to put secrets known only from your CI environment in the .env.production file created on the fly.&lt;/li&gt;
&lt;li&gt;The artifacts section is very important as it configures which files from the build step will be included in the artifacts and thus available at run time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Auto-deploy when remote branches satisfying a certain name pattern are created
&lt;/h3&gt;

&lt;p&gt;Amplify allow to create a dedicated environment when you push to a branch with a specific name pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AutoBranchCreation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-cdk/aws-amplify-alpha&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;autoBranchCreation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AutoBranchCreation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;autoBuild&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feature/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;pullRequestPreview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might want to disable the feature in production. For this, simply use &lt;code&gt;undefined&lt;/code&gt; instead of this configuration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Note that this environment can be automatically removed once the branch is deleted if the parameter &lt;code&gt;autoBranchDeletion&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Configuring environment variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environmentVariables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// https://docs.aws.amazon.com/amplify/latest/userguide/build-settings.html#enable-diff-deploy&lt;/span&gt;
  &lt;span class="na"&gt;AMPLIFY_DIFF_DEPLOY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Environment variables can be defined globally or in branch environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Setting up a domain
&lt;/h3&gt;

&lt;p&gt;You can add the domain configuration directly in the &lt;code&gt;stack.ts&lt;/code&gt; file, to have access to the &lt;code&gt;amplifyApp&lt;/code&gt; and &lt;code&gt;mainBranch&lt;/code&gt; variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amplifyApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-domain.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;autoSubdomainCreationPatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feature/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;enableAutoSubdomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to map it with a specific branch by adding the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mapRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mainBranch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 If the domain is already defined in AWS Route 53 service, it will be automatically linked to your stack: AWS will add the appropriate DNS records in the hosted zone. Otherwise, you will need to add the records manually in your domain name provider.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;Almost there! Before being able to detect and deploy Next.js, Amplify needs to be configured with the platform type &lt;code&gt;WEB_COMPUTE&lt;/code&gt;. It needs to be done after Amplify app deployment. As there is no option in Cloudformation for now, the best way to do it programmatically is to use a Custom Resource construct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AwsCustomResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AwsCustomResourcePolicy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/custom-resource&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Set Amplify platform type to WEB_COMPUTE&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsCustomResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AmplifySetPlatform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;onUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;updateApp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amplifyApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WEB_COMPUTE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;physicalResourceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PhysicalResourceId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AmplifyCustomResourceSetPlatform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AwsCustomResourcePolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromSdkCalls&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;amplifyApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 This construct is equivalent to a call to AWS SDK, that can also be written with an &lt;code&gt;aws&lt;/code&gt; CLI command:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws amplify update-app &lt;span class="nt"&gt;--app-id&lt;/span&gt; &amp;lt;yourAppID&amp;gt; &lt;span class="nt"&gt;--platform&lt;/span&gt;  WEB_COMPUTE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, to deploy your AWS CDK app, navigate to the folder containing the &lt;code&gt;cdk.json&lt;/code&gt; file, then enter the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm cdk bootstrap &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pnpm cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the deployment ends successfully, the id of your Amplify app will be printed in your command line interface.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ This step only deploys an Amplify stack, but it does not build nor deploy the Next.js app yet! To do so, you can either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set the &lt;code&gt;autoBuild&lt;/code&gt; parameter to &lt;code&gt;true&lt;/code&gt; in a branch setting to deploy on GitHub pushes (or other source provider)&lt;/li&gt;
&lt;li&gt;Use a webhook as explained below.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adding a webhook
&lt;/h3&gt;

&lt;p&gt;In many cases, a dedicated CI/CD platform is used to test and deploy an app. Then, the direct GitHub integration that triggers an Amplify build at each push on a particular branch is no more sufficient. So you may want to trigger a deployment directly in a CD step. Amplify allows to trigger a deployment with webhooks. This is not yet configurable through the CDK but you can use the Amplify CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws amplify create-webhook &lt;span class="nt"&gt;--app-id&lt;/span&gt; &amp;lt;yourAppID&amp;gt; &lt;span class="nt"&gt;--branch-name&lt;/span&gt; &amp;lt;yourBranchName&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/amplify/create-webhook.html"&gt;https://awscli.amazonaws.com/v2/documentation/api/latest/reference/amplify/create-webhook.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In your CD, add &lt;code&gt;curl [webhook url]&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Alternatively, you can use a Custom Resource construct to create your webhook:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a webhook to use in your proper CI/CD&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webhookCustomResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsCustomResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AmplifyWebhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;onUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createWebhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amplifyApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;branchName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;physicalResourceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PhysicalResourceId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AmplifyCustomResourceWebhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AwsCustomResourcePolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromSdkCalls&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amplifyApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/webhooks/*`&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Outputs the secret deployment webhook&lt;/span&gt;
&lt;span class="nx"&gt;webhookCustomResource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getResponseField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webhook.webhookUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other interesting features
&lt;/h3&gt;

&lt;p&gt;Other configurations are available: custom response headers, custom rewrites and redirects or even basic authentication to prevent access to any test environment!&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Caveats with a monorepo
&lt;/h2&gt;

&lt;p&gt;In a monorepo, additional configuration is required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;appRoot&lt;/code&gt; property in the build settings, with the frontend directory path.&lt;/li&gt;
&lt;li&gt;Ensure &lt;code&gt;node_modules&lt;/code&gt; are correctly included in the build. In the context of a monorepo, most package managers will put the &lt;code&gt;node_modules&lt;/code&gt; at the root of your repository. A nice workaround, with &lt;code&gt;pnpm&lt;/code&gt;, is to set the virtual store directory inside the frontend directory when installing the project: &lt;code&gt;pnpm install --virtual-store-dir [your frontend directory]/node_modules/.pnpm&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See &lt;a href="https://github.com/swarmion/swarmion/pull/433"&gt;swarmion&lt;/a&gt; full implementation example.&lt;/p&gt;

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

&lt;p&gt;If you plan to go on a monorepo architecture, you can already use &lt;a href="https://www.swarmion.dev"&gt;Swarmion&lt;/a&gt; to create high scalable serverless application including Next.js latest version by simply running: &lt;code&gt;pnpm create swarmion-app&lt;/code&gt; and choosing the Next.js option.&lt;/p&gt;

&lt;p&gt;Swarmion will pre-configure all required settings so that your app will smoothly be deployed to amplify.&lt;/p&gt;

&lt;p&gt;To complete, there are other Next.js deployment solutions which may be interesting to explore like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vercel.com/guides/deploying-nextjs-with-vercel"&gt;Vercel&lt;/a&gt; (Next.js core team): probably the most user friendly infrastructure solution, if you can afford it on your project.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/serverless-nextjs/serverless-next.js#features"&gt;serverless-nextjs&lt;/a&gt;: very interesting initiative but seems no longer maintained unfortunately. The resources declared in this library are very similar to the one deployed by Amplify under the hood, which probably made it the cheapest solution around for a while.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://open-next.js.org/"&gt;OpenNext project&lt;/a&gt; which uses the &lt;a href="https://docs.sst.dev/constructs/NextjsSite"&gt;NetJSite SST construct&lt;/a&gt;. A promising project, but still a work in progress. This one focuses on the connector between the Next.js framework and the infrastructure, but will not provide the infrastructure as code template.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jetbridge/cdk-nextjs"&gt;JetBridge cdk-nextjs construct&lt;/a&gt;, another proposal to deploy Next.js with a construct.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks a lot to &lt;a class="mentioned-user" href="https://dev.to/mamadoudicko"&gt;@mamadoudicko&lt;/a&gt;, &lt;a class="mentioned-user" href="https://dev.to/alexandreperni4"&gt;@alexandreperni4&lt;/a&gt; and &lt;a href="https://github.com/apollisa"&gt;@Antoine Apollis&lt;/a&gt; who co-wrote this article and tested the setup process in this &lt;a href="https://github.com/alexandrepernin/nextjs-amplify"&gt;repository&lt;/a&gt;! We also relied a lot on this &lt;a href="https://aws.amazon.com/blogs/mobile/deploy-a-nextjs-13-application-to-amplify-with-the-aws-cdk/"&gt;AWS blog article&lt;/a&gt; to build this guide.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>nextjs</category>
      <category>amplify</category>
    </item>
  </channel>
</rss>
