<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anaïs Schlienger</title>
    <description>The latest articles on DEV Community by Anaïs Schlienger (@schliengeranais).</description>
    <link>https://dev.to/schliengeranais</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%2F1081276%2F71257e73-d4dc-4b24-b50c-72501743074b.jpeg</url>
      <title>DEV Community: Anaïs Schlienger</title>
      <link>https://dev.to/schliengeranais</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/schliengeranais"/>
    <language>en</language>
    <item>
      <title>Avoiding API Gateway’s integrations hard limit: scaling serverless architectures efficiently</title>
      <dc:creator>Anaïs Schlienger</dc:creator>
      <pubDate>Tue, 26 Nov 2024 07:00:00 +0000</pubDate>
      <link>https://dev.to/slsbytheodo/avoiding-api-gateways-integrations-hard-limit-scaling-serverless-architectures-efficiently-49on</link>
      <guid>https://dev.to/slsbytheodo/avoiding-api-gateways-integrations-hard-limit-scaling-serverless-architectures-efficiently-49on</guid>
      <description>&lt;p&gt;Avoid AWS API Gateway’s integration limit with efficient multi-API Gateway setups. Learn how to scale serverless projects!&lt;/p&gt;

&lt;p&gt;Last week, I was coding a new feature. It was looking good, until I had to deploy. AWS cli yelled at me and refused to deploy my stack. I got the following error : &lt;code&gt;CREATE_FAILED: HttpApiIntegration (AWS::ApiGatewayV2::Integration) Maximum number of Integrations for this API has been reached.&lt;/code&gt; Did this ever happen to you? If it did not, and you are using AWS, then it might be worth to run a quick sanity check on your architecture.&lt;/p&gt;

&lt;p&gt;AWS combined with API Gateway is widely used for scale-up apps. If API Gateway can handle a lot of routes (there is a 300 quota that can be increased), it has however a lesser-known but critical &lt;a href="https://www.alexdebrie.com/posts/api-gateway-elements/#api-gateway-integration-passthrough-behavior" rel="noopener noreferrer"&gt;limitation of &lt;strong&gt;300 integrations per API Gateway instance&lt;/strong&gt;.&lt;/a&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An API Gateway integration is a connection between an API Gateway and a backend service, such as a Lambda function, an HTTP endpoint, or an AWS service, allowing the API Gateway to route incoming requests to the appropriate service for processing and return responses to the client. An endpoint can have only one integration. Here is a &lt;a href="https://www.alexdebrie.com/posts/api-gateway-elements/#api-gateway-integration-passthrough-behavior" rel="noopener noreferrer"&gt;great article about integrations.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a non-monolithic serverless architecture, where every Lambda function represents a different endpoint, it’s easy to hit this limit once your app grows. This was exactly what happened to me in a large-scale serverless project using the Serverless Framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The challenge: reaching API Gateway’s 300 integration limit&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s represent my project as an online library, where users can read, buy, lend and comment on books. The backend is split into two services, &lt;code&gt;users&lt;/code&gt; and &lt;code&gt;books&lt;/code&gt;, representing the core features.&lt;/p&gt;

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

&lt;p&gt;Our architecture, typical of many non-monolithic setups, follows a one Lambda per endpoint model. When you have multiple services each exposing dozens of endpoints, it doesn’t take long to bump against the 300 integration per API limit (in this simplified example with only 2 services, it might take a long time I agree). This bottleneck emerged (I was not aware of it before!) when our API maxed out its available integrations and simply refused to deploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Evaluating the options: lambda-lith vs micro-APIs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;With an architecture with all services living behind the same API Gateway, we faced two clear paths forward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Split the API into smaller chunks (micro-APIs).&lt;/li&gt;
&lt;li&gt;Shift to a more monolithic Lambda setup, where multiple endpoints share a Lambda.&lt;/li&gt;
&lt;/ol&gt;

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

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

&lt;p&gt;Going down the monolith route would have required significant changes to our architecture, a large refactor, and a fair amount of coordination with the front-end. This wasn't ideal for us since we wanted minimal disruption to our existing setup and quick implementation. So we opted for the second option: splitting the API into smaller chunks by creating a new API Gateway for each service.&lt;/p&gt;

&lt;p&gt;This approach allowed us to break our system into manageable micro-services while keeping our frontend mostly unchanged.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The solution: multi-API Gateways behind CloudFront&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The architecture change was made easier by the fact that we had CloudFront in front of our API Gateway. Our frontend communicates with CloudFront, and CloudFront handles routing requests to our API. &lt;/p&gt;

&lt;p&gt;This setup meant that adding new API Gateways could be done without any drastic changes to the frontend. All we needed to do was redirect calls to the appropriate API Gateway, based on the service prefix.&lt;/p&gt;

&lt;p&gt;Here’s how we did it step-by-step:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: create a new API Gateway for the  &lt;code&gt;user&lt;/code&gt; service&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We began by creating a new API Gateway for the &lt;code&gt;user&lt;/code&gt; service. Since this was essentially a new API, we also had to configure a new &lt;code&gt;Cognito&lt;/code&gt; authorizer to handle authentication for this service.&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;serverlessConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AwsConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Serverless&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;servcice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;allowedOrigins&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;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,],&lt;/span&gt;
          &lt;span class="na"&gt;allowedHeaders&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;Content-Type&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;Authorization&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;Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;allowedMethods&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;GET&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;POST&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;PATCH&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;DELETE&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;PUT&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;OPTIONS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;exposedResponseHeaders&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;apigw-requestid&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;authorizers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;httpUserApiAuthorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;identitySource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$request.header.Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;audience&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;${self:custom.cognitoUserPoolClientId}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="na"&gt;issuerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;${self:custom.issuerUrl}&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="na"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cognitoUserPoolClientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CognitoUserPoolClientId-${sls:stage}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;issuerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CognitoUserPoolIssuerUrl-${sls:stage}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal here is not to explain how to set-up a Cognito authorization, if you want to learn about it you can read &lt;a href="https://dev.to/slsbytheodo/learn-serverless-on-aws-authentication-with-cognito-19bo"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Link service lambdas to the new API gateway&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The next step involved linking all of the Lambdas in the &lt;code&gt;user&lt;/code&gt; service to this new API Gateway. Serverless Framework does it for us when we declare the API in the configuration (step 1). However, we still need to set the new authorizer on our lambdas.&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handler.main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user/create&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// authorizer: { name: 'httpApiAuthorizer' }, &amp;lt; previous authorizer&lt;/span&gt;
        &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="na"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;httpUserApiAuthorizer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Update API routes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We then prefixed all API routes for the &lt;code&gt;user&lt;/code&gt; service with a specific route : &lt;code&gt;/user&lt;/code&gt;. This allowed our CloudFront distribution to easily differentiate between services based on the URL.&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handler.main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user/create&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// make sure all user routes are prefixed&lt;/span&gt;
        &lt;span class="na"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;httpUserApiAuthorizer&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;h3&gt;
  
  
  &lt;strong&gt;Step 4: Update frontend API calls&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;On the frontend, we updated all API calls related to the &lt;code&gt;users&lt;/code&gt; service by adding the &lt;code&gt;/users&lt;/code&gt; prefix. This ensured that requests were routed to the correct API Gateway without any confusion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&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;/users/list&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;
  
  
  &lt;strong&gt;Step 5: Redirect Service Requests via CloudFront&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, we set up a redirection rule in CloudFront. Any requests starting with &lt;code&gt;/service2&lt;/code&gt; were routed to the new API Gateway.&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CloudFrontApiDistribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AwsConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CloudFormationResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AWS::CloudFront::Distribution&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;DistributionConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Enabled&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;HttpVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Origins&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;DomainName&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;Fn::Join&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="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;${self:custom.httpBooksApiId}&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;.execute-api.&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;${self:provider.region}&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;.amazonaws.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;// Id: 'BackendAPI', &amp;lt;- previous global API name&lt;/span&gt;
          &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BooksAPI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
          &lt;span class="na"&gt;OriginPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;CustomOriginConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;OriginProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https-only&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="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;DomainName&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;Fn::Join&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="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;${self:custom.httpUsersApiId}&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;.execute-api.&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;${self:provider.region}&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;.amazonaws.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="na"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UsersAPI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;OriginPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;CustomOriginConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;OriginProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https-only&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="o"&gt;**&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;PriceClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PriceClass_All&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ViewerCertificate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;CloudFrontDefaultCertificate&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;DefaultCacheBehavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// TargetOriginId: 'BooksAPI', &amp;lt;- previous global API name&lt;/span&gt;
        &lt;span class="na"&gt;TargetOriginId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BooksAPI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="na"&gt;CacheBehaviors&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;PathPattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;TargetOriginId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UsersAPI&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="o"&gt;**&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default cache behaviors redirects all other routes to the original API, which is a keep-safe for all our other routes, because we don’t need to take care of them as they are already routed. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The benefits of this approach&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;By splitting the API and allocating different services to their own API Gateways, we gained several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modular growth&lt;/strong&gt;: Each service can now grow independently, with its own API Gateway, reducing the risk of hitting the 300 integration limit again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal frontend changes&lt;/strong&gt;: Thanks to CloudFront, the frontend barely needed any modifications. The redirection was handled transparently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service isolation&lt;/strong&gt;: Each service can now be managed and deployed independently, allowing for greater flexibility and scalability.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The AWS API Gateway limit of 300 integrations per API can be a hidden bottleneck, especially in fast-growing projects when you are unaware of its existence. In order to spot in before it arrives, I am adding a new rule on the &lt;a href="https://www.sls-mentor.dev/" rel="noopener noreferrer"&gt;sls-mentor&lt;/a&gt; tool, in order to be warned when you reach 250 integrations.&lt;/p&gt;

&lt;p&gt;While initially daunting, addressing it through a multi-API Gateway approach can keep your architecture flexible, scalable, and modular. This experience reinforced the importance of periodically reviewing architectural decisions and splitting services when they become too large. From now on, when I have an architecture with micro services, I will be creating a micro API for each service.&lt;/p&gt;

&lt;p&gt;For teams scaling on AWS, this approach ensures that when your services grow to the point of hitting such limits, you have a clear path forward without drastic changes to the frontend or overall application flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/slsbytheodo/sls-mentor-your-serverless-quality-teacher-has-arrived-5fin"&gt;sls-mentor and how to use it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cover photo : by &lt;a href="https://unsplash.com/@arthurbizkit?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Arthur Mazi&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/black-and-red-arch-tunnel-KLAPnoLrpOE?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>Override the 500 resource limit of AWS CloudFormation templates with Serverless Framework</title>
      <dc:creator>Anaïs Schlienger</dc:creator>
      <pubDate>Fri, 08 Dec 2023 14:47:45 +0000</pubDate>
      <link>https://dev.to/slsbytheodo/override-the-resource-limit-of-aws-cloudformation-templates-with-serverless-framework-3bdh</link>
      <guid>https://dev.to/slsbytheodo/override-the-resource-limit-of-aws-cloudformation-templates-with-serverless-framework-3bdh</guid>
      <description>&lt;p&gt;AWS provides a robust infrastructure for deploying serverless applications using AWS CloudFormation. However, it's not uncommon to encounter resource limits when working on large serverless projects. One way to tackle this challenge is by splitting your CloudFormation stacks into smaller, more manageable units. AWS suggests themselves to use &lt;em&gt;nested stacks&lt;/em&gt; to overcome the 500-resource limit. In this article, we'll explore how to achieve this using the Serverless Framework and a plugin called &lt;code&gt;serverless-plugin-split-stacks&lt;/code&gt;. We'll also discuss some tips and tricks to make the process smoother.&lt;/p&gt;

&lt;p&gt;Nested stacks are a safe way to address the resource limit wall whilst keeping the benefits of a single stack. The main benefit of a single stack is that you can deploy and rollback all resources at once. As nested stacks have a dependency to their parent stack, so if one of the nested stacks fails to deploy, the parent stack will roll back to its previous state. The nested stacks also share the output variables (such as ids or arn). We keep an atomic behavior of our stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before we dive into the details, make sure you have the following in place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS Account&lt;/li&gt;
&lt;li&gt;Node.js and npm installed&lt;/li&gt;
&lt;li&gt;Serverless Framework installed (&lt;code&gt;npm install -g serverless&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A Serverless Framework project set up&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Splitting Stacks with serverless-plugin-split-stacks
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Installation
&lt;/h4&gt;

&lt;p&gt;First, you need to install the &lt;code&gt;serverless-plugin-split-stacks&lt;/code&gt; plugin. Navigate to your Serverless project directory and run 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;npm &lt;span class="nb"&gt;install &lt;/span&gt;serverless-plugin-split-stacks &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Configuration
&lt;/h4&gt;

&lt;p&gt;In your &lt;code&gt;serverless.ts&lt;/code&gt; (or &lt;code&gt;serverless.yml&lt;/code&gt;) file, add the plugin to the &lt;code&gt;plugins&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nx"&gt;plugins&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;serverless-plugin-split-stacks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and configure it under the &lt;code&gt;custom&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nl"&gt;splitStacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;perFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;perType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;perGroupFunction&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;nestedStackCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&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;ul&gt;
&lt;li&gt;
&lt;code&gt;perFunction&lt;/code&gt;: Setting this to &lt;code&gt;true&lt;/code&gt; would split the stack for each AWS Lambda function. Setting it to &lt;code&gt;false&lt;/code&gt; keeps your structure manageable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;perType&lt;/code&gt;: If you want to split stacks based on resource types (e.g., DynamoDB tables, S3 buckets), set this to &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;perGroupFunction&lt;/code&gt;: This option is set to &lt;code&gt;true&lt;/code&gt;, which splits stacks equally based groupings of functions (the lambda and all its associated ressources, eg. IAM roles).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nestedStackCount&lt;/code&gt;: disabled if not specified ; it controls how many resources are deployed in parallel.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stackConcurrency: number&lt;/code&gt;: disabled if not specified ; it controls how many stacks are deployed in parallel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Renaming Existing Lambdas
&lt;/h4&gt;

&lt;p&gt;Here is the tricky part. The plugin doesn't work with existing resources. You can force the migration of an existing resource to a new stack if you use a custom migration with &lt;code&gt;force: true&lt;/code&gt; but it will delete the resource and recreate it. It might create conflicts in CloudFormation or issues with IAM. This is not a good idea for production environments.&lt;/p&gt;

&lt;p&gt;No worries, there is a workaround.&lt;/p&gt;

&lt;p&gt;If you have existing Lambdas, you need to rename them to follow the new structure. The easiest way is to suffix their names with an underscore &lt;code&gt;_&lt;/code&gt;. For example, if you had a Lambda named &lt;code&gt;myFunction&lt;/code&gt;, you can rename it to &lt;code&gt;myFunction_&lt;/code&gt;. You use any other suffix you like honestly, but the underscore is a good convention as it is easy to read and less bulky visually.&lt;/p&gt;

&lt;p&gt;Just like that, without being deleted, your lambdas are moved to the new stack.&lt;/p&gt;

&lt;h4&gt;
  
  
  Deploying the Stacks
&lt;/h4&gt;

&lt;p&gt;When deploying your Serverless project for the first time with these changes, ensure you set the &lt;code&gt;disableRollback&lt;/code&gt; parameter to &lt;code&gt;false&lt;/code&gt;. This way, if something goes wrong during deployment, AWS will not automatically roll back the changes. Here's how you can set it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sls deploy &lt;span class="nt"&gt;--disableRollback&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the initial deployment, it's recommended to set &lt;code&gt;disableRollback&lt;/code&gt; back to &lt;code&gt;true&lt;/code&gt; for safety reasons. This ensures that the stack rolls back to its previous state in case of deployment failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Let's illustrate these steps with a simple example. Consider a Serverless service with three functions: &lt;code&gt;userFunction&lt;/code&gt;, &lt;code&gt;orderFunction&lt;/code&gt;, and &lt;code&gt;paymentFunction&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install the &lt;code&gt;serverless-plugin-split-stacks&lt;/code&gt; plugin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure your &lt;code&gt;serverless.ts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-serverless-app&lt;/span&gt;

&lt;span class="na"&gt;frameworkVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=2.50.0'&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;serverless-plugin-split-stacks&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs18.x&lt;/span&gt;

&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;splitStacks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;perFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;perType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;perGroupFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;nestedStackCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;userFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;userFunction.handler&lt;/span&gt;
  &lt;span class="na"&gt;orderFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orderFunction.handler&lt;/span&gt;
  &lt;span class="na"&gt;paymentFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;paymentFunction.handler&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Rename existing Lambdas if necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy the stacks as explained above.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  💭 Limitations
&lt;/h3&gt;

&lt;p&gt;Hitting the resource limit may be a sign that your application is becoming too complex. It's a good idea to review your architecture and see if you can simplify it or re-organize it. Having smaller services is easier to maintain and manage.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Conclusion
&lt;/h3&gt;

&lt;p&gt;By splitting your AWS CloudFormation stacks using the &lt;code&gt;serverless-plugin-split-stacks&lt;/code&gt;, you can overcome the 500-resource limit and manage your serverless applications more effectively. This approach not only makes your infrastructure more scalable but also eases the maintenance of your serverless projects as they grow.&lt;/p&gt;

&lt;p&gt;However, sub-stacks have their limits too and it does not mean you can grow them out indefinitely. They have the same resource limit as the main stack, and the more nested stacks you have, the longer your stack takes to deploy.&lt;/p&gt;

&lt;p&gt;Having to split your stacks is a sign that your application is becoming too complex and you should review your architecture. One way to change the way you split your stacks is to have a stack for each decoupled business entity. Or maybe a micro-service architecture is maybe not the good fit for your use case: you can look into hexagonal architectures for example.&lt;/p&gt;

&lt;h4&gt;
  
  
  References
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html"&gt;AWS CloudFormation Limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html"&gt;AWS CloudFormation Nested Stacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.serverless.com/"&gt;Serverless Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/serverless-plugin-split-stacks"&gt;Serverless Plugin Split Stacks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>tutorial</category>
      <category>cloudformation</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Understand the AWS SSO login configuration</title>
      <dc:creator>Anaïs Schlienger</dc:creator>
      <pubDate>Wed, 12 Jul 2023 22:33:11 +0000</pubDate>
      <link>https://dev.to/slsbytheodo/understand-the-aws-sso-login-configuration-4am7</link>
      <guid>https://dev.to/slsbytheodo/understand-the-aws-sso-login-configuration-4am7</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;AWS SSO makes it easy for you to switch between profiles. In this article, you'll learn how to easily set up your AWS profiles, to switch between them, and use the automatic SSO login! Moreover, we will focus on the new SSO sessions parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is SSO?
&lt;/h2&gt;

&lt;p&gt;AWS Single Sign-On (SSO) is a service provided by AWS that simplifies the management of user access to multiple AWS accounts and applications.&lt;/p&gt;

&lt;p&gt;By providing a single set of credentials for accessing multiple AWS accounts and services. Users can log in once using their organization's identity provider (IdP) credentials and then access multiple AWS accounts without the need to enter separate credentials each time.&lt;/p&gt;

&lt;h3&gt;
  
  
  How did we do before SSO?
&lt;/h3&gt;

&lt;p&gt;Before using SSO, you could log into AWS by using credentials composed of either user/password (in the Management Console) or access_key/secret_key (with the CLI or different SDKs and APIs).&lt;/p&gt;

&lt;p&gt;These methods required users to manage separate sets of credentials for different AWS accounts and regions. Each time they accessed a different account or region, they had to provide the appropriate credentials.&lt;/p&gt;

&lt;p&gt;AWS Single Sign-On (SSO) simplifies this process by enabling centralized user management. It was made available in the &lt;code&gt;aws-cli&lt;/code&gt; with the V2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do we use SSO?
&lt;/h3&gt;

&lt;p&gt;The main advantages are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Access Management:&lt;/strong&gt; administrators can define user permissions centrally and apply them across multiple accounts, making it easier to grant or revoke access as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized User Management&lt;/strong&gt;: administrators can manage user identities, roles, and permissions from a single location, making it efficient to handle user onboarding, offboarding, and role changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration with Identity Providers (IdP):&lt;/strong&gt; it allows organizations to leverage their existing identity systems and enable users to log in using their familiar corporate credentials. (IdP: Microsoft Active Directory, Okta, Azure Active Directory, …).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Sign-On Experience:&lt;/strong&gt; once authenticated with their organization's IdP, users can access various AWS accounts and applications without needing to re-enter credentials, improving productivity and reducing authentication fatigue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increased Security:&lt;/strong&gt; reduces the risk of password-related issues. Users don't need to remember multiple passwords, reducing the likelihood of weak or reused passwords. Centralized access management also ensures the consistent application of security policies and allows for easier auditing and monitoring of user activity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Overall, AWS SSO simplifies the management of user access to AWS accounts and applications, enhances security, and provides a better user experience by enabling single sign-on functionality.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to configure a profile?
&lt;/h3&gt;

&lt;p&gt;You need to have a user account within an organization. Go to the start url of the access portal and sign in. (It looks like this: &lt;code&gt;https://sso-portal.awsapps.com/start&lt;/code&gt;). Once you're signed in with your unique identifier, you will have access to all the accounts the organization has set for you.&lt;/p&gt;

&lt;p&gt;Now you can configure your profile locally. The most common way to do so is with the &lt;code&gt;aws-cli&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws configure sso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will save your profile configuration in the &lt;code&gt;~/.aws/config&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the different parameters?
&lt;/h3&gt;

&lt;p&gt;AWS added support for SSO with CLI v2. A configuration file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[profile dev]
sso_account_id = 123456789011
sso_role_name = FullAccess
sso_region = eu-west-2
sso_start_url = https://sso-portal.awsapps.com/start
region = west-1
output = json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can have different profiles that point to the same account. However, the parameters give the profiles different use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sso_account_id&lt;/code&gt;: Your account id with the organization. (1 account id = 1 line on the access portal)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sso_role_name&lt;/code&gt;: The IAM role that defines the permissions a user with this profile will have.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sso_region&lt;/code&gt;: The region where the access portal is hosted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sso_start_url&lt;/code&gt;: The URL to your organization's access portal (you can get it in the invitation email or the IAM Identity Center service in the console).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;region&lt;/code&gt;: All the commands made through this profile will be sent to this region.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt;: The default output format for the commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may already be used to this if you used the previous legacy configuration. Let's see what changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  New configuration: SSO introduces sso-sessions
&lt;/h2&gt;

&lt;p&gt;In the 2.90 release of the aws-cli, AWS introduced sessions to manage our SSO profiles. Now, we only have one set of credentials for our session (instead of one set per profile) and &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html" rel="noopener noreferrer"&gt;we do not need to refresh our tokens periodically when they expire&lt;/a&gt;. The refreshed tokens are automatically fetched by our SDK or tool (ie &lt;a href="https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/sso.html#sso-generate-use-token-overview" rel="noopener noreferrer"&gt;AWS SDK&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  What changed in the new configuration: sso-sessions
&lt;/h3&gt;

&lt;p&gt;We can now add sessions in our SSO configuration file. A session is linked to a start url and the region where the start url is hosted (different from the url where your account is hosted).&lt;/p&gt;

&lt;p&gt;Then for one session, you can have multiple profiles. I can have a single session for my project start url, and different profiles linked to this session (so I have to configure only the session details once).&lt;/p&gt;

&lt;p&gt;The credentials are now stored at the session level, instead of having one set of credentials per profile.&lt;/p&gt;

&lt;p&gt;Another feature of the session is that you can restrict the access scope of the profiles. For the same start url, you could have a session allowing the profiles default access to your accounts, and one session restricting its profiles to only Read Access (e.g.). The default config is to grant the profile the access set to your listed accounts on the start url.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is in the new configuration file
&lt;/h3&gt;

&lt;p&gt;Now, in your config file, in addition to having the regular properties listed above, you can have a session linked to a profile. The start url, region, and registration scope are linked to the session, to better reflect what is going on in aws.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[profile dev-profile]
sso_session = devto-article
sso_account_id = 123456789011
sso_role_name = FullAccess
region = eu-west-1
output = json

[sso-session devto-article]
sso_region = eu-west-2
sso_start_url = https://sso-portal.awsapps.com/start
sso_registration_scopes = sso:account:access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;In the profile, we now have &lt;code&gt;sso_session&lt;/code&gt; points to the session linked to this profile&lt;/li&gt;
&lt;li&gt;The fields &lt;code&gt;sso_region&lt;/code&gt; and &lt;code&gt;sso_start_url&lt;/code&gt; are moved to the session's configuration, as they do not depend on the profile but on the access portal.&lt;/li&gt;
&lt;li&gt;There is a new parameter, &lt;code&gt;sso_registration_scopes&lt;/code&gt; that grants the scopes allowed to an application. It is the minimum access the accounts need to have to retrieve the access tokens. There is still very little documentation about it, &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-config-sso_registration_scopes" rel="noopener noreferrer"&gt;here is what AWS provides&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Let's add a new profile with a session
&lt;/h3&gt;

&lt;p&gt;This time, we are going to add a profile that only has read access to the account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws configure sso

SSO session name &lt;span class="o"&gt;(&lt;/span&gt;Recommended&lt;span class="o"&gt;)&lt;/span&gt;: devto-article
SSO start URL &lt;span class="o"&gt;[&lt;/span&gt;None]: https://sso-portal.awsapps.com/start
SSO region &lt;span class="o"&gt;[&lt;/span&gt;None]: eu-west-2
SSO registration scopes &lt;span class="o"&gt;[&lt;/span&gt;sso:account:access]:

Using the account ID: 123456789011
There are 2 roles available to you.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ReadOnly
  FullAccess
Using the role name &lt;span class="s2"&gt;"ReadOnly"&lt;/span&gt;

CLI default client region &lt;span class="o"&gt;[&lt;/span&gt;eu-west-1]: eu-west-1
CLI default output format &lt;span class="o"&gt;[&lt;/span&gt;json]: json
CLI profile name &lt;span class="o"&gt;[&lt;/span&gt;123456789011_ReadOnly]: test-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our configuration file now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[profile dev-profile]
sso_session = devto-article
sso_account_id = 123456789011
sso_role_name = FullAccess
region = eu-west-1
output = json

[profile test-profile]
sso_session = devto-article
sso_account_id = 123456789012
sso_role_name = ReadOnly
region = eu-west-1
output = json

[sso-session devto-article]
sso_region = eu-west-2
sso_start_url = https://sso-portal.awsapps.com/start
sso_registration_scopes = sso:account:access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tips
&lt;/h3&gt;

&lt;p&gt;You can you this profile by running. The command will automatically open an authorization page in your browser and fill in the authorization code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws sso login --profile test-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To log out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws sso logout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To know which profile you are using, you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Take-aways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AWS SSO makes it easy for you to switch between profiles and to make the best use of the start url features. You don't have to deal with access keys anymore!&lt;/li&gt;
&lt;li&gt;SSO sessions add one level of abstraction to have different access patterns with the same organization.&lt;/li&gt;
&lt;li&gt;Overall, the commands to set a profile and session do not change a lot, but if you decide to use sso sessions, make sure that the non-native AWS tools you use are maintained and have integrated the changes.&lt;/li&gt;
&lt;li&gt;An easy tool that supports SSO to manage your profiles is &lt;a href="https://www.granted.dev/" rel="noopener noreferrer"&gt;Granted&lt;/a&gt;, I will write a follow-up article on it!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>sso</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
