<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ aws lambda - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ aws lambda - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 28 May 2026 16:46:55 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/aws-lambda/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Securely Deploy APIs to Amazon Lambda – A Practical Guide ]]>
                </title>
                <description>
                    <![CDATA[ Cyber attacks against APIs (Application Programming Interfaces) are on the increase. These attacks arise from issues with proper authentication, authorization, unnecessary data exposure, lack of request limits, resource consumption, and use of vulner... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-securely-deploy-apis-to-amazon-lambda-a-practical-guide/</link>
                <guid isPermaLink="false">68e8418df4be6f5ede699317</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ API Gateway ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ secrets management ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Agnes Olorundare ]]>
                </dc:creator>
                <pubDate>Thu, 09 Oct 2025 23:13:17 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760051580641/75d09121-6167-4e06-94d7-53cf23a6f6a1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Cyber attacks against APIs (Application Programming Interfaces) are on the increase. These attacks arise from issues with proper authentication, authorization, unnecessary data exposure, lack of request limits, resource consumption, and use of vulnerable third-party APIs.</p>
<p>Gaps in APIs can occur before requests reach the APIs, within the code housing the APIs, and even along the path of the APIs’ communication with downstream services, dependencies, or other microservices.</p>
<p>Attackers leverage flaws in APIs to gain access to confidential data, harvest or manipulate data, or even make your service unavailable through distributed denial of service attacks.</p>
<p>In this article, you’ll learn to deploy your APIs in Lambda and apply some security measures pre-function, within the function, and post-function.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-an-api">What is an API?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-requirementsprerequisites">Requirements/Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-goal">Project Goal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-overall-architecture">Project Overall Architecture</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-aws-set-up">AWS Set Up</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-clone-project">Clone Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-simple-notification-service">Set Up Simple Notification Service</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-secrets-manager">Set Up Secrets Manager</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-internal-lambda">Set Up Internal Lambda</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-external-lambda">Set Up External Lambda</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-configure-web-application-firewall">Configure Web Application Firewall</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-configure-cognito-user-pools">Configure Cognito User Pools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-configure-api-gateway">Configure API Gateway</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-test-setup-end-to-end">Test Setup End-to-End</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-clean-up">Clean Up</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-improvements">Improvements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-an-api">What is an API?</h2>
<p>The focus of this article is the security of Application Programming Interfaces (APIs). An API is an interface that connects two programms or applications, allowing them to exchange data and communicate.</p>
<p>An API can be internal to an organization or it can belong to a third-party that allows other users to consume their data through the API.</p>
<h2 id="heading-requirementsprerequisites">Requirements/Prerequisites</h2>
<p>While this tutorial is beginner-friendly, you’ll need the following prerequisites to follow along seamlessly:</p>
<ul>
<li><p>A basic knowledge of the AWS Cloud.</p>
</li>
<li><p>An AWS account with administrator access.</p>
</li>
<li><p>AWS CLI. You can find the installation guide <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html">here</a>. Follow the instructions for your operating system.</p>
</li>
<li><p>Python. You can visit Python’s official documentation <a target="_blank" href="https://www.python.org/downloads/">site</a> for a guide on how to download and install Python for your specific operating system.</p>
</li>
<li><p>Pipenv or any Python virtual environment creation tool. You can find the Pipenv installation guide <a target="_blank" href="https://pypi.org/project/pipenv/">here</a><em>.</em></p>
</li>
<li><p>A basic knowledge of Git.</p>
</li>
<li><p>An API client, like Postman or Thunderclient.</p>
</li>
</ul>
<h2 id="heading-project-goal">Project Goal</h2>
<p>By the end of this project, you should be able to deploy APIs in Lambda securely, leveraging AWS cloud-native security services.</p>
<h2 id="heading-project-overall-architecture">Project Overall Architecture</h2>
<p>Below is the architecture of the project workflow:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758829544078/b76347ee-bbd3-41f4-88c8-2b3a89ad9087.png" alt="Project Architectural Diagram" class="image--center mx-auto" width="1159" height="464" loading="lazy"></p>
<p>As shown in the architectural diagram, when a user sends a request (a JSON object consisting of the user’s name) to an API hosted in Lambda, the user first gets authenticated by an authentication service called Amazon Cognito.</p>
<p>The request passes through a Web Application Firewall, then an API Gateway. API Gateway will perform a check to see if the user is authorized to access the API using the token that the user sends with the request after authentication. API Gateway then allows the traffic to pass through to the API if the user is authorized.</p>
<p>The user’s request will first get to an External Lambda function, which will then save the user’s name as a message to a Simple Notification Service (SNS) topic. This will then invoke an Internal Lambda to run and log the output in Amazon CloudWatch logs. The SNS topic will be accessed by External Lambda using the SNS’s unique identifier stored in Amazon Secrets Manager.</p>
<h3 id="heading-aws-set-up">AWS Set Up</h3>
<p>You’ll need to set up an AWS environment to get started. This requires creating an account if you don’t already have one.</p>
<p>Following account creation, a root user is automatically created, with all privileges attached to the user. Security best practice is to create another user with administrator privileges and use this user for subsequent tasks.</p>
<p>Then, create an access key for this user, which usually consists of two parts (Access Key ID and Secret Access Key) by navigating to the following:</p>
<p>IAM —→ Users —→ Create Access key</p>
<p>Follow the prompts and choose the <code>Command Line Interface</code> option. Check the <code>Confirmation</code> box, and go on to create the key. Download the CSV file provided, or manually copy the <code>Access Key ID</code> and <code>Secret Access Key</code>. Save them securely.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758890397608/a88ec1c6-511c-4a66-aa7a-d0dd3f41f665.png" alt="IAM Dashboard" class="image--center mx-auto" width="951" height="513" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758890497481/faab2cb7-b7ba-4e5c-b67e-a00d8fb27a10.png" alt="IAM User Page" class="image--center mx-auto" width="1366" height="542" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758890928429/1a4b3163-6340-47d2-be3e-0e61c275ba8f.png" alt="Create Access Key Page" class="image--center mx-auto" width="1348" height="551" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758890991928/5bb150b6-b014-4398-b839-ee5d6e49c425.png" alt="Access Key Use Option Page" class="image--center mx-auto" width="1366" height="494" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758891021874/a2e4eb61-eaca-4732-9377-b499fa7eab5d.png" alt="Set Access Key Tag Page" class="image--center mx-auto" width="1366" height="348" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758891049362/372f3a63-8e64-478d-9b06-61f7aa88f73a.png" alt="Download Access Key" class="image--center mx-auto" width="1366" height="517" loading="lazy"></p>
<p>Open up your terminal and run the following commands using the AWS CLI:</p>
<pre><code class="lang-bash">aws configure
</code></pre>
<p>The above command will give some prompts to provide the components of the <code>Access Key</code> created earlier and your default region (the AWS region hosting the service you intend to interact with).</p>
<h3 id="heading-clone-project">Clone Project</h3>
<p>In the next step, you’ll clone the GitHub repository containing the assets and resources used in the project implementation.</p>
<p>Visit the project <a target="_blank" href="https://github.com/Agnes4Him/secure-lambda">URL</a> and clone the repository locally.</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> &lt;repository_clone_url&gt;
</code></pre>
<h3 id="heading-set-up-simple-notification-service">Set Up Simple Notification Service</h3>
<p>Amazon Simple Notification Service (SNS) connects system components, enabling asynchronous communication and messaging among them.</p>
<p>Find <code>SNS</code> on the console, click on it, and create a topic that your APIs will send messages to. After successfully creating a topic, navigate to the topic, and in the topic details, you’ll find the topic’s <code>ARN</code>. An ARN is an Amazon Resource Name, and it’s a unique string attached to a resource you’ve created on AWS to help identify the resource. Copy the <code>ARN</code> of the topic.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758983690093/a2820581-46fb-41d1-aed9-9471a0c2db02.png" alt="SNS Dashboard" class="image--center mx-auto" width="1349" height="517" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758982964553/3eb717c7-8ce3-497b-96fb-c16483cff43e.png" alt="Create SNS Topic" class="image--center mx-auto" width="1366" height="371" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758983004729/854335ec-3d53-42e4-8bef-0e8a7d3fb2e6.png" alt="Topic Details" class="image--center mx-auto" width="1348" height="541" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758983094356/320ebaf9-9f2d-4241-b747-fd3fd0f0b62b.png" alt="SNS Topic Access Policy" class="image--center mx-auto" width="1348" height="535" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758983451385/7886c2c4-a52f-4538-8e9d-0d33738f7632.png" alt="Topic Created" class="image--center mx-auto" width="1351" height="546" loading="lazy"></p>
<h3 id="heading-set-up-secrets-manager">Set Up Secrets Manager</h3>
<p>Amazon Secrets Manager is used to store, manage, and retrieve sensitive information such as keys, credentials, tokens, and so on. You’ll store the <code>Topic ARN</code> created earlier. With this approach, you’ll demonstrate how your API can securely access the data and information it needs for its performance.</p>
<p>Go to <code>Secrets Manager</code> on the AWS console and create a secret. Provide the secret’s details, and add a new secret named <code>TOPIC_ARN</code> as the key and the actual SNS Topic ARN as the value.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758984342157/38c7855c-2221-4406-9078-496cbb480e47.png" alt="Secrets Manager Console" class="image--center mx-auto" width="1351" height="546" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758984379959/3c4ebdf7-26c8-4b74-a175-0ea2eefd258d.png" alt="Create Secret" class="image--center mx-auto" width="1351" height="542" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758984459477/cfdd8fce-4a33-45c3-8f12-d6a4a04bf799.png" alt="Choose Other Types of Secret" class="image--center mx-auto" width="1351" height="546" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758984512368/7a76618e-18f6-4b3d-bc03-5941c89909ef.png" alt="Secret Details" class="image--center mx-auto" width="1348" height="546" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758984575543/91ca9360-6320-442a-a121-37f9f35b175b.png" alt="Final Secret Store" class="image--center mx-auto" width="1356" height="314" loading="lazy"></p>
<p>Next, you’ll create some Lambda functions to serve your APIs and consume the output of the APIs. There’re three Lambda functions to set up. Two of the functions will host APIs, each of which can only be accessed by specific users. These will be referred to as <code>ExternalLambda</code>. The third Lambda will consume the output of the External Lambda functions through SNS.</p>
<h3 id="heading-set-up-internal-lambda">Set Up Internal Lambda</h3>
<p>AWS Lambda is a serverless service on AWS that users can leverage to run application functions or code when needed. You’re billed for your Lambda function based on the number of invocations of the function, the duration each invocation lasted, and the amount of memory allocated to the function. Lambda can be provisioned to use any runtime, such as Python or NodeJS. In this demonstration, you’ll focus on the NodeJS runtime.</p>
<p>Now that you know what Lambda is and does, you can create one. Let’s call the first Lambda function InternalLambda. On the AWS console, search for <code>Lambda</code>, and on the <code>Lambda</code> dashboard, click <code>Create a function</code> and provide the details. We’ll be using <code>Node.js</code> – JavaScript at the backend as the runtime of choice.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759140048151/60d5c813-a190-456e-9bad-50b429bdc6f7.png" alt="AWS Lambda" class="image--center mx-auto" width="1351" height="542" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759140165012/d519f8e9-cb98-4f75-b94e-d4d343e3003c.png" alt="Lambda Details" class="image--center mx-auto" width="1348" height="549" loading="lazy"></p>
<p>For the <code>Permissions</code> details, let Lambda create a default <code>IAM Role</code>. This default role is named according to your function, and the permissions attached to the role allow your Lambda function to send logs to CloudWatch, another AWS service used for monitoring and observability.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759140368576/3f922a46-7b3c-4034-b20b-c2b9ab5dde94.png" alt="Lambda Permissions" class="image--center mx-auto" width="1348" height="528" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759146593382/39d50020-d0bf-4cfe-950f-eec8a2ff8989.png" alt="39d50020-d0bf-4cfe-950f-eec8a2ff8989" class="image--center mx-auto" width="1345" height="546" loading="lazy"></p>
<p>As you can see in the last image above, the Lambda function you’ve created needs a <code>trigger</code> and sometimes, a <code>destination</code>. For your <code>InternalLambda</code>, the trigger is the SNS topic we configured earlier. This Lambda will read the messages that’ve been published to it, and then you can access the message from your client or even CloudWatch logs.</p>
<p>To achieve this, click the <code>Add trigger</code> button and provide the details.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759140925997/2534535f-5ad3-4e13-99f1-d8bf48c9cec1.png" alt="Add SNS to Lambda" class="image--center mx-auto" width="1358" height="508" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759146639798/1f3d75c9-ef5d-4538-9f3a-aaf2e8c0ddbb.png" alt="SNS ARN" class="image--center mx-auto" width="1366" height="533" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759146670136/367f7ca9-0b41-41ed-8749-ff70e1770ebb.png" alt="InternalLambda Overview" class="image--center mx-auto" width="1339" height="535" loading="lazy"></p>
<p>Next, you’ll provide the <code>code</code> you want to invoke through Lambda. Find the code in the GitHub repository that you cloned earlier. Paste the code in the Lambda function code space and click on <code>Deploy</code> to deploy the function.</p>
<p><code>secure-lambda/InternalLambda/index.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">async</span> (event) =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Request successfully received from SNS'</span>);                            

        <span class="hljs-keyword">let</span> name = event[<span class="hljs-string">'Records'</span>][<span class="hljs-number">0</span>][<span class="hljs-string">'Sns'</span>][<span class="hljs-string">'Message'</span>];
        <span class="hljs-keyword">let</span> response = {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">`Hello <span class="hljs-subst">${name}</span>. Greetings from InternalLambda!`</span>),
        };       
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Response: '</span>, response);                                                
        <span class="hljs-keyword">return</span> response;
    } <span class="hljs-keyword">catch</span> (err) {                            
        <span class="hljs-keyword">let</span> response = {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">500</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'An error occurred while processing your request.'</span>),
        };

        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error processing event'</span>, err);
        <span class="hljs-keyword">return</span> response;
    }   
};
</code></pre>
<p>The function defined in the index.js file above is simply taking the <code>event</code> object sent to it from SNS and extracting the <code>Message</code> attribute within it. We’re using <code>console.log</code> here to view outputs from the function and ensure it behaves as expected. Just don’t use this in a production-ready application.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759142277747/f4437ff2-4495-485d-b891-d9dda3fc939c.png" alt="InternalLambda Code" class="image--center mx-auto" width="1342" height="550" loading="lazy"></p>
<h3 id="heading-set-up-external-lambda">Set Up External Lambda</h3>
<p>You’ll be creating two external Lambda functions: 1 and 2. These two functions will receive user requests, process them, and publish messages to your SNS topic.</p>
<p>On the Lambda console, create another function and name it <code>ExternalLambda1</code>. Allow Lambda to create a default IAM Role, as previously.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759144966306/ee8a2ed1-5a2e-48df-8556-5dedd7ecdde1.png" alt="Create ExternalLambda1" class="image--center mx-auto" width="1351" height="539" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759146732803/82a46fd1-e3e5-4d72-a9fe-b41496ba076b.png" alt="ExternalLambda1 Overview" class="image--center mx-auto" width="1345" height="546" loading="lazy"></p>
<p>Paste the code snippet below in the <code>ExternalLambda1</code> code space:</p>
<p><code>secure-lambda/ExternalLambda1/insex.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
  GetSecretValueCommand,
  SecretsManagerClient,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@aws-sdk/client-secrets-manager"</span>;

<span class="hljs-keyword">import</span> { SNSClient, 
    PublishCommand 
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@aws-sdk/client-sns"</span>;

<span class="hljs-keyword">const</span> secretsManagerClient = <span class="hljs-keyword">new</span> SecretsManagerClient();

<span class="hljs-keyword">const</span> snsClient = <span class="hljs-keyword">new</span> SNSClient({});

<span class="hljs-comment">// Fetch topicArn from AWS Secrets Manager</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSecretValue</span>(<span class="hljs-params">secretName</span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> secretsManagerClient.send(
                            <span class="hljs-keyword">new</span> GetSecretValueCommand({
                            <span class="hljs-attr">SecretId</span>: secretName,
                            }),
                        );
        <span class="hljs-keyword">if</span> (data.SecretString) {
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(data.SecretString);
        }   <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">let</span> buff = Buffer.from(data.SecretBinary, <span class="hljs-string">'base64'</span>);
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(buff.toString(<span class="hljs-string">"utf-8"</span>));
        }
    } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error retrieving secret'</span>, err);                             <span class="hljs-comment">// added for debugging</span>
        <span class="hljs-keyword">throw</span> err;
    }
}                                        

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">async</span> (event) =&gt; {

    <span class="hljs-keyword">let</span> name = event[<span class="hljs-string">'name'</span>];
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Request successfully received from <span class="hljs-subst">${name}</span>`</span>);    

    <span class="hljs-comment">// Retrieve SNS Topic ARN from Secrets Manager</span>
    <span class="hljs-keyword">let</span> topicArn;
    <span class="hljs-keyword">let</span> response;
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> secret = <span class="hljs-keyword">await</span> getSecretValue(<span class="hljs-string">'LambdaSNSTopicARN'</span>);
        topicArn = secret.TOPIC_ARN;
    } <span class="hljs-keyword">catch</span> (err) {
        response = {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">500</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'An error occured, try again later.'</span>),
        };
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to load SNS Topic ARN from Secrets Manager'</span>, err);
        <span class="hljs-keyword">return</span> response;        
    }

    <span class="hljs-comment">// Publish to SNS topic</span>
   <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> snsResponse = <span class="hljs-keyword">await</span> snsClient.send(
        <span class="hljs-keyword">new</span> PublishCommand({
            <span class="hljs-attr">Message</span>: name,
            <span class="hljs-attr">TopicArn</span>: topicArn,
        })
        );
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Message published successfully:"</span>, snsResponse.MessageId);
        response = {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">`Hello <span class="hljs-subst">${name}</span>. Greetings from ExternalLambda1! Message forwarded to InternalLambda.`</span>),
        };
        <span class="hljs-keyword">return</span> response;
  } <span class="hljs-keyword">catch</span> (err) {
        response = {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">500</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">`Sorry <span class="hljs-subst">${name}</span>.An error occurred while processing your request.`</span>),
        };
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Failed to publish message:"</span>, err);
        <span class="hljs-keyword">return</span> response;
  }  
};
</code></pre>
<p>The code above leverages the AWS SDK to fetch the ARN of the SNS topic created earlier from Secrets Manager. It then publishes a message to the topic.</p>
<p>The SDK already comes installed in the Lambda function. Outside of Lambda, the SDK has to be explicitly installed. The function receives its <code>event</code> from the client via API Gateway, which we’ll configure later.</p>
<p>The SNS topic you created earlier will be the destination for this function. For Lambda to publish a topic to SNS, it needs the necessary permission attached to its IAM Role. AWS can automatically take care of that during your configuration, as shown below.</p>
<p>For the trigger, you’ll use another service known as <code>API Gateway</code>. More on that later.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759146816330/3c542eff-984e-4d02-85b3-c1da142f94d7.png" alt="ExternalLambda1 Add Destination" class="image--center mx-auto" width="1345" height="535" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759146778567/9b3650f1-90fd-47ce-99c3-627654f2d41f.png" alt="ExternalLambda1 Destination Permissions" class="image--center mx-auto" width="1348" height="539" loading="lazy"></p>
<p>Follow the same steps to provision another Lambda known as <code>ExternalLambda2</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759145915181/631aa639-493a-4f12-af1e-45f425ca2c16.png" alt="ExternalLambda2" class="image--center mx-auto" width="1351" height="539" loading="lazy"></p>
<p>The outcome of the External Lambda setup is as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759146919917/93aaf281-387f-44c3-bf6d-995b076150e9.png" alt="ExternalLambda2 Overview" class="image--center mx-auto" width="1346" height="539" loading="lazy"></p>
<p>Paste the code below into <code>ExternalLambda2</code>. It performs the same function as <code>ExternalLambda1</code>, but their output differ. Each of the two Lambda functions will be receiving traffic to a specific user that’s authorized to access the function.</p>
<p><code>secure-lambda/ExternalLambda2/index.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
  GetSecretValueCommand,
  SecretsManagerClient,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@aws-sdk/client-secrets-manager"</span>;

<span class="hljs-keyword">import</span> { SNSClient, 
    PublishCommand 
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@aws-sdk/client-sns"</span>;

<span class="hljs-keyword">const</span> secretsManagerClient = <span class="hljs-keyword">new</span> SecretsManagerClient();

<span class="hljs-keyword">const</span> snsClient = <span class="hljs-keyword">new</span> SNSClient({});

<span class="hljs-comment">// Fetch topicArn from AWS Secrets Manager</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSecretValue</span>(<span class="hljs-params">secretName</span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> secretsManagerClient.send(
                            <span class="hljs-keyword">new</span> GetSecretValueCommand({
                            <span class="hljs-attr">SecretId</span>: secretName,
                            }),
                        );
        <span class="hljs-keyword">if</span> (data.SecretString) {
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(data.SecretString);
        }   <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">let</span> buff = Buffer.from(data.SecretBinary, <span class="hljs-string">'base64'</span>);
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(buff.toString(<span class="hljs-string">"utf-8"</span>));
        }
    } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error retrieving secret'</span>, err);  
        <span class="hljs-keyword">throw</span> err;
    }
}                                        

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">async</span> (event) =&gt; {

    <span class="hljs-keyword">let</span> name = event[<span class="hljs-string">'name'</span>];
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Request successfully received from <span class="hljs-subst">${name}</span>`</span>);    

    <span class="hljs-comment">// Retrieve SNS Topic ARN from Secrets Manager</span>
    <span class="hljs-keyword">let</span> topicArn;
    <span class="hljs-keyword">let</span> response;
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> secret = <span class="hljs-keyword">await</span> getSecretValue(<span class="hljs-string">'LambdaSNSTopicARN'</span>);
        topicArn = secret.TOPIC_ARN;
    } <span class="hljs-keyword">catch</span> (err) {
        response = {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">500</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'An error occured, try again later.'</span>),
        };
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to load SNS Topic ARN from Secrets Manager'</span>, err);
        <span class="hljs-keyword">return</span> response;        
    }

    <span class="hljs-comment">// Publish to SNS topic</span>
   <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> snsResponse = <span class="hljs-keyword">await</span> snsClient.send(
        <span class="hljs-keyword">new</span> PublishCommand({
            <span class="hljs-attr">Message</span>: name,
            <span class="hljs-attr">TopicArn</span>: topicArn,
        })
        );
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Message published successfully:"</span>, snsResponse.MessageId);
        response = {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">`Hello <span class="hljs-subst">${name}</span>. Greetings from ExternalLambda2! Message forwarded to InternalLambda.`</span>),
        };
        <span class="hljs-keyword">return</span> response;
  } <span class="hljs-keyword">catch</span> (err) {
        response = {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">500</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">`Sorry <span class="hljs-subst">${name}</span>.An error occurred while processing your request.`</span>),
        };
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Failed to publish message:"</span>, err);
        <span class="hljs-keyword">return</span> response;
  }              
};
</code></pre>
<p>Before moving on, you need to modify the External Lambda’s IAM Roles. Currently, IAM Roles only have permissions to write to CloudWatch and SNS (automatically added). External Lambda also needs permission to fetch the ARN of the SNS topic that was created earlier.</p>
<p>The point here is to show how to leverage a secrets manager, such as AWS Secrets Manager, to store sensitive information or data, and still access these securely. This approach is more secure than storing the ARN as an environment variable within Lambda.</p>
<p>Navigate to IAM, and click on <code>Policies</code> tab on the left. This brings you to a list of policies. Next, click on <code>Create policy</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759320098124/71bde9ad-c6d9-4c0d-8472-d56107708be2.png" alt="IAM Policies" class="image--center mx-auto" width="1348" height="542" loading="lazy"></p>
<p>Search for <code>secrets manager</code> in the Policy editor.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759320163875/a040af9a-1e92-4aea-8c2e-5c029f60f54e.png" alt="Policy Editor" class="image--center mx-auto" width="1366" height="539" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759320923537/c3e8bb54-e78d-498c-9fec-7c7a5225b116.png" alt="Policy Editor2" class="image--center mx-auto" width="1366" height="546" loading="lazy"></p>
<p>Select the permissions Lambda needs to access Secrets Manager. In this case, that would be <code>Read —&gt; GetSecretValue</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759321060458/963c1876-dbcc-4d7d-a6fe-9f65281c1a26.png" alt="Policy Editor - Specify Permissions" class="image--center mx-auto" width="1348" height="549" loading="lazy"></p>
<p>Select <code>Specific</code> for Resources, and click on <code>Add ARNs</code>. On the next tab, add the details of the Secrets Manager Secret created earlier.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759321219657/f50fc354-a238-499c-9009-958bbc624299.png" alt="Policy Editor - Select Access" class="image--center mx-auto" width="1348" height="532" loading="lazy"></p>
<p>The Secret’s ARN will be populated here.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759321662642/356fb6bd-adc7-4663-a337-3cfaedb74b2d.png" alt="Policy Editor - Add Secrets Manager ARN" class="image--center mx-auto" width="1351" height="550" loading="lazy"></p>
<p>Next, give the policy a name and create it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759321510186/fa5da448-293f-4d95-a3b5-651292a91a7f.png" alt="Policy Editor - Create Policy" class="image--center mx-auto" width="1342" height="542" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759321721882/27807dda-8ea3-4489-bcab-d03efc201655.png" alt="Newly Added Policy" class="image--center mx-auto" width="1354" height="546" loading="lazy"></p>
<p>Next, navigate to <code>Roles</code>, and search for the IAM Roles assigned to the External Lambda functions. These are named according to the Lambda.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759321748368/dfc73acb-622c-44f9-9cf8-be51b31e3fe9.png" alt="IAM Roles" class="image--center mx-auto" width="1351" height="549" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759321856410/f1d4a13c-a568-4c9c-b14f-bb3d24b870f8.png" alt="Lambda IAM Roles" class="image--center mx-auto" width="1366" height="551" loading="lazy"></p>
<p>Click <code>Add permissions</code> to add a new permission to the IAM Role selected.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759322020293/689715ef-7e8c-45cb-9473-010f5aa105fa.png" alt="ExternalLambda1 Role" class="image--center mx-auto" width="1352" height="547" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759322453532/83996cca-7a05-48fb-8e31-d3fc679df7bc.png" alt="ExternalLambda1 Role - Policy Added" class="image--center mx-auto" width="1349" height="547" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759322498243/29a8fff5-af9a-4d4c-b3ff-3790b82b6339.png" alt="ExternalLambda2 Role" class="image--center mx-auto" width="1352" height="549" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759322570298/ab28750d-eb99-40f3-bf48-936b63bba1f0.png" alt="ExternalLambda2 Role - Policy Added" class="image--center mx-auto" width="1350" height="553" loading="lazy"></p>
<h3 id="heading-configure-web-application-firewall">Configure Web Application Firewall</h3>
<p>A firewall is a system placed in front of an application, workload, APIs, and so on to inspect traffic, filter it, and either allow or block the traffic based on some preconfigured rules.</p>
<p>For this project, you’ll use the AWS Web Application Firewall (WAF) service to inspect user requests before routing the traffic to your APIs running in Lambda.</p>
<p>Head over to the AWS console and search for WAF.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759310730367/bbcbdf00-2759-4dd7-9b7b-63ee9c252542.png" alt="AWS Web Application Firewall" class="image--center mx-auto" width="1353" height="546" loading="lazy"></p>
<p>Click on the <code>IP sets</code> tab on the left. This will enable you to create a list of IP addresses that you want to allow (as in this case) or deny.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759311298551/4537577d-a574-417e-8352-3f72b3732926.png" alt="IP Sets Page" class="image--center mx-auto" width="1366" height="547" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759311354043/edd29c9c-63e7-4bf6-a503-23ef0af5ac20.png" alt="IP Set Configuration" class="image--center mx-auto" width="1351" height="549" loading="lazy"></p>
<p>The IP addresses should include a CIDR block. For instance, if adding a single IP address, it should be <code>X.X.X.X/32</code>. The same applies to IP address ranges such as <code>X.X.X.X/24</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759311560565/0ad16e51-b70b-4a80-98f4-821659fa61b8.png" alt="IP Set Overview" class="image--center mx-auto" width="1366" height="551" loading="lazy"></p>
<p>Next, click on the <code>Web ACLs</code> tab, then <code>Create web ACL</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759311623780/9742ab87-3303-4046-84df-f9f770ed7c41.png" alt="Web ACL Page" class="image--center mx-auto" width="1366" height="549" loading="lazy"></p>
<p>Choose <code>Regional resources</code> as the Resource type, and enter your region. It’s best to keep all resources you’re creating in this project within the same region. Give your Web ACL a name, then click next.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759311736091/b4201885-2dc0-4ed8-aa38-5e25824c363b.png" alt="Web ACL Description" class="image--center mx-auto" width="1351" height="544" loading="lazy"></p>
<p>Add rules to the Web ACL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759311892739/5efad662-6c20-4678-b490-54fa33bc3a7b.png" alt="WAF Rule" class="image--center mx-auto" width="1352" height="546" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759311985197/9e7157c8-bfb4-47a9-a850-67ce8bb302b2.png" alt="Add Rule" class="image--center mx-auto" width="1350" height="551" loading="lazy"></p>
<p>Choose a rule type. In this case, you’ll use <code>IP set</code>, and give the rule a name. Choose the IP set created earlier.</p>
<p>Select <code>Source IP address</code>, and <code>Count</code> as the Action. For this project, you’ll focus on counting the requests sent to your APIs. But as shown in the image below, you can perform other actions, such as allow, block, and so on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759312925911/ea491527-970c-4b5f-b658-4345ce3d08e4.png" alt="WAF Rule Configuration" class="image--center mx-auto" width="1366" height="548" loading="lazy"></p>
<p>Your final rule configuration will appear this way.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759313133810/8e1be6d3-f6bf-42d9-881d-87216862b3bd.png" alt="WAF Rule Overview" class="image--center mx-auto" width="1350" height="553" loading="lazy"></p>
<p>Scroll down, then click on <code>Create web ACL</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759313210947/d625c4f3-a5a3-47c9-961f-ca67f652c992.png" alt="Create Rule" class="image--center mx-auto" width="1348" height="546" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759313261493/3bd16ec5-3376-4607-86cc-fd1716ad68aa.png" alt="Web ACL Dashboard" class="image--center mx-auto" width="1366" height="551" loading="lazy"></p>
<h3 id="heading-configure-cognito-user-pools">Configure Cognito User Pools</h3>
<p>Amazon Cognito is an identity management service used for creating and managing users. You can leverage it to authenticate and authorize users to applications, APIs, or other workloads.</p>
<p>You’ll create <code>User Pools</code> within Cognito and add a user to each pool. You’ll configure how these users can be authenticated and authorized to access the External Lambda functions already created.</p>
<p>Search for <code>Cognito</code> on AWS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759315681568/c2e7df4e-0e51-4c03-bf59-41ca895df74d.png" alt="Amazon Cognito" class="image--center mx-auto" width="1356" height="542" loading="lazy"></p>
<p>Click on <code>Get started for free</code>, then <code>Create user pool</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759315735324/1c09e934-186f-49db-811f-dd84d7400285.png" alt="Create User Pool" class="image--center mx-auto" width="1366" height="554" loading="lazy"></p>
<p>Select Single-page application (SPA), give the User pool the name <code>MyUserPool1</code>, and select <code>Email</code> as an option for sign-in. This means the main attribute users will provide at signup and sign-in will be their email address. Leave everything else as the default. We’ll keep things as simple as possible.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759315828576/73cb66a3-cbde-4443-8dfd-34338091aabc.png" alt="Use Pool Configuration" class="image--center mx-auto" width="1348" height="549" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759315901551/9fdc173d-f92b-4080-98d4-513b404a9aeb.png" alt="User Pool Configuration2" class="image--center mx-auto" width="1351" height="546" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759315994247/3fb17ade-90aa-4d47-b2f9-258f6b547a1f.png" alt="User Pool Configuration3" class="image--center mx-auto" width="1351" height="546" loading="lazy"></p>
<p>After creating the User pool, you’ll find the page shown below. You can view the login and signup page for the pool you’ve just created by clicking on the <code>View login page</code> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759316208497/4db5f370-deb2-449e-8017-505dc1e13079.png" alt="Cognito App Client Login URL" class="image--center mx-auto" width="1352" height="551" loading="lazy"></p>
<p>You can add <code>App clients</code> to your User Pool. By default, a client named <code>MyUserPool1</code> will be added to the pool. Navigate to your User pool, and click on <code>App clients</code> to see details of this client. Note the <code>Client ID</code>. You’ll also make some edits to the App client by clicking on the <code>Edit</code> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759316443170/58081c40-cdcb-4af9-a60b-79156f6d2d68.png" alt="User Pool App Client Overview" class="image--center mx-auto" width="1348" height="551" loading="lazy"></p>
<p>You’ll edit the <code>Authentication flows</code> field by ticking the <code>Sign in with username and password…</code> and <code>Sign in with server-side administrative credentials…</code> boxes. These changes will enable you to authenticate the user who will be added to this client programmatically, rather than through a UI. With this approach, we can fetch the token assigned to the user by Cognito and use this token to authorize access to Lambda.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759317429341/f8db3816-c603-49fe-b661-696bfff98639.png" alt="Edit App Client" class="image--center mx-auto" width="1349" height="549" loading="lazy"></p>
<p>Now, add a user to this pool. The user needs a valid email address. You’ll need the login page URL to create the user.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759318555729/d9a8ac0c-72d4-4fca-8a94-ff71a5a20caf.png" alt="Cognito Create New User" class="image--center mx-auto" width="1347" height="636" loading="lazy"></p>
<p>You need access to the email used to create the user. Fetch the code sent to the email address and submit to confirm the account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759318672240/42d4418d-a4e1-4af9-b8b5-deaf5fb63118.png" alt="Cognito Confirm Email" class="image--center mx-auto" width="1345" height="603" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759318710760/4505260f-13c9-4b3b-af99-1cb9e7436147.png" alt="Cognito Successful Sign up" class="image--center mx-auto" width="1366" height="636" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759318734000/1b3789d9-917a-4341-84ef-b8e498628557.png" alt="User Pool Users" class="image--center mx-auto" width="1366" height="547" loading="lazy"></p>
<p>Follow the same steps and create another User pool named <code>MyUserPool2</code>. Add a user with a different email to this pool.</p>
<h3 id="heading-configure-api-gateway">Configure API Gateway</h3>
<p>API Gateway is a service used to manage access and route traffic to API backend services such as APIs. It serves as a reverse proxy and provides an extra layer of security for backend services.</p>
<p>You’ll configure API Gateway to direct traffic to your Lambda functions.</p>
<p>Navigate to <code>API Gateway</code> and click on <code>Create an API</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759336384538/0e2e4120-ade3-43c3-8a3e-9ec4d0e2b343.png" alt="API Gateway" class="image--center mx-auto" width="1351" height="546" loading="lazy"></p>
<p>Select the <code>REST API</code> option —→ <code>Build</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759336533762/56d96fcb-ff27-4f96-b739-fc9658dca50e.png" alt="Select API Type" class="image--center mx-auto" width="1352" height="551" loading="lazy"></p>
<p>Select <code>New API</code>, provide a name, and choose <code>Regional</code> as the API endpoint type. IP address type can be IPv4 or Dualstack. We’ll select IPv4 here. Then create.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759336585590/ff2b11c2-cb32-4657-913e-6dfc9922531f.png" alt="API Gateway Configuration" class="image--center mx-auto" width="1349" height="549" loading="lazy"></p>
<p>An important part of API Gateway configuration for this project is the Authorizer. API Gateway uses Authorizer to allow traffic from clients to backend services.</p>
<p>You’ll create two Authorizers. Each will be connected to one of the User pools you configured earlier. On the left-hand side of the API Gateway you configured, click on <code>Authorizers</code> —→ <code>Create authorizer</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759336744757/bb00a260-3897-4867-96d3-5370e40eae59.png" alt="API Gateway Authorizer" class="image--center mx-auto" width="1366" height="547" loading="lazy"></p>
<p>Provide the name <code>AGAuthorizer1</code>, and select <code>Cognito</code> as the Authorizer type. Add the User pool for MyUserPool1 created earlier. For the Token source, use <code>Authorization</code>. When you send a request from your API client, a token will be added to the request header for authorization. The token’s key will be named <code>Authorization</code>, while the value will be the token itself.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759337066816/1ef4b11c-3508-49a5-ae7b-561b4c7f4259.png" alt="Authorizer1 Configuration" class="image--center mx-auto" width="1366" height="548" loading="lazy"></p>
<p>Create another Authorization for MyUserPool2 the same way.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759337472200/aacad54f-8927-4c3e-9a60-f207bbf45577.png" alt="Authorizer2 Configuration" class="image--center mx-auto" width="1366" height="544" loading="lazy"></p>
<p>Both Authorizers will appear this way.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759337540381/56362d9c-7f84-4021-b077-59992abd979b.png" alt="Authorizers Overview" class="image--center mx-auto" width="1366" height="547" loading="lazy"></p>
<p>Next, you’ll create resources and endpoints within the API Gateway that you’ve defined.</p>
<p>A <code>resource</code> in API Gateway is used to group certain endpoints within a specific path. You’ll define two resources within the API Gateway you’ve created. This will create two different paths, &lt;BASE_URL&gt;/&lt;RESOURCE1&gt; and &lt;BASE_URL/RESOURCE2&gt;.</p>
<p>On the API Gateway dashboard, navigate to your Gateway, click on <code>Create resource</code>, define your root path (‘/’ in your case), and provide the resource name (<code>lambda1</code>).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759420480646/c6e7c1c9-9eee-4dbf-af5b-8c335e14927c.png" alt="API Gateway Lambda1 Resource" class="image--center mx-auto" width="1366" height="528" loading="lazy"></p>
<p>Create another resource named <code>lambda2</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759420822042/74d1790e-c752-490c-883f-b42bd00d91eb.png" alt="API Gateway Resources Overview" class="image--center mx-auto" width="1366" height="532" loading="lazy"></p>
<p>Now, click on <code>/lambda1</code>, then <code>Create method</code> to define an endpoint within this resource. You’ll use the <code>POST</code> method to send requests to the backend service via this endpoint.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759420769955/bd861a6a-884d-4873-aaeb-dc958a0915b1.png" alt="API Gateway Method Configuration" class="image--center mx-auto" width="1349" height="541" loading="lazy"></p>
<p>For the backend service or Integration type, select Lambda function, and provide the ARN of ExternalLambda1.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759421208532/8d84c272-58ad-4ca4-ae56-682151495b76.png" alt="API Gateway Method Configuration2" class="image--center mx-auto" width="1352" height="538" loading="lazy"></p>
<p>For Authorization, select <code>AWS IAM —→ Cognito user pool authorizers —→ AGAuthorizer1</code>. Leave other configurations, then create the endpoint.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759421234684/1e76c46e-ba07-4178-94b3-e579ed752278.png" alt="API Gateway Method Configuration3" class="image--center mx-auto" width="1352" height="542" loading="lazy"></p>
<p>Repeat the same step to create a <code>POST</code> method for <code>/lambda2</code> resource. The <code>method</code> should be attached to <code>ExternalLambda2</code>, and <code>AGAuthorizer2</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759421691530/563c85e5-52ff-43fa-8216-41fc269989e0.png" alt="API Gateway Deployment" class="image--center mx-auto" width="1349" height="539" loading="lazy"></p>
<p>The API Gateway you’ve created needs to be deployed to become accessible. Deployment is usually done to a Stage.</p>
<p>Click on <code>Deploy API</code>, select New stage and name the stage development. Then, deploy.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759421954217/f0ca31c8-78d9-4b1b-a47c-424f6ef32093.png" alt="API Gateway Stage" class="image--center mx-auto" width="1366" height="541" loading="lazy"></p>
<p>After deployment to a stage, an invoke URL will be provided. This will serve as the base URL for the endpoints you’ve defined.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759422031311/c7cac4e7-52e9-43dd-a565-46aa062aa364.png" alt="API Gateway Stage Overview" class="image--center mx-auto" width="1345" height="539" loading="lazy"></p>
<p>The stage you’ve created needs some modifications for enhanced security. Firstly, you need to attach the <code>WAF</code> that you created earlier. Secondly, the default rate limit for the API deployed to this stage is 10000. Rate limit restricts excessive resource consumption and protects your API from abuse. For this project, you can reduce the limit to 50.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759422132101/b0f67d30-30ec-4d1f-9eee-735dc3b26500.png" alt="Edit API Gateway Stage" class="image--center mx-auto" width="1352" height="542" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759423441047/95dc8f74-c71d-449a-b15a-46caa53c7595.png" alt="API Gateway Stage - Add Rate Limit and WAF" class="image--center mx-auto" width="1352" height="536" loading="lazy"></p>
<p>To test the API Gateway set up, click on the endpoint you want to test, then the <code>Test</code> button. This initial test doesn’t need any authorization, since the test is done directly within the Gateway.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759435394293/8933349c-3e2a-4795-adb4-bb9eeb990e81.png" alt="API Gateway Endpoint Testing" class="image--center mx-auto" width="1346" height="549" loading="lazy"></p>
<p>Add JSON data as the Request body. The key will be <code>name</code>, and the value will be any string.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759435424778/a613816e-8b89-4978-9b3e-6734e48119eb.png" alt="API Gateway Testing2" class="image--center mx-auto" width="1351" height="541" loading="lazy"></p>
<p>The response sent back from ExternalLambda1 shows a status code of 200, and a response body containing exactly the message expected from the Lambda function.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759435744694/516bab58-99de-4b41-8926-e9928b8c42e4.png" alt="API Gateway Test Response" class="image--center mx-auto" width="1349" height="540" loading="lazy"></p>
<p>If you head over to CloudWatch Log groups, you should also find the Log groups that were automatically created for the Lambda functions. Click on the Log group for ExternalLambda1 and navigate to the latest Log stream. You should find the logs for the request you’ve just made from API Gateway.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759435884121/05741f0c-82dd-43f1-855c-157df5c112fc.png" alt="CloudWatch Logs for Testing" class="image--center mx-auto" width="1366" height="546" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759436073425/d4a0a2c7-8d86-44ce-adb7-4c67c3cdf40b.png" alt="CloudWatch Logs for Testing2" class="image--center mx-auto" width="1366" height="551" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759436150374/5fae1a11-c80d-41df-8ea9-39303287144b.png" alt="CloudWatch Logs - Output from InternalLambda" class="image--center mx-auto" width="1366" height="551" loading="lazy"></p>
<h3 id="heading-test-setup-end-to-end">Test Setup End-to-End</h3>
<p>To test our setup properly, and from the internet, send the same request from your API client with no additional information in the request header. This should return a <code>401</code> error – Unauthorized. This is expected.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759436452961/342e7659-5a58-45a2-af26-a657b622a83a.png" alt="Request without Token" class="image--center mx-auto" width="1321" height="448" loading="lazy"></p>
<p>API Gateway expects an authorization token from each request it receives before routing traffic to the appropriate backend service. It validates this token through Cognito.</p>
<p>You’ll mimic a user login for each user added to Coginito User pools to get a token for the user. This token will then be sent alongside any request. To achieve this, you’ll use the two Python scripts I’ve provided below:</p>
<p><code>secure-lambda/auth-scripts/user1.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

client = boto3.client(<span class="hljs-string">"cognito-idp"</span>)

response = client.initiate_auth(
    AuthFlow=<span class="hljs-string">"USER_PASSWORD_AUTH"</span>,  <span class="hljs-comment"># or ADMIN_USER_PASSWORD_AUTH if using admin creds</span>
    AuthParameters={
        <span class="hljs-string">"USERNAME"</span>: <span class="hljs-string">""</span>,             <span class="hljs-comment"># user1 email</span>
        <span class="hljs-string">"PASSWORD"</span>: <span class="hljs-string">""</span>              <span class="hljs-comment"># user1 password</span>
    },
    ClientId=<span class="hljs-string">""</span>                     <span class="hljs-comment"># Cognito App Client ID</span>
)

id_token = response[<span class="hljs-string">"AuthenticationResult"</span>][<span class="hljs-string">"IdToken"</span>]
access_token = response[<span class="hljs-string">"AuthenticationResult"</span>][<span class="hljs-string">"AccessToken"</span>]
refresh_token = response[<span class="hljs-string">"AuthenticationResult"</span>][<span class="hljs-string">"RefreshToken"</span>]

print(<span class="hljs-string">"ID Token:"</span>, id_token)
</code></pre>
<p>Using the Python boto3 library, you’ll initiate an authentication request to Cognito. Provide the email address and password of the user in MyUserPool1. Also, add the Client ID of the App client.</p>
<p>To run the script, create an isolated environment using Pipenv, uv, or a similar library. Install the dependency used in the project as defined in the Pipfile, and run the script with the Pipenv shell.</p>
<pre><code class="lang-bash">pipenv install
pipenv shell
Python secure-lambda/auth-scripts/user1.py
</code></pre>
<p>The Python command will return with a token assigned to the user. Next, you use this token to authorize a user to access ExternalLambda1.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759437804885/f03ab150-74f9-4ecf-9c7c-0ee07e8662a2.png" alt="Add Token to Request Header" class="image--center mx-auto" width="711" height="446" loading="lazy"></p>
<p>Ensure that the URL for the POST request is in the format: &lt;BASE_URL/lambda1&gt;. You should receive a response from API Gateway indicating success.</p>
<p>Now try accessing ExternalLambda2 using User1 token. You should get an <code>Unauthorized</code> message. Note that user1 will always receive an unauthorized message when it tries accessing ExternalLambda1 without an Authorization token in the header, a wrong token, or when it tries accessing ExternalLambda2, which it is not authorized to access.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438020972/39e2650a-6a99-466a-ad40-6bcf72c491c4.png" alt="User1 Access ExternalLambda2" class="image--center mx-auto" width="1323" height="439" loading="lazy"></p>
<p>Repeat the process with User2 using the token generated for the user in MyUserPool2. First, test access to ExternalLambda2 without a token in the request header.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438058029/37dbffcb-7f58-4ce0-abfa-abf0e6d1d3a4.png" alt="User2 Request without Token" class="image--center mx-auto" width="1366" height="450" loading="lazy"></p>
<p>Then test access with the token.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438118097/a408bfe0-6e94-46a1-b98e-c959f31673f8.png" alt="User2 Request with Token" class="image--center mx-auto" width="1311" height="460" loading="lazy"></p>
<p>Next, try accessing ExternalLambda1 using User2.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438144636/06ba905d-ff7b-48d6-8e5b-e35b959221ba.png" alt="User2 Access ExternalLambda1" class="image--center mx-auto" width="1366" height="466" loading="lazy"></p>
<p>You can also view the outcome of some of the requests made by your client on CloudWatch Logs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438188476/2e5aaa35-b52c-4fd3-88f4-00bc704cd809.png" alt="CloudWatch Logs Output" class="image--center mx-auto" width="1366" height="535" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438223720/630bd443-41d2-40b7-b28f-ff937fbf13f9.png" alt="CloudWatch Logs Output2" class="image--center mx-auto" width="1366" height="528" loading="lazy"></p>
<p>Also, since WAF has been configured previously to count requests (although, in a real scenario, you want to achieve much more with WAF, such as allow or block certain traffic), you can view activities captured by WAF by navigating to the service on AWS, then searching for the WAF you configured, and navigating to Traffic overview.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438469300/df718ef7-7eaa-49ad-8780-c43878d2d388.png" alt="WAF - Traffic details" class="image--center mx-auto" width="1366" height="541" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438498828/b1619abf-db45-4946-85e2-53e5a769cdb8.png" alt="WAF - Traffic Details2" class="image--center mx-auto" width="1351" height="541" loading="lazy"></p>
<p>You can find other details, such as the client device types and where requests originated.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438552359/477c4f39-04e4-4427-a2d2-74a6066622dd.png" alt="WAF - Traffic Details3" class="image--center mx-auto" width="1348" height="546" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759438590513/31acecd0-7f45-4fb9-b352-5dc5fcf49e75.png" alt="WAF - Traffic Details4" class="image--center mx-auto" width="1348" height="547" loading="lazy"></p>
<h3 id="heading-clean-up">Clean Up</h3>
<p>It’s important to clean up the resources created so far after the hands-on exercise. Due to the dependencies among the resources, trying to delete a resource that another resource depends on may lead to an error. So, you should delete them in this order:</p>
<ul>
<li><p>Secrets Manager</p>
</li>
<li><p>Cognito – Users, App Client, then User Pool</p>
</li>
<li><p>API Gateway – Endpoints/ Methods, Resources, API, Stage</p>
</li>
<li><p>Web Application Firewall – IP Set, Web ACL</p>
</li>
<li><p>All Lambda Functions</p>
</li>
<li><p>Lambda IAM Roles and the policies attached to them</p>
</li>
<li><p>CloudWatch Log Group for all the Lambda functions</p>
</li>
<li><p>SNS Topic</p>
</li>
</ul>
<p>Also, you can deactivate or delete the credentials created for your IAM Admin user if not in use.</p>
<h2 id="heading-improvements">Improvements</h2>
<p>Consider the following areas to improve, apply best practices to, and enhance the security posture of your systems further.</p>
<ol>
<li><p>Use of API keys</p>
</li>
<li><p>Third-party API consumption</p>
</li>
<li><p>API inventory management/ documentation</p>
</li>
<li><p>Resource provisioning using Infrastructure as Code</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Security at every layer of an IT system is not negotiable. In this project, we’ve demonstrated how to leverage cloud-native solutions to secure APIs hosted in a serverless service, allowing only authorized users access to the APIs.</p>
<p>I’m Agnes Olorundare, and you can find out more about me on <a target="_blank" href="https://www.linkedin.com/in/agnes-olorundare-446055b8/"><strong>LinkedIn</strong></a><strong><em>.</em></strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a Static Web App on AWS with Amplify, Lambda, API Gateway, & DynamoDB ]]>
                </title>
                <description>
                    <![CDATA[ Building modern web applications often involves complex setups and managing servers – but it doesn't have to be that way. Amazon Web Services (AWS) offers a powerful suite of "serverless" services that allow you to build and deploy applications witho... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-a-static-web-app-on-aws-with-amplify-lambda-api-gateway-and-dynamodb/</link>
                <guid isPermaLink="false">687a511b69d126230571eb7e</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS Amplify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner-friendly AWS projects for hands-on experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #aws projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws-apigateway ]]>
                    </category>
                
                    <category>
                        <![CDATA[  #HandsOnLearning  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cloud project ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Raju Manoj ]]>
                </dc:creator>
                <pubDate>Fri, 18 Jul 2025 13:50:19 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752792908077/abfe8200-e4bd-4c3c-892f-15847715c918.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building modern web applications often involves complex setups and managing servers – but it doesn't have to be that way. Amazon Web Services (AWS) offers a powerful suite of "serverless" services that allow you to build and deploy applications without having to worry about the underlying infrastructure. This means AWS handles all the heavy lifting of servers, scaling, and maintenance for you.</p>
<p>In this tutorial, we'll walk you through building a simple yet fully functional web application using several key AWS serverless services. You'll learn how to connect a frontend (what users see) with a powerful backend (what processes data) that can scale automatically and efficiently.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-well-build-a-serverless-sum-calculator">What We'll Build: A Serverless Sum Calculator</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-core-aws-services-well-use">Core AWS Services We'll Use</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites-what-youll-need">Prerequisites: What You'll Need</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-started-how-to-build-our-serverless-web-app">Getting Started: How to Build Our Serverless Web App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-your-application-is-it-working">How to Test Your Application: Is It Working?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-issues-and-solutions">Common Issues and Solutions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps-enhance-your-application">Next Steps: Enhance Your Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-well-build-a-serverless-sum-calculator">What We'll Build: A Serverless Sum Calculator</h2>
<p>We're going to create a straightforward <strong>Sum Calculator web application</strong>. This app will allow users to enter two numbers, send them to our AWS backend for calculation, store the result, and then display the sum back to the user.</p>
<p><strong>Here's how our calculator will work:</strong></p>
<ul>
<li><p><strong>First, it’ll take user input:</strong> You'll enter two numbers into a simple web page.</p>
</li>
<li><p><strong>Then the backend magic happens with AWS Lambda:</strong> These numbers will be sent to a special piece of code running on AWS Lambda, which will add them together.</p>
</li>
<li><p><strong>Next, the data is stored using DynamoDB:</strong> The calculation details (the two numbers, their sum, and when it happened) will be saved in a super-fast database called DynamoDB.</p>
</li>
<li><p><strong>Finally, it displays the results:</strong> The sum will be sent back to your web page and shown to you.</p>
</li>
</ul>
<p>This project is a fantastic way to understand the core concepts of serverless architecture and how different AWS services work together to create a dynamic web application.</p>
<h2 id="heading-core-aws-services-well-use">Core AWS Services We'll Use</h2>
<p>Before we dive in, let's get familiar with the main AWS services we'll be using. Think of them as specialized tools, each with its own job, working together to build our app.</p>
<ol>
<li><p><strong>AWS Lambda:</strong> Imagine you have a tiny robot that only activates when it's given a specific task. That's Lambda! It's a "serverless compute" service, meaning you don't manage any servers. You just upload your code (our calculator logic, in this case), and Lambda runs it only when needed.</p>
<p> <strong>Why we use it:</strong> It handles our backend math (adding numbers). When a user asks for a sum, Lambda "wakes up," does the calculation, and then goes back to sleep. This is efficient and cost-effective because you only pay for the time your code is actually running.</p>
</li>
<li><p><strong>Amazon API Gateway:</strong> Think of this as the doorman or receptionist for your backend. When your web page wants to talk to your Lambda function, it doesn't talk directly. Instead, it sends a request to API Gateway.</p>
<p> <strong>Why we use it:</strong> API Gateway securely receives requests from our web page (like "Please sum these two numbers") and then tells the correct Lambda function to wake up and handle it. It acts as the secure entry point to our backend, making sure only authorized requests get through.</p>
</li>
<li><p><strong>Amazon DynamoDB:</strong> This is our super-fast, flexible database. Unlike traditional databases that are like filing cabinets, DynamoDB is a NoSQL (Not Only SQL) database, which is great for handling large amounts of data quickly and efficiently without a fixed structure.</p>
<p> <strong>Why we use it:</strong> We'll use DynamoDB to store the history of our calculations (the numbers entered and their sums). It's designed to handle massive amounts of traffic without slowing down, making it perfect for web applications that need to store and retrieve data quickly.</p>
</li>
<li><p><strong>AWS Amplify:</strong> Amplify is like your personal construction crew for web and mobile apps. It simplifies the process of building, deploying, and hosting your frontend application, and it integrates seamlessly with other AWS services.</p>
<p> <strong>Why we use it:</strong> We'll use Amplify to host our simple HTML, CSS, and JavaScript web page. It provides an easy way to get our website live on the internet, handling all the complex deployment steps for us.</p>
</li>
</ol>
<h2 id="heading-prerequisites-what-youll-need">Prerequisites: What You'll Need</h2>
<p>To follow along with this tutorial, you should have:</p>
<ul>
<li><p><strong>An AWS account:</strong> This is essential to access all the AWS services. If you don't have one, you can sign up for a free tier account on the AWS website.</p>
</li>
<li><p><strong>Basic knowledge of Python:</strong> Our backend logic will be written in Python.</p>
</li>
<li><p><strong>Understanding of REST APIs:</strong> Knowing what an API (Application Programming Interface) is and how RESTful APIs work will be helpful, but we'll explain the key parts.</p>
</li>
<li><p><strong>Familiarity with HTML/CSS/JavaScript:</strong> Our frontend will be built using these standard web technologies.</p>
</li>
<li><p><strong>Basic knowledge of NoSQL:</strong> While not strictly required, understanding the concept of key-value pairs in databases can be beneficial.</p>
</li>
</ul>
<p>Let's get started!</p>
<h2 id="heading-getting-started-how-to-build-our-serverless-web-app">Getting Started: How to Build Our Serverless Web App</h2>
<p>We'll build our application step-by-step, starting with the database, then our backend code, connecting them with an API, and finally, deploying our frontend.</p>
<h3 id="heading-step-1-set-up-your-database-with-amazon-dynamodb">Step 1: Set Up Your Database with Amazon DynamoDB</h3>
<p>Our calculator needs a place to store the results of its calculations. For this, we'll use Amazon DynamoDB. You can follow these steps to get it all set up:</p>
<h4 id="heading-1-navigate-to-the-dynamodb-console">1. Navigate to the DynamoDB Console:</h4>
<p>First, log in to your AWS Management Console. In the search bar at the top, type "DynamoDB" and select "DynamoDB" from the results. This will take you to the DynamoDB dashboard.</p>
<h4 id="heading-2-create-a-new-table">2. Create a New Table:</h4>
<p>On the DynamoDB dashboard, look for and click the <strong>"Create table"</strong> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752056336365/33bf0598-da2e-45b9-9211-833bcd447ca9.png" alt="Screenshot of DynamoDB console with &quot;Create table&quot; button highlighted" class="image--center mx-auto" width="1919" height="860" loading="lazy"></p>
<p>Figure 1: Creating a new table in the DynamoDB console.</p>
<h4 id="heading-3-configure-your-table">3. Configure Your Table:</h4>
<p>You’ll need to add the following information for the table:</p>
<ul>
<li><p><strong>Table name:</strong> Enter <code>myTable</code>. This will be the name of our database table.</p>
</li>
<li><p><strong>Partition key:</strong> Enter <code>ID</code>.</p>
</li>
</ul>
<p>But you might be wondering – what is a Partition Key? In DynamoDB, the "Partition Key" (also sometimes called a Hash Key) is like the primary identifier for each unique item in your table. Think of it as a unique ID number for each record. It helps DynamoDB quickly find and distribute your data. For our calculator, each calculation we store will get a unique <code>ID</code>.</p>
<p>Leave all other settings at their default values for this tutorial. These defaults are usually sufficient for basic use cases.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752053498063/05a9db73-0df6-4679-b2bd-6eab52f93a6b.png" alt="Screenshot of DynamoDB &quot;Create table&quot; form with &quot;myTable&quot; and &quot;ID&quot; entered, and other settings default" class="image--center mx-auto" width="1919" height="857" loading="lazy"></p>
<p>Figure 2: Configuring the 'myTable' with 'ID' as the Partition Key.</p>
<h4 id="heading-4-finalize-table-creation">4. Finalize Table Creation:</h4>
<p>Click the <strong>"Create table"</strong> button at the bottom of the page. DynamoDB will now create your table, which usually takes a few seconds.</p>
<h4 id="heading-5-important-note-down-the-table-arn-amazon-resource-name">5. Important: Note Down the Table ARN (Amazon Resource Name):</h4>
<p>Once your table is created, click on its name (<code>myTable</code>) in the list to go to its details page. Under the "Summary" tab, you will find a section that displays the <strong>"ARN"</strong>. This is a unique identifier for your DynamoDB table across AWS.</p>
<p><strong>Copy this entire ARN and save it somewhere safe (like a notepad).</strong> We will need it later when we set up permissions for our Lambda function.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752053703489/be90f79a-38cd-42c6-b014-e0be229384fa.png" alt="Screenshot of DynamoDB table details page with the ARN highlighted" class="image--center mx-auto" width="1919" height="857" loading="lazy"></p>
<p>Figure 3: Locating and copying the ARN for your DynamoDB table.</p>
<h3 id="heading-step-2-creating-your-backend-logic-with-aws-lambda">Step 2: Creating Your Backend Logic with AWS Lambda</h3>
<p>Now that we have our database, let's create the brain of our operation: the Lambda function that will perform the addition and save the results.</p>
<h4 id="heading-1-navigate-to-the-lambda-console">1. Navigate to the Lambda Console:</h4>
<p>In the AWS Management Console search bar, type "Lambda" and select "Lambda" from the results. This will take you to the Lambda dashboard.</p>
<p>Then create a new function by clicking the <strong>"Create function"</strong> button on the dashboard.</p>
<h4 id="heading-2-configure-the-function">2. Configure the Function:</h4>
<p>Now, it’s time to configure your function:</p>
<ul>
<li><p><strong>Author from scratch:</strong> Ensure this option is selected.</p>
</li>
<li><p><strong>Function name:</strong> Give your function a meaningful name, for example, <code>SumCalculatorFunction</code> or <code>AddFunc</code>.</p>
</li>
<li><p><strong>Runtime:</strong> This specifies the programming language your code is written in. For this tutorial, select <strong>"Python 3.9"</strong> (or the latest Python 3.x runtime available).</p>
<ul>
<li><strong>Note:</strong> Always choose a runtime that matches the code you're going to write!</li>
</ul>
</li>
<li><p><strong>Architecture:</strong> Leave as <code>x86_64</code> (default).</p>
</li>
<li><p><strong>Permissions:</strong> For now, you can leave the default execution role. We will modify its permissions shortly.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752053897286/6873bb14-d94e-4857-be6c-2964c21f7759.png" alt="Screenshot of Lambda &quot;Create function&quot; configuration with name and Python runtime selected" class="image--center mx-auto" width="1919" height="792" loading="lazy"></p>
</li>
</ul>
<p>Figure 4: Configuring your Lambda function's name and runtime.</p>
<h4 id="heading-3-create-the-function">3. Create the Function:</h4>
<p>Next, create the function by clicking the <strong>"Create function"</strong> button at the bottom.</p>
<h4 id="heading-4-write-lambda-function-code">4. Write Lambda Function Code:</h4>
<p>Now it’s time to write your Lambda Function code. Once your function is created, you'll be taken to its configuration page. Scroll down to the <strong>"Code source"</strong> section. This is where you'll write or paste your Python code.</p>
<p>You'll see a default <code>lambda_function.py</code> file. Replace its contents with the following Python code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> botocore.exceptions <span class="hljs-keyword">import</span> ClientError

<span class="hljs-comment"># Create a DynamoDB client. This line creates a connection to the DynamoDB service.</span>
<span class="hljs-comment"># 'boto3' is the AWS SDK for Python, allowing our Python code to talk to AWS services.</span>
dynamodb = boto3.resource(<span class="hljs-string">'dynamodb'</span>)
<span class="hljs-comment"># Specify the DynamoDB table we want to interact with. Make sure 'myTable' matches the name you created.</span>
table = dynamodb.Table(<span class="hljs-string">'myTable'</span>)

<span class="hljs-comment"># This is the main function that Lambda will run when it's triggered.</span>
<span class="hljs-comment"># 'event' contains the data sent to our Lambda function (e.g., the numbers from our frontend).</span>
<span class="hljs-comment"># 'context' provides runtime information about the invocation, function, and execution environment.</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    <span class="hljs-comment"># Extract the numbers from the 'event' data.</span>
    <span class="hljs-comment"># .get() is used to safely retrieve values, returning None if the key doesn't exist.</span>
    num1 = event.get(<span class="hljs-string">'num1'</span>)
    num2 = event.get(<span class="hljs-string">'num2'</span>)

    <span class="hljs-comment"># Basic validation: Check if both numbers were provided.</span>
    <span class="hljs-keyword">if</span> num1 <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span> <span class="hljs-keyword">or</span> num2 <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
        <span class="hljs-comment"># If not, return an error message with a 400 (Bad Request) status code.</span>
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">400</span>,
            <span class="hljs-string">'body'</span>: json.dumps({<span class="hljs-string">'message'</span>: <span class="hljs-string">'Both num1 and num2 are required'</span>})
        }

    <span class="hljs-comment"># Calculate the sum of the two numbers.</span>
    sum_result = num1 + num2

    <span class="hljs-comment"># Generate a unique ID for our DynamoDB item.</span>
    <span class="hljs-comment"># We use a timestamp (current time in milliseconds) to ensure uniqueness.</span>
    partition_key = str(int(time.time() * <span class="hljs-number">1000</span>))

    <span class="hljs-comment"># Generate a Sort Key (optional, but good practice for more complex data models).</span>
    <span class="hljs-comment"># Here, we're using a timestamp as well, but you could use other meaningful data.</span>
    sort_key = str(int(time.time()))

    <span class="hljs-comment"># Prepare the data (item) to be stored in our DynamoDB table.</span>
    <span class="hljs-comment"># Each key-value pair here represents an attribute in our database record.</span>
    item = {
        <span class="hljs-string">'ID'</span>: partition_key,  <span class="hljs-comment"># This matches our Partition Key in DynamoDB</span>
        <span class="hljs-string">'Timestamp'</span>: sort_key, <span class="hljs-comment"># An additional attribute to track when the calculation happened</span>
        <span class="hljs-string">'num1'</span>: num1,
        <span class="hljs-string">'num2'</span>: num2,
        <span class="hljs-string">'sum'</span>: sum_result
    }

    <span class="hljs-comment"># Attempt to store the item in the DynamoDB table.</span>
    <span class="hljs-keyword">try</span>:
        table.put_item(Item=item) <span class="hljs-comment"># 'put_item' is the DynamoDB operation to add a new item.</span>
    <span class="hljs-keyword">except</span> ClientError <span class="hljs-keyword">as</span> e:
        <span class="hljs-comment"># If there's an error storing data (e.g., permission issues), return a 500 error.</span>
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">500</span>,
            <span class="hljs-string">'body'</span>: json.dumps({<span class="hljs-string">'message'</span>: <span class="hljs-string">f'Error storing data in DynamoDB: <span class="hljs-subst">{e.response[<span class="hljs-string">"Error"</span>][<span class="hljs-string">"Message"</span>]}</span>'</span>})
        }

    <span class="hljs-comment"># If everything was successful, return a success message and the calculation details.</span>
    <span class="hljs-comment"># The 'statusCode: 200' indicates success.</span>
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">'body'</span>: json.dumps({
            <span class="hljs-string">'message'</span>: <span class="hljs-string">'Sum calculated and stored successfully'</span>,
            <span class="hljs-string">'result'</span>: {
                <span class="hljs-string">'ID'</span>: partition_key,
                <span class="hljs-string">'Timestamp'</span>: sort_key,
                <span class="hljs-string">'num1'</span>: num1,
                <span class="hljs-string">'num2'</span>: num2,
                <span class="hljs-string">'sum'</span>: sum_result
            }
        })
    }
</code></pre>
<p>I’ve added a lot of comments to the code, but here’s what’s going on in summary:</p>
<ul>
<li><p><code>import json, boto3, time, ClientError</code>: These lines bring in necessary Python modules.</p>
<ul>
<li><p><code>boto3</code> is crucial for interacting with AWS services like DynamoDB.</p>
</li>
<li><p><code>json</code> helps us work with data in JSON format, which is common for web APIs.</p>
</li>
<li><p><code>time</code> is used to generate unique IDs.</p>
</li>
<li><p><code>ClientError</code> helps us catch specific AWS errors.</p>
</li>
</ul>
</li>
<li><p><code>dynamodb = boto3.resource('dynamodb')</code>: This line creates a connection object to the DynamoDB service.</p>
</li>
<li><p><code>table = dynamodb.Table('myTable')</code>: This specifies which DynamoDB table (the one we just created) our Lambda function will work with.</p>
</li>
<li><p><code>lambda_handler(event, context)</code>: This is the special function that AWS Lambda automatically calls when your function is triggered.</p>
<ul>
<li><p><code>event</code>: Contains all the information about the trigger. In our case, it will contain the <code>num1</code> and <code>num2</code> sent from our web frontend.</p>
</li>
<li><p><code>context</code>: Provides runtime information about the Lambda function.</p>
</li>
</ul>
</li>
<li><p><code>num1 = event.get('num1')</code>, <code>num2 = event.get('num2')</code>: These lines safely extract the numbers <code>num1</code> and <code>num2</code> from the incoming <code>event</code> data.</p>
</li>
<li><p><code>if num1 is None or num2 is None:</code>: This is a basic check to make sure both numbers were actually provided. If not, it sends back an error message.</p>
</li>
<li><p><code>sum_result = num1 + num2</code>: This is where the actual sum calculation happens.</p>
</li>
<li><p><code>partition_key = str(int(time.time() * 1000))</code>: This creates a unique <code>ID</code> for each record. We use the current timestamp (in milliseconds) to ensure it's always unique.</p>
</li>
<li><p><code>item = {...}</code>: This dictionary defines the data structure for the item we want to save into our <code>myTable</code> in DynamoDB.</p>
</li>
<li><p><code>table.put_item(Item=item)</code>: This is the core line that tells DynamoDB to save our <code>item</code> into the <code>myTable</code> table. It's wrapped in a <code>try-except</code> block to catch any errors during the database operation.</p>
</li>
<li><p><code>return { 'statusCode': 200, 'body': json.dumps({...}) }</code>: This is the response our Lambda function sends back to whoever called it (which will be API Gateway, and then our web frontend). A <code>statusCode</code> of <code>200</code> means success. The <code>body</code> contains the message and the result of the calculation in JSON format.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752053964053/037e2788-b43f-4f43-bc9a-65417001d353.png" alt="Screenshot of Lambda code editor with the Python code pasted in" class="image--center mx-auto" width="1889" height="832" loading="lazy"></p>
<p>Figure 5: Entering the Python code for your Sum Calculator Lambda function.</p>
<h4 id="heading-5-deploy-the-code">5. Deploy the Code:</h4>
<p>Now it’s time to deploy your code. After pasting in the code, click the <strong>"Deploy"</strong> button above the code editor. This saves your changes and makes them active.</p>
<h4 id="heading-6-test-the-lambda-function">6. Test the Lambda Function:</h4>
<p>And finally, once the code is deployed, you’ll want to test your Lambda Function (this is optional but recommended). Click the <strong>"Test"</strong> button next to the "Deploy" button. You'll be prompted to configure a test event.</p>
<p>Choose "New event" and for the "Event template", select "hello-world" (or leave it as default if "hello-world" isn't an option). Replace the sample JSON in the "Event JSON" box with the following test data:</p>
<pre><code class="lang-json">{ <span class="hljs-attr">"num1"</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">"num2"</span>: <span class="hljs-number">10</span> }
</code></pre>
<p>Give your test event a name (for example, <code>testSum</code>). Then click <strong>"Save"</strong> and then <strong>"Test"</strong>. You should see a "Status: Succeeded" message and in the "Execution result" tab, you'll see a <code>statusCode: 200</code> and the calculated sum in the <code>body</code>. This confirms your Lambda function is working correctly!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752054962119/650bee28-fcd3-416d-9e19-0784274b8210.png" alt="AWS Lambda console showing a Python script for a Lambda function named &quot;AddFunc&quot;. The code imports modules for interacting with DynamoDB and defines a handler function to extract and sum two numbers. A test event is configured with JSON input values for &quot;num1&quot; and &quot;num2&quot;. The console output indicates a successful execution, displaying the calculated sum and a status message." class="image--center mx-auto" width="1889" height="832" loading="lazy"></p>
<p>Figure 6: We can see 200 ok after the test and deploy of your Lambda function.</p>
<h3 id="heading-updating-lambda-function-permissions-iam-role-policy">Updating Lambda Function Permissions (IAM Role Policy)</h3>
<p>Our Lambda function currently has default permissions, which usually don't include access to DynamoDB. We need to explicitly grant it permission to <em>write</em> data to our <code>myTable</code>. We’ll do this through an IAM (Identity and Access Management) role policy.</p>
<h4 id="heading-1-navigate-to-permissions">1. Navigate to Permissions:</h4>
<p>On your Lambda function's configuration page, click on the <strong>"Configuration"</strong> tab. Then, select the <strong>"Permissions"</strong> sub-tab.</p>
<p>You will see an <strong>"Execution role"</strong> section with a "Role name". Click on this <strong>"Role name"</strong> (it will be a link). This will take you to the IAM console, specifically to the details of the role associated with your Lambda function.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752054336167/bcb46506-a95b-47f0-b629-3a8ac9024819.png" alt=" Screenshot of Lambda function Configuration -> Permissions tab with the &quot;Role name&quot; highlighted" class="image--center mx-auto" width="1731" height="844" loading="lazy"></p>
<p>Figure 7: Locating the IAM role associated with your Lambda function.</p>
<h4 id="heading-2-add-permissions">2. Add Permissions:</h4>
<p>On the IAM role's page, you'll see a section titled "Permissions policies". Click on <strong>"Add permissions"</strong> and then select <strong>"Create inline policy"</strong>. An "inline policy" is a policy that is embedded directly into a specific IAM identity (in this case, our Lambda's execution role).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752056539801/500c1465-91dc-49cc-b313-93ddf314142e.png" alt=" Screenshot of IAM role page, showing &quot;Add permissions&quot; dropdown and &quot;Create inline policy&quot; selected" class="image--center mx-auto" width="1917" height="817" loading="lazy"></p>
<p>Figure 8: Adding an inline policy to the Lambda function's IAM role.</p>
<h4 id="heading-3-configure-the-policy">3. Configure the Policy:</h4>
<p>You'll be presented with a <strong>"Policy editor"</strong>. Click on the <strong>"Visual editor"</strong> tab (if not already selected).</p>
<ul>
<li><p><strong>Service:</strong> Click on <strong>"Choose a service"</strong> and search for and select <strong>"DynamoDB"</strong>.</p>
</li>
<li><p><strong>Actions:</strong> In the "Actions" section, expand "Write". We need to give our Lambda function permission to "put" items into DynamoDB.</p>
<ul>
<li>Search for <code>PutItem</code> and select the checkbox next to <code>PutItem</code>. <strong>Why</strong> <code>PutItem</code>? Our Python code uses <code>table.put_item(Item=item)</code> to store data. This <code>PutItem</code> action directly corresponds to that operation.</li>
</ul>
</li>
<li><p><strong>Resources:</strong> This is crucial! We need to specify <em>which</em> DynamoDB table our Lambda function is allowed to write to.</p>
<ul>
<li><p>Click on <strong>"Specific"</strong> under the "Resources" section.</p>
</li>
<li><p>Click <strong>"Add ARN"</strong> next to "table".</p>
</li>
<li><p>In the pop-up, paste the <strong>DynamoDB Table ARN</strong> you copied earlier from Step 1.</p>
</li>
<li><p>Click <strong>"Add ARN"</strong> again in the pop-up to confirm.</p>
</li>
</ul>
</li>
</ul>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752056582357/10454e95-e63c-456f-905b-34cb7b8374ab.png" alt=" Screenshot of IAM visual policy editor: DynamoDB service selected, PutItem action checked, and DynamoDB table ARN pasted in the resources section" class="image--center mx-auto" width="1257" height="639" loading="lazy"></p>
<p>Figure 9: Configuring the IAM policy to allow Lambda to perform <code>PutItem</code> actions on your DynamoDB table.</p>
<h4 id="heading-4-review-and-create-policy">4. Review and Create Policy:</h4>
<p>Click <strong>"Next: Tags"</strong> (if applicable, skip tags). Then click <strong>"Next: Review policy"</strong>. Give your policy a meaningful name, for example, <code>DynamoDBPutItemPolicy</code>.</p>
<p>Review the policy JSON to ensure it grants <code>dynamodb:PutItem</code> permission on your specific <code>myTable</code> ARN. Then click <strong>"Create policy"</strong>.</p>
<p>Your Lambda function now has the necessary permissions to write to your DynamoDB table!</p>
<h3 id="heading-step-3-connect-the-frontend-to-the-backend-with-amazon-api-gateway">Step 3: Connect the Frontend to the Backend with Amazon API Gateway</h3>
<p>Now that we have our Lambda function ready, we need a way for our web page to talk to it. That's where API Gateway comes in.</p>
<p>In the AWS Management Console search bar, type "API Gateway" and select "API Gateway" from the results.</p>
<h4 id="heading-1-create-a-new-api">1. Create a New API:</h4>
<p>On the API Gateway dashboard, under "REST API" (not Private, WebSocket, or HTTP API), click the <strong>"Build"</strong> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752057747903/36c55082-2cac-4f05-831c-610186857a92.png" alt="Screenshot of API Gateway landing page with &quot;Build&quot; button under &quot;REST API&quot; highlighted" class="image--center mx-auto" width="1914" height="810" loading="lazy"></p>
<p>Figure 10: Starting the creation of a new REST API in API Gateway.</p>
<h4 id="heading-2-configure-api-settings">2. Configure API Settings:</h4>
<ul>
<li><p><strong>Choose the protocol:</strong> Select <code>REST</code>.</p>
</li>
<li><p><strong>Create new API:</strong> Select <code>New API</code>.</p>
</li>
<li><p><strong>API name:</strong> Give your API a clear name, e.g., <code>SumCalculatorAPI</code>.</p>
</li>
<li><p><strong>Endpoint Type:</strong> Choose <code>Regional</code> (default).</p>
</li>
</ul>
<p>Then click <strong>"Create API"</strong>.</p>
<h4 id="heading-3-create-a-resource">3. Create a Resource:</h4>
<p>After creating the API, you'll be taken to its dashboard. From the "Actions" dropdown menu, select <strong>"Create Resource"</strong>.</p>
<ul>
<li><p><strong>Resource Name:</strong> You can leave this as <code>calculator</code> or whatever makes sense. For a simple calculator endpoint, we can even leave it empty, and just work with the root <code>/</code> path. Let's keep it simple for now, and apply the method directly to the root resource (<code>/</code>). If you're building a more complex API with many functions, creating specific resources is good practice.</p>
</li>
<li><p><strong>Path Part:</strong> Leave as <code>/</code> (root path).</p>
</li>
</ul>
<p>Then click <strong>"Create Resource"</strong>.</p>
<h4 id="heading-4-create-a-method-post">4. Create a Method (POST):</h4>
<p>With the root resource (<code>/</code>) selected in the left navigation panel, from the <strong>"Actions"</strong> dropdown menu, select <strong>"Create Method"</strong>.</p>
<p>Choose <strong>"POST"</strong> from the dropdown that appears and click the checkmark button next to it. We use a <code>POST</code> request because we are <em>sending</em> data (the two numbers) to the server to create a new calculation record (or perform an action that changes data).</p>
<p>Here are the setup details:</p>
<ul>
<li><p><strong>Integration type:</strong> Select <code>Lambda Function</code>.</p>
</li>
<li><p><strong>Use Lambda Proxy integration:</strong> Check this box.</p>
<ul>
<li><strong>What is Lambda Proxy Integration?</strong> This tells API Gateway to send the entire incoming request (headers, body, query parameters) directly to your Lambda function as part of the <code>event</code> object, and also to take the entire response from Lambda and pass it back directly to the client. It simplifies the setup because you don't have to map specific request/response fields.</li>
</ul>
</li>
<li><p><strong>Lambda Region:</strong> Select the AWS region where you created your Lambda function (e.g., <code>us-east-1</code> if you used that).</p>
</li>
<li><p><strong>Lambda Function:</strong> Start typing the name of your Lambda function (e.g., <code>SumCalculatorFunction</code> or <code>AddFunc</code>) and select it from the dropdown list.</p>
</li>
</ul>
<p>Then click <strong>"Save"</strong>. You'll likely get a pop-up asking to confirm adding permissions to Lambda. Click <strong>"OK"</strong>. This automatically grants API Gateway permission to invoke your Lambda function.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752057684772/72eb9071-8ba1-42a2-9a34-60d4268ac77e.png" alt="Screenshot of API Gateway &quot;Create Method&quot; screen with POST selected and Lambda Function integration configured" class="image--center mx-auto" width="1919" height="886" loading="lazy"></p>
<p>Figure 11: Configuring the POST method to integrate with your Lambda function.</p>
<h4 id="heading-5-enable-cors-cross-origin-resource-sharing">5. Enable CORS (Cross-Origin Resource Sharing):</h4>
<p>This is a crucial step! Our frontend (which will be hosted on AWS Amplify, a different domain) needs permission to talk to our API Gateway endpoint. CORS is a security feature built into web browsers that prevents web pages from making requests to a different domain than the one they originated from, unless explicitly allowed.</p>
<p>With the root resource (<code>/</code>) still selected, from the <strong>"Actions"</strong> dropdown menu, select <strong>"Enable CORS"</strong>. A dialog box will appear. You can usually accept the default settings for this simple app. Ensure <code>POST</code> is selected under "Methods" and that "Access-Control-Allow-Origin" is set to <code>'*'</code> (meaning any origin is allowed, which is fine for a public demo).</p>
<p>Then click <strong>"Enable CORS and replace existing CORS headers"</strong>. Click <strong>"Yes, replace existing values"</strong> when prompted.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752057812238/0babb26e-2808-47f9-b6a8-50d31bb84ca9.png" alt="Screenshot of API Gateway &quot;Enable CORS&quot; dialog box" class="image--center mx-auto" width="1478" height="632" loading="lazy"></p>
<p>Figure 12: Enabling CORS for your API Gateway endpoint to allow frontend access.</p>
<h4 id="heading-6-deploy-your-api">6. Deploy Your API:</h4>
<p>Changes made to API Gateway methods aren't live until the API is "deployed" to a "stage." From the <strong>"Actions"</strong> dropdown menu, select <strong>"Deploy API"</strong>.</p>
<ul>
<li><p><strong>Deployment stage:</strong> Select <strong>"[New Stage]"</strong>.</p>
</li>
<li><p><strong>Stage name:</strong> Give your stage a name, for example, <code>dev</code> (for development), <code>prod</code> (for production), and so on. <code>dev</code> is a good choice for this tutorial.</p>
</li>
<li><p><strong>Stage description:</strong> (Optional) Add a description like "Development stage for calculator API."</p>
</li>
</ul>
<p>Then click <strong>"Deploy"</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752057834205/5de98b6c-f128-4dc6-ab68-9af65f8b8318.png" alt="Screenshot of API Gateway &quot;Deploy API&quot; dialog with &quot;New Stage&quot; and &quot;dev&quot; entered" class="image--center mx-auto" width="1472" height="681" loading="lazy"></p>
<p>Figure 13: Deploying your API to a new stage called 'dev'.</p>
<h4 id="heading-7-note-down-the-invoke-url">7. Note Down the Invoke URL:</h4>
<p>After deployment, you'll be taken to the "Stages" view. Click on your newly created stage (for example, <code>dev</code>) in the left navigation panel.</p>
<p>You will see an <strong>"Invoke URL"</strong>. This is the public URL of your API endpoint.</p>
<p><strong>Copy this entire Invoke URL and save it somewhere safe. This is the URL our frontend will use to send numbers to our Lambda function!</strong> It will look something like <a target="_blank" href="https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev"><code>https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev</code></a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752057869112/ec23f6bb-b885-4a44-b135-3a48c76fa93b.png" alt="Screenshot of API Gateway Stages page with the &quot;Invoke URL&quot; " class="image--center mx-auto" width="1919" height="877" loading="lazy"></p>
<p>Figure 14: Copying the Invoke URL for your deployed API.</p>
<h3 id="heading-step-4-create-your-frontend-web-application">Step 4: Create Your Frontend Web Application</h3>
<p>Now that our backend is ready to receive requests and process calculations, let's build the simple web page that users will interact with. We'll use basic HTML, CSS, and JavaScript.</p>
<p>You'll need to create three files in a new folder on your computer: <code>index.html</code>, <code>app.js</code>, and <code>style.css</code>.</p>
<h4 id="heading-1-indexhtml-the-structure-of-our-web-page">1. <code>index.html</code> (The Structure of Our Web Page)</h4>
<p>This file defines the basic layout and content of our calculator.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Sum Calculator<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>SUM CALCULATOR<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"calculatorForm"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"num1"</span>&gt;</span>Number 1:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"num1"</span> <span class="hljs-attr">required</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"num2"</span>&gt;</span>Number 2:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"num2"</span> <span class="hljs-attr">required</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"calculateBtn"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"btnText"</span>&gt;</span>CALCULATE<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"loadingSpinner"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"spinner d-none"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> 
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"errorAlert"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert d-none"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color: red;"</span>&gt;</span>Error!<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"errorMessage"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"resultSection"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"result-box d-none"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Calculation Result:<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Transaction ID:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"resultId"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Timestamp:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"resultTimestamp"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Numbers:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"resultNum1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> + <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"resultNum2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Sum:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"resultSum"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"app.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<ul>
<li><p><strong>HTML code explanation:</strong></p>
<ul>
<li><p><code>&lt;!DOCTYPE html&gt;</code>, <code>&lt;html&gt;</code>, <code>&lt;head&gt;</code>, <code>&lt;body&gt;</code>: These are standard HTML boilerplate tags that define the document structure.</p>
</li>
<li><p><code>&lt;title&gt;Sum Calculator&lt;/title&gt;</code>: Sets the title that appears in the browser tab.</p>
</li>
<li><p><code>&lt;link rel="stylesheet" href="style.css"&gt;</code>: This line links our HTML file to our <code>style.css</code> file, which will make our page look nice.</p>
</li>
<li><p><code>&lt;h1&gt;SUM CALCULATOR&lt;/h1&gt;</code>: The main heading of our page.</p>
</li>
<li><p><code>&lt;form id="calculatorForm"&gt;</code>: This is the container for our input fields and button. The <code>id="calculatorForm"</code> allows our JavaScript to easily find and interact with this form.</p>
</li>
<li><p><code>&lt;label for="num1"&gt;</code>, <code>&lt;input type="number" id="num1" required&gt;</code>: These create labels and input boxes where users can type numbers. <code>type="number"</code> ensures only numbers can be entered, and <code>required</code> means the field must be filled.</p>
</li>
<li><p><code>&lt;button type="submit" id="calculateBtn"&gt;</code>: This is the button that, when clicked, will trigger our JavaScript to send data to the API. <code>type="submit"</code> means it's part of a form submission.</p>
</li>
<li><p><code>&lt;span id="btnText"&gt;CALCULATE&lt;/span&gt;</code>, <code>&lt;span id="loadingSpinner" class="spinner d-none"&gt;&lt;/span&gt;</code>: These are for managing the text on the button and showing a little spinner animation when the calculation is in progress. <code>d-none</code> initially hides the spinner.</p>
</li>
<li><p><code>&lt;div id="errorAlert" ...&gt;</code>: A hidden section to display any error messages.</p>
</li>
<li><p><code>&lt;div id="resultSection" ...&gt;</code>: A hidden section to display the results of a successful calculation. We use <code>&lt;span&gt;</code> tags with IDs inside to update specific pieces of information (like <code>resultId</code>, <code>resultSum</code>, and so on) using JavaScript.</p>
</li>
<li><p><code>&lt;script src="app.js"&gt;&lt;/script&gt;</code>: This line links our HTML file to our <code>app.js</code> file. This is where all the interactive logic for our calculator will reside. It's placed at the end of the <code>&lt;body&gt;</code> so that the HTML elements are fully loaded before the JavaScript tries to access them.</p>
</li>
</ul>
</li>
</ul>
<h4 id="heading-2-appjs-the-brains-of-our-frontend">2. <code>app.js</code> (The Brains of Our Frontend)</h4>
<p>This JavaScript file handles the interaction on the web page: getting numbers, sending them to our API, and displaying the results or errors.</p>
<p><strong>Remember to replace</strong> <code>&lt;YOUR Invoke URL&gt;</code> with the actual Invoke URL you copied from API Gateway in Step 3 above!</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'DOMContentLoaded'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Define the API endpoint. REPLACE THIS WITH YOUR ACTUAL API GATEWAY INVOKE URL!</span>
    <span class="hljs-keyword">const</span> API_ENDPOINT = <span class="hljs-string">'https://YOUR_API_GATEWAY_INVOKE_URL_HERE.execute-api.us-east-1.amazonaws.com/dev'</span>; <span class="hljs-comment">// Example: https://5nur6rhsjb.execute-api.us-east-1.amazonaws.com/dev</span>

    <span class="hljs-comment">// Get references to our HTML elements so we can interact with them.</span>
    <span class="hljs-keyword">const</span> calculatorForm = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'calculatorForm'</span>);
    <span class="hljs-keyword">const</span> num1Input = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'num1'</span>);
    <span class="hljs-keyword">const</span> num2Input = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'num2'</span>);
    <span class="hljs-keyword">const</span> calculateBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'calculateBtn'</span>);
    <span class="hljs-keyword">const</span> btnText = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'btnText'</span>);
    <span class="hljs-keyword">const</span> loadingSpinner = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'loadingSpinner'</span>);
    <span class="hljs-keyword">const</span> errorAlert = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'errorAlert'</span>);
    <span class="hljs-keyword">const</span> errorMessage = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'errorMessage'</span>);
    <span class="hljs-keyword">const</span> resultSection = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'resultSection'</span>);

    <span class="hljs-comment">// References to the elements where we'll display the results.</span>
    <span class="hljs-keyword">const</span> resultId = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'resultId'</span>);
    <span class="hljs-keyword">const</span> resultTimestamp = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'resultTimestamp'</span>);
    <span class="hljs-keyword">const</span> resultNum1 = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'resultNum1'</span>);
    <span class="hljs-keyword">const</span> resultNum2 = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'resultNum2'</span>);
    <span class="hljs-keyword">const</span> resultSum = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'resultSum'</span>);

    <span class="hljs-comment">// Listen for when the calculator form is submitted (i.e., when the "CALCULATE" button is clicked).</span>
    calculatorForm.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
        event.preventDefault(); <span class="hljs-comment">// Prevent the default form submission behavior (which would refresh the page).</span>

        <span class="hljs-comment">// Hide any previous error messages or results before a new calculation.</span>
        errorAlert.classList.add(<span class="hljs-string">'d-none'</span>); <span class="hljs-comment">// 'd-none' is a class (from our CSS) to hide elements.</span>
        resultSection.classList.add(<span class="hljs-string">'d-none'</span>);

        <span class="hljs-comment">// Get the values entered by the user in the input fields and convert them to numbers.</span>
        <span class="hljs-keyword">const</span> num1 = <span class="hljs-built_in">parseFloat</span>(num1Input.value);
        <span class="hljs-keyword">const</span> num2 = <span class="hljs-built_in">parseFloat</span>(num2Input.value);

        <span class="hljs-comment">// Basic input validation: Check if the values are actually numbers.</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(num1) || <span class="hljs-built_in">isNaN</span>(num2)) {
            showError(<span class="hljs-string">'Please enter valid numbers'</span>); <span class="hljs-comment">// Display an error if inputs are not numbers.</span>
            <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Stop the function here.</span>
        }

        <span class="hljs-comment">// Show a loading state on the button to indicate that a calculation is in progress.</span>
        setLoadingState(<span class="hljs-literal">true</span>);

        <span class="hljs-comment">// Prepare the data to be sent to our API Gateway in JSON format.</span>
        <span class="hljs-keyword">const</span> requestData = {
            <span class="hljs-attr">num1</span>: num1,
            <span class="hljs-attr">num2</span>: num2
        };

        <span class="hljs-comment">// Use the Fetch API to send a POST request to our API Gateway endpoint.</span>
        fetch(API_ENDPOINT, {
            <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-comment">// We are sending data, so it's a POST request.</span>
            <span class="hljs-attr">headers</span>: {
                <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> <span class="hljs-comment">// Tell the API we're sending JSON data.</span>
            },
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(requestData) <span class="hljs-comment">// Convert our JavaScript object to a JSON string.</span>
        })
        .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
            <span class="hljs-comment">// Check if the network response was OK (status code 200-299).</span>
            <span class="hljs-keyword">if</span> (!response.ok) {
                <span class="hljs-comment">// If not OK, try to parse the error message from the response body.</span>
                <span class="hljs-keyword">return</span> response.json().then(<span class="hljs-function"><span class="hljs-params">errData</span> =&gt;</span> {
                    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errData.message || <span class="hljs-string">'Server error'</span>);
                });
            }
            <span class="hljs-comment">// If OK, parse the successful response body as JSON.</span>
            <span class="hljs-keyword">return</span> response.json();
        })
        .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
            <span class="hljs-comment">// Once we get a response, hide the loading state.</span>
            setLoadingState(<span class="hljs-literal">false</span>);

            <span class="hljs-comment">// Process the response from our Lambda function.</span>
            <span class="hljs-comment">// Our Lambda function wraps the actual result in a 'body' string, so we need to parse it again.</span>
            <span class="hljs-keyword">if</span> (data.statusCode &amp;&amp; data.statusCode === <span class="hljs-number">200</span> &amp;&amp; data.body) {
                <span class="hljs-keyword">try</span> {
                    <span class="hljs-comment">// Try to parse the nested 'body' string into a JavaScript object.</span>
                    <span class="hljs-keyword">const</span> resultData = <span class="hljs-keyword">typeof</span> data.body === <span class="hljs-string">'string'</span> 
                        ? <span class="hljs-built_in">JSON</span>.parse(data.body) 
                        : data.body;

                    <span class="hljs-comment">// Display the calculation result on the page.</span>
                    displayResult(resultData);
                } <span class="hljs-keyword">catch</span> (err) {
                    showError(<span class="hljs-string">'Error parsing the result: '</span> + err.message);
                }
            } <span class="hljs-keyword">else</span> {
                showError(<span class="hljs-string">'Unexpected API response format'</span>); <span class="hljs-comment">// If the response isn't what we expect.</span>
            }
        })
        .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
            <span class="hljs-comment">// Catch any errors that occurred during the fetch operation (e.g., network issues, API errors).</span>
            setLoadingState(<span class="hljs-literal">false</span>); <span class="hljs-comment">// Hide loading state even on error.</span>
            showError(error.message || <span class="hljs-string">'An error occurred while communicating with the API'</span>);
        });
    });

    <span class="hljs-comment">// Helper function to display an error message.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">showError</span>(<span class="hljs-params">message</span>) </span>{
        errorMessage.textContent = message; <span class="hljs-comment">// Set the error message text.</span>
        errorAlert.classList.remove(<span class="hljs-string">'d-none'</span>); <span class="hljs-comment">// Show the error alert.</span>
    }

    <span class="hljs-comment">// Helper function to manage the button's loading state.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setLoadingState</span>(<span class="hljs-params">isLoading</span>) </span>{
        <span class="hljs-keyword">if</span> (isLoading) {
            btnText.textContent = <span class="hljs-string">'Calculating...'</span>; <span class="hljs-comment">// Change button text.</span>
            loadingSpinner.classList.remove(<span class="hljs-string">'d-none'</span>); <span class="hljs-comment">// Show spinner.</span>
            calculateBtn.disabled = <span class="hljs-literal">true</span>; <span class="hljs-comment">// Disable button to prevent multiple clicks.</span>
        } <span class="hljs-keyword">else</span> {
            btnText.textContent = <span class="hljs-string">'CALCULATE'</span>; <span class="hljs-comment">// Reset button text.</span>
            loadingSpinner.classList.add(<span class="hljs-string">'d-none'</span>); <span class="hljs-comment">// Hide spinner.</span>
            calculateBtn.disabled = <span class="hljs-literal">false</span>; <span class="hljs-comment">// Enable button.</span>
        }
    }

    <span class="hljs-comment">// Helper function to display the successful calculation result.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">displayResult</span>(<span class="hljs-params">data</span>) </span>{
        <span class="hljs-comment">// Ensure we have the 'result' object from the API response.</span>
        <span class="hljs-keyword">if</span> (!data.result) {
            showError(<span class="hljs-string">'No result data in the API response'</span>);
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">const</span> result = data.result;

        <span class="hljs-comment">// Format the timestamp into a human-readable date and time.</span>
        <span class="hljs-keyword">let</span> timestampDisplay = result.Timestamp;
        <span class="hljs-keyword">if</span> (result.Timestamp &amp;&amp; !<span class="hljs-built_in">isNaN</span>(result.Timestamp)) {
            <span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-built_in">parseInt</span>(result.Timestamp) * <span class="hljs-number">1000</span>); <span class="hljs-comment">// Convert milliseconds to Date object.</span>
            timestampDisplay = date.toLocaleString(); <span class="hljs-comment">// Format to local date/time string.</span>
        }

        <span class="hljs-comment">// Update the text content of our result display elements.</span>
        resultId.textContent = result.ID || <span class="hljs-string">'N/A'</span>;
        resultTimestamp.textContent = timestampDisplay || <span class="hljs-string">'N/A'</span>;
        resultNum1.textContent = result.num1;
        resultNum2.textContent = result.num2;
        resultSum.textContent = result.sum;

        <span class="hljs-comment">// Show the result section.</span>
        resultSection.classList.remove(<span class="hljs-string">'d-none'</span>);
    }
});
</code></pre>
<ul>
<li><p><strong>JavaScript code explanation – there’s a lot going on here:</strong></p>
<ul>
<li><p><code>document.addEventListener('DOMContentLoaded', function() { ... });</code>: This ensures our JavaScript code runs only after the entire HTML document has been loaded and parsed.</p>
</li>
<li><p><code>const API_ENDPOINT = '...';</code>: This is where you <strong>MUST</strong> paste your API Gateway Invoke URL from step 3 above. This line defines where our frontend will send its requests.</p>
</li>
<li><p><code>document.getElementById(...)</code>: These lines are how JavaScript "grabs" specific elements from our HTML using their <code>id</code> attributes. This allows us to read values from inputs or change the text/visibility of other elements.</p>
</li>
<li><p><code>calculatorForm.addEventListener('submit', function(event) { ... });</code>: This sets up an "event listener." It waits for the <code>calculatorForm</code> to be submitted (when the "CALCULATE" button is clicked) and then runs the code inside this function. <code>event.preventDefault()</code> stops the browser from reloading the page, which is the default behavior for form submissions.</p>
</li>
<li><p><code>parseFloat(num1Input.value)</code>: This gets the text value from the input fields and converts them into decimal numbers.</p>
</li>
<li><p><code>if (isNaN(num1) || isNaN(num2))</code>: This checks if the user entered actual numbers. <code>isNaN</code> means "is Not a Number."</p>
</li>
<li><p><code>fetch(API_ENDPOINT, { ... })</code>: This is the core part that communicates with our backend.</p>
<ul>
<li><p><code>method: 'POST'</code>: Specifies that we're sending data.</p>
</li>
<li><p><code>headers: { 'Content-Type': 'application/json' }</code>: Tells the API Gateway that the data we're sending is in JSON format.</p>
</li>
<li><p><code>body: JSON.stringify(requestData)</code>: Converts our JavaScript <code>requestData</code> object into a JSON string that can be sent over the network.</p>
</li>
</ul>
</li>
<li><p><code>.then(response =&gt; response.json())</code>: After the <code>fetch</code> call, these <code>.then()</code> blocks handle the response. The first one checks if the network request was successful (<code>response.ok</code>) and then parses the response body as JSON.</p>
</li>
<li><p><code>.then(data =&gt; { ... })</code>: This block is executed once the JSON data from the API is received. It updates the frontend with the <code>result</code> or shows an <code>errorAlert</code>. Notice <code>JSON.parse(data.body)</code> is used because our Lambda function returns the actual data <em>inside</em> a <code>body</code> string within the overall JSON response.</p>
</li>
<li><p><code>.catch(error =&gt; { ... })</code>: This block catches any errors that happen during the <code>fetch</code> operation (for example, network issues, or errors returned from the API).</p>
</li>
<li><p><code>showError()</code>, <code>setLoadingState()</code>, <code>displayResult()</code>: These are helper functions to keep our code organized. They handle showing messages, toggling the loading spinner, and updating the result display.</p>
</li>
<li><p><code>classList.add('d-none')</code>, <code>classList.remove('d-none')</code>: These are used to dynamically hide and show HTML elements by adding or removing a CSS class named <code>d-none</code>.</p>
</li>
</ul>
</li>
</ul>
<h4 id="heading-3-stylecss-the-look-of-our-web-page">3. <code>style.css</code> (The Look of Our Web Page)</h4>
<p>This CSS file adds some basic styling to make our calculator look presentable.</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Basic styling for the body */</span>
<span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#222629</span>; <span class="hljs-comment">/* Dark background */</span>
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#FFFFFF</span>; <span class="hljs-comment">/* White text for contrast */</span>
    <span class="hljs-attribute">font-family</span>: system-ui, -apple-system, BlinkMacSystemFont, <span class="hljs-string">"Segoe UI"</span>, Roboto, Oxygen, Ubuntu, Cantarell, <span class="hljs-string">"Open Sans"</span>, <span class="hljs-string">"Helvetica Neue"</span>, sans-serif;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">align-items</span>: center; <span class="hljs-comment">/* Center content horizontally */</span>
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>; <span class="hljs-comment">/* Full viewport height */</span>
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#86C232</span>; <span class="hljs-comment">/* A vibrant green for the heading */</span>
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">30px</span>;
}

<span class="hljs-comment">/* Styling for labels (text next to input fields) */</span>
<span class="hljs-selector-tag">label</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
    <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">display</span>: inline-block; <span class="hljs-comment">/* Allows labels and inputs to be on the same line */</span>
    <span class="hljs-attribute">width</span>: <span class="hljs-number">80px</span>; <span class="hljs-comment">/* Give labels a consistent width */</span>
    <span class="hljs-attribute">text-align</span>: right;
}

<span class="hljs-comment">/* Styling for input fields */</span>
<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"number"</span>]</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#444</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#FFFFFF</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span> <span class="hljs-number">12px</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">15px</span>; <span class="hljs-comment">/* Space below each input row */</span>
    <span class="hljs-attribute">width</span>: <span class="hljs-number">150px</span>; <span class="hljs-comment">/* Consistent width for inputs */</span>
    <span class="hljs-attribute">outline</span>: none; <span class="hljs-comment">/* Remove default outline */</span>
}

<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"number"</span>]</span><span class="hljs-selector-pseudo">:focus</span> {
    <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#86C232</span>; <span class="hljs-comment">/* Highlight border on focus */</span>
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">3px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">134</span>, <span class="hljs-number">194</span>, <span class="hljs-number">50</span>, <span class="hljs-number">0.5</span>); <span class="hljs-comment">/* Green glow on focus */</span>
}

<span class="hljs-comment">/* Styling for the CALCULATE button */</span>
<span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#86C232</span>; <span class="hljs-comment">/* Green background */</span>
    <span class="hljs-attribute">border</span>: none; <span class="hljs-comment">/* No border */</span>
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#FFFFFF</span>; <span class="hljs-comment">/* White text */</span>
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
    <span class="hljs-attribute">font-weight</span>: bold;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">cursor</span>: pointer; <span class="hljs-comment">/* Indicate it's clickable */</span>
    <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.3s</span> ease; <span class="hljs-comment">/* Smooth transition for hover effect */</span>
    <span class="hljs-attribute">display</span>: flex; <span class="hljs-comment">/* Allow spinner inside */</span>
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">gap</span>: <span class="hljs-number">10px</span>; <span class="hljs-comment">/* Space between text and spinner */</span>
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">180px</span>; <span class="hljs-comment">/* Consistent width */</span>
}

<span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:disabled)</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#6a9b2b</span>; <span class="hljs-comment">/* Darker green on hover */</span>
}

<span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:disabled</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#555</span>; <span class="hljs-comment">/* Grey out when disabled */</span>
    <span class="hljs-attribute">cursor</span>: not-allowed;
}

<span class="hljs-comment">/* Styling for the form container */</span>
<span class="hljs-selector-id">#calculatorForm</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">4px</span> <span class="hljs-number">8px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.2</span>);
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">align-items</span>: flex-start; <span class="hljs-comment">/* Align form elements to the left */</span>
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">30px</span>;
}

<span class="hljs-comment">/* Styling for the loading spinner */</span>
<span class="hljs-selector-class">.spinner</span> {
    <span class="hljs-attribute">border</span>: <span class="hljs-number">3px</span> solid <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.3</span>);
    <span class="hljs-attribute">border-top</span>: <span class="hljs-number">3px</span> solid <span class="hljs-number">#FFFFFF</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">animation</span>: spin <span class="hljs-number">1s</span> linear infinite; <span class="hljs-comment">/* Animation for spinning */</span>
}

<span class="hljs-keyword">@keyframes</span> spin {
    0% { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>); }
    100% { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">360deg</span>); }
}

<span class="hljs-comment">/* Styling for the error message alert */</span>
<span class="hljs-selector-class">.alert</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#330d0d</span>; <span class="hljs-comment">/* Dark red background for errors */</span>
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ff4d4d</span>; <span class="hljs-comment">/* Red border */</span>
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#ff4d4d</span>; <span class="hljs-comment">/* Red text */</span>
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">15px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
    <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-comment">/* Styling for the result display box */</span>
<span class="hljs-selector-class">.result-box</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#86C232</span>; <span class="hljs-comment">/* Green border for results */</span>
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">4px</span> <span class="hljs-number">8px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.2</span>);
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">animation</span>: fadeIn <span class="hljs-number">0.5s</span> ease-in-out; <span class="hljs-comment">/* Fade-in animation */</span>
}

<span class="hljs-selector-class">.result-box</span> <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#86C232</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">border-bottom</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#444</span>;
    <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">15px</span>;
}

<span class="hljs-selector-class">.result-box</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">8px</span>;
}

<span class="hljs-selector-class">.result-box</span> <span class="hljs-selector-tag">strong</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#99EE33</span>; <span class="hljs-comment">/* Slightly brighter green for strong text */</span>
}

<span class="hljs-comment">/* Utility class to hide elements */</span>
<span class="hljs-selector-class">.d-none</span> {
    <span class="hljs-attribute">display</span>: none <span class="hljs-meta">!important</span>;
}

<span class="hljs-comment">/* Keyframe animation for fade-in effect */</span>
<span class="hljs-keyword">@keyframes</span> fadeIn {
    <span class="hljs-selector-tag">from</span> { <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>; <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateY</span>(<span class="hljs-number">10px</span>); }
    <span class="hljs-selector-tag">to</span> { <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>; <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateY</span>(<span class="hljs-number">0</span>); }
}
</code></pre>
<ul>
<li><p><strong>CSS code explanation:</strong></p>
<ul>
<li><p><code>body { ... }</code>: Styles the overall page background, text color, font, and centers the content.</p>
</li>
<li><p><code>h1 { ... }</code>: Styles the main heading.</p>
</li>
<li><p><code>label { ... }</code>, <code>input[type="number"] { ... }</code>: Styles the input fields and their labels, making them look consistent and user-friendly. <code>input:focus</code> adds a visual highlight when an input field is clicked.</p>
</li>
<li><p><code>button { ... }</code>: Styles the "CALCULATE" button, giving it a distinct look and adding a hover effect. <code>button:disabled</code> changes its appearance when it's temporarily inactive.</p>
</li>
<li><p><code>#calculatorForm { ... }</code>: Styles the container for our input form, giving it a background, padding, and a subtle shadow.</p>
</li>
<li><p><code>.spinner { ... }</code>, <code>@keyframes spin { ... }</code>: These define the style and animation for the loading spinner that appears on the button while the calculation is in progress.</p>
</li>
<li><p><code>.alert { ... }</code>: Styles the error message box, giving it a red background and text to clearly indicate an error.</p>
</li>
<li><p><code>.result-box { ... }</code>: Styles the box where the calculation results are displayed, giving it a green border and a fade-in animation.</p>
</li>
<li><p><code>.d-none { display: none !important; }</code>: This is a utility class. When this class is added to an HTML element via JavaScript, it will completely hide that element from the page. Removing the class makes it visible again. The <code>!important</code> ensures this style overrides any other conflicting display styles.</p>
</li>
<li><p><code>@keyframes fadeIn { ... }</code>: This defines a simple animation that makes an element gradually appear (fade in) while slightly moving upwards, making the appearance of results smoother.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-step-5-deploy-your-frontend-with-aws-amplify">Step 5: Deploy Your Frontend with AWS Amplify</h3>
<p>Finally, let's get our frontend web application online using AWS Amplify. Amplify makes it incredibly easy to host static websites.</p>
<h4 id="heading-1-prepare-your-frontend-files">1. Prepare Your Frontend Files:</h4>
<p>Make sure you have all three files (<code>index.html</code>, <code>app.js</code>, <code>style.css</code>) in a single folder on your computer.</p>
<p><strong>Compress this folder into a ZIP file.</strong> Ensure the <code>index.html</code> file is at the root of the ZIP file (not nested inside another subfolder within the ZIP).</p>
<h4 id="heading-2-navigate-to-the-aws-amplify-console">2. Navigate to the AWS Amplify Console:</h4>
<p>In the AWS Management Console search bar, type "Amplify" and select "AWS Amplify" from the results.</p>
<h4 id="heading-3-deploy-without-a-git-provider">3. Deploy Without a Git Provider:</h4>
<p>On the Amplify console, you'll see options to connect to a Git provider (like GitHub, GitLab, and so on). For simplicity in this tutorial, we'll choose to deploy manually.</p>
<p>Under "Deploy without Git provider", click <strong>"Deploy"</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752057954174/d4579189-1049-4896-8cc9-441101352731.png" alt="Screenshot of Amplify console landing page with &quot;Deploy&quot; button under &quot;Deploy without Git provider&quot;" class="image--center mx-auto" width="1908" height="898" loading="lazy"></p>
<p>Figure 15: Choosing to deploy your web app manually with AWS Amplify.</p>
<h4 id="heading-4-upload-your-files">4. Upload Your Files:</h4>
<ul>
<li><p><strong>App name:</strong> Give your application a name, e.g., <code>SumCalculatorWebApp</code>.</p>
</li>
<li><p><strong>Environment name:</strong> You can use <code>dev</code> or <code>main</code>.</p>
</li>
<li><p><strong>Drag and drop or browse files:</strong> Click on the "drag and drop" area or "Choose files" button.</p>
</li>
<li><p>Select the <strong>ZIP file</strong> you created in step 5 above.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752054718089/1e94472b-4190-489b-8c10-ec402f883cef.png" alt="Screenshot of Amplify &quot;Deploy without Git provider&quot; screen with app name and ZIP upload section" class="image--center mx-auto" width="1905" height="878" loading="lazy"></p>
</li>
</ul>
<p>Figure 16: Naming your Amplify app and uploading the ZIP file containing your frontend.</p>
<h4 id="heading-5-review-and-deploy">5. Review and Deploy:</h4>
<p>Now, click <strong>"Save and Deploy"</strong>. Amplify will now take your ZIP file, extract its contents, and deploy your static website to a global content delivery network (CDN) provided by AWS CloudFront. This makes your website load quickly for users anywhere in the world. This process may take a few minutes.</p>
<h4 id="heading-6-access-your-live-application">6. Access Your Live Application:</h4>
<p>Once the deployment is complete, Amplify will provide you with a <strong>"Domain" URL</strong>. Click on this URL. This is your live, public web application!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752054574379/a42bc253-94a1-4bc2-a42c-f7f5444d964b.png" alt="Screenshot of Amplify console after successful deployment showing the &quot;Domain&quot; URL" class="image--center mx-auto" width="1919" height="836" loading="lazy"></p>
<p>Figure 17: Your deployed Sum Calculator web app with its public URL.</p>
<h2 id="heading-how-to-test-your-application-is-it-working">How to Test Your Application: Is It Working?</h2>
<p>Now for the exciting part – let's test our fully serverless web application by following these steps:</p>
<ol>
<li><p><strong>Open your amplify app URL:</strong> Navigate to the "Domain" URL provided by AWS Amplify (from Step 5.6).</p>
</li>
<li><p><strong>Enter numbers:</strong> In the "Number 1" and "Number 2" input fields, type in any two numbers (e.g., 5 and 10).</p>
</li>
<li><p><strong>Click "CALCULATE":</strong> Click the "CALCULATE" button. You should briefly see "Calculating..." on the button.</p>
</li>
<li><p><strong>Observe the Result:</strong> If everything is set up correctly, you should see the "Calculation Result" box appear below the form, displaying the Transaction ID, Timestamp, the numbers, and their sum!</p>
</li>
<li><p><strong>Verify in DynamoDB (Optional):</strong></p>
<ul>
<li><p>Go back to your DynamoDB console.</p>
</li>
<li><p>Click on your <code>myTable</code>.</p>
</li>
<li><p>Click on the <strong>"Explore table items"</strong> tab.</p>
</li>
<li><p>You should now see new entries (items) corresponding to each calculation you performed on your web app, with the <code>ID</code>, <code>Timestamp</code>, <code>num1</code>, <code>num2</code>, and <code>sum</code> stored!</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-common-issues-and-solutions">Common Issues and Solutions</h2>
<p>Sometimes things don't work perfectly on the first try. Here are some common problems and how to fix them:</p>
<h3 id="heading-cors-errors-cross-origin-resource-sharing"><strong>CORS Errors (Cross-Origin Resource Sharing):</strong></h3>
<ul>
<li><p><strong>Symptom:</strong> Your web browser's developer console (usually accessible by pressing F12 or right-clicking and selecting "Inspect") shows errors like "Access to fetch at '...' from origin '...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present..."</p>
</li>
<li><p><strong>Solution:</strong> This means your frontend isn't allowed to talk to your API Gateway. Go back to <strong>Step 3, part 5 (Enable CORS)</strong> in API Gateway and ensure you've enabled CORS for your API. Make sure <code>POST</code> is selected and that the <code>Access-Control-Allow-Origin</code> is properly configured (for this tutorial, <code>'*'</code> is fine, but in production, you'd specify your Amplify domain).</p>
</li>
</ul>
<h3 id="heading-lambda-timeouts"><strong>Lambda Timeouts:</strong></h3>
<ul>
<li><p><strong>Symptom:</strong> Your web app keeps showing "Calculating..." or gets a generic error, and in your Lambda function's logs (CloudWatch Logs), you see "Task timed out".</p>
</li>
<li><p><strong>Solution:</strong> Your Lambda function took too long to execute. For a simple sum calculator, this is unlikely, but for more complex operations, you might need to increase the "Timeout" setting for your Lambda function. You can find this under your Lambda function's "Configuration" tab, in the "General configuration" section.</p>
</li>
</ul>
<h3 id="heading-dynamodb-permissions-errors"><strong>DynamoDB Permissions Errors:</strong></h3>
<ul>
<li><p><strong>Symptom:</strong> Your Lambda function execution fails, and in its CloudWatch logs, you see an error like "User: arn:aws:sts::... is not authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:..."</p>
</li>
<li><p><strong>Solution:</strong> This means your Lambda function doesn't have the necessary permissions to write to your DynamoDB table. Go back to <strong>Step 2, part 2 (about updating Lambda Function permissions)</strong> and ensure you've correctly added the <code>dynamodb:PutItem</code> action and specified your <code>myTable</code>'s ARN as the resource.</p>
</li>
</ul>
<h3 id="heading-incorrect-api-gateway-invoke-url"><strong>Incorrect API Gateway Invoke URL:</strong></h3>
<ul>
<li><p><strong>Symptom:</strong> Your frontend makes a request, but it either fails immediately, or you get a network error in the browser console.</p>
</li>
<li><p><strong>Solution:</strong> Double-check that the <code>API_ENDPOINT</code> constant in your <code>app.js</code> file is <em>exactly</em> the Invoke URL you copied from API Gateway. Even a tiny typo can break the connection.</p>
</li>
</ul>
<h2 id="heading-next-steps-enhance-your-application">Next Steps: Enhance Your Application</h2>
<p>This calculator is a great starting point, but you can expand it significantly:</p>
<ul>
<li><p><strong>Add authentication:</strong> Implement user login and signup (for example, using AWS Amplify Auth with Amazon Cognito) so only authorized users can use the calculator.</p>
</li>
<li><p><strong>Implement error handling:</strong> Make the frontend more robust by displaying specific error messages based on what the backend sends.</p>
</li>
<li><p><strong>Create a calculation history view:</strong> Extend your frontend to fetch and display a list of all past calculations stored in DynamoDB. This would involve another Lambda function and API Gateway endpoint (for example, a <code>GET</code> method).</p>
</li>
<li><p><strong>Add input validation:</strong> Implement more robust validation on both the frontend (JavaScript) and backend (Lambda) to handle non-numeric inputs or other edge cases.</p>
</li>
<li><p><strong>Implement real-time updates:</strong> Use AWS AppSync (GraphQL) or WebSockets with API Gateway to push new calculation results to the frontend in real-time, without needing a page refresh.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! You've successfully built and deployed a fully functional serverless web application on AWS. You've seen how to leverage powerful services like AWS Lambda, Amazon API Gateway, Amazon DynamoDB, and AWS Amplify to create a scalable, cost-effective, and low-maintenance application.</p>
<p>This architecture is incredibly powerful because it can scale automatically to handle thousands or even millions of users without you needing to manage a single server. Remember to clean up your AWS resources when you're done experimenting to avoid unnecessary charges.</p>
<h3 id="heading-resources-for-further-learning">Resources for Further Learning</h3>
<ul>
<li><p><a target="_blank" href="https://aws.amazon.com/lambda/resources/">AWS Lambda Documentation</a>: Dive deeper into serverless functions.</p>
</li>
<li><p><a target="_blank" href="https://www.google.com/search?q=https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Welcome.html">Amazon DynamoDB Developer Guide</a>: Learn more about NoSQL databases.</p>
</li>
<li><p><a target="_blank" href="https://www.google.com/search?q=https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-getting-started.html">Amazon API Gateway Tutorials</a>: Explore building various types of APIs.</p>
</li>
<li><p><a target="_blank" href="https://docs.amplify.aws/">AWS Amplify Documentation</a>: Discover more features for building and deploying full-stack applications.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Serverless Architecture Handbook: How to Publish a Node Js Docker Image to AWS ECR and Deploy the Container to AWS Lambda ]]>
                </title>
                <description>
                    <![CDATA[ Imagine you’re tasked with building a web application that can handle incoming traffic surges as your users grow without accumulating too much cost. Sounds like a dream, right? But here’s the thing: traditionally, to do this, you would have to manage... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/serverless-architecture-with-aws-lambda/</link>
                <guid isPermaLink="false">68006521c1f51bf42a74f4b0</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ecr ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Prince Onukwili ]]>
                </dc:creator>
                <pubDate>Thu, 17 Apr 2025 02:19:13 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744843935296/c359998f-1657-482f-adf4-5ab023cb1c02.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Imagine you’re tasked with building a web application that can handle incoming traffic surges as your users grow without accumulating too much cost. Sounds like a dream, right?</p>
<p>But here’s the thing: traditionally, to do this, you would have to manage lots of infrastructure – resources on which your application will be deployed – which can be a real headache. You’d have servers (VM instances or physical computers) to configure, databases to scale, load balancers to monitor...it’s a whole lot 😩</p>
<p>This is where Serverless architecture comes to the rescue. With the Serverless model, you can deploy your applications to handle thousands of users without you having to worry about incurring too much cost, managing infrastructure, servers, networking, and so on.</p>
<p>In this article, you’ll learn about Serverless Architecture: what it’s all about, and how to deploy your very own application using AWS Lambda. We’ll walk through the entire process step-by-step:</p>
<ul>
<li><p>How to clone your application repository using Git.</p>
</li>
<li><p>How to build an image of your application using Docker.</p>
</li>
<li><p>How to install the AWS CLI on your local machine and create AWS IAM users with the right permissions to push your Docker image to AWS Elastic Container Registry (ECR).</p>
</li>
</ul>
<p>Once the image is up and running on ECR, we’ll then connect it to AWS Lambda and deploy the container to Lambda for a fully serverless experience. 💡✨</p>
<p>Ready to go serverless? Let’s get started! 🚀</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-serverless-architecture">What is Serverless Architecture?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-differences-between-serverless-and-other-deployment-models">Differences Between Serverless and Other Deployment Models ⚡</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites-what-you-should-know-before-following-along">🧠 Prerequisites — What You Should Know Before Following Along!</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-the-application-using-git">How to Set Up the Application Using Git 🐙</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-the-codebase">Understanding the Codebase 🔎</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-docker-image-of-the-application">How to Create a Docker Image of the Application 🐋</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-container-registry-on-aws-elastic-container-registry-ecr">How to Create a Container Registry on AWS Elastic Container Registry (ECR) 📁</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-iam-with-aws-how-to-create-a-user-on-aws-iam-to-allow-access-to-your-aws-ecr">IAM with AWS: How to Create a User on AWS IAM to Allow Access to Your AWS ECR 👤🔐</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-upload-your-docker-image-to-the-aws-ecr-repository">How to Upload Your Docker Image to the AWS ECR repository ⬆️</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-deploy-the-application-container-to-aws-lambda-from-the-image-on-aws-ecr">How to Deploy the Application Container to AWS Lambda from the Image on AWS ECR 🚀</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-advantages-of-adopting-the-serverless-model-in-businesses">Advantages of Adopting the Serverless Model in Businesses 💼</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-disadvantages-of-the-serverless-model">Disadvantages of the Serverless Model 🚫</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-adopt-the-serverless-model">When to Adopt the Serverless Model 🤔</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion 📝</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-about-the-author">About the Author 👨‍💻</a></p>
</li>
</ol>
<h2 id="heading-what-is-serverless-architecture">What is Serverless Architecture?</h2>
<p>Before we dive deeper, let’s break down what we mean by Servers. In the tech world, servers are powerful computers that store, process, and manage data. Think of them as the behind-the-scenes workhorses that:</p>
<ul>
<li><p><strong>Store your data:</strong> Like a central filing cabinet for your digital documents.</p>
</li>
<li><p><strong>Run your applications:</strong> They execute the code that keeps your app or website running.</p>
</li>
<li><p><strong>Handle requests:</strong> Servers respond to user requests – like loading a webpage or processing a login.</p>
</li>
</ul>
<p>Alright, now let’s talk about Serverless Architecture – but first, let’s clear up a common misconception. When most people hear the word "Serverless", they immediately think, "Wait… no servers? How does that even work?!" 😅</p>
<p>Here’s the truth: Serverless doesn’t mean there are no servers involved (surprise, surprise! 😉). Instead, it means you, as a developer, don’t have to worry about managing the servers that your application runs on. The server-side infrastructure is fully handled by the cloud provider – in this case, AWS Lambda. You just focus on writing code and deploying it, and AWS takes care of the rest.</p>
<h3 id="heading-so-whats-the-big-deal-with-serverless">So, What’s the Big Deal with Serverless?</h3>
<p>In a traditional setup, when you deploy your application, you’re responsible for things like:</p>
<ul>
<li><p><strong>Provisioning servers</strong> (how many servers do you need? What size?)</p>
</li>
<li><p><strong>Scaling resources</strong> (how do you handle traffic spikes without overpaying?)</p>
</li>
<li><p><strong>Monitoring</strong> and keeping everything running smoothly.</p>
</li>
</ul>
<p>Sounds like a lot, right? 🤯 Well, Serverless Architecture simplifies all of that by letting you focus purely on your application code. With Lambda, you can run code in response to events (like an HTTP request, a file upload, or a database change) without worrying about the infrastructure behind it. AWS automatically scales the compute resources as needed, charging you only for the time your code is actually running. ⏱️💸</p>
<p>Imagine you’re at a restaurant. Instead of running the kitchen yourself (like managing your own servers), you just place an order (your code) and the chef (AWS Lambda) makes it for you, on-demand, based on what you need. 🍽️🍴</p>
<h2 id="heading-differences-between-serverless-and-other-deployment-models">Differences Between Serverless and Other Deployment Models ⚡</h2>
<p>Now that you understand how Serverless works, let’s take a little detour and explore the other models used to deploy applications. After all, Serverless isn’t the only kid on the block, and this will give you some important perspective when choosing the right model for your use case. 👀</p>
<p>When you build an app, you need somewhere to host it – a home for your code to live and run. Over the years, the tech world has come up with different ways to handle this, and each one gives you a different level of control (and responsibility) over your servers.</p>
<p>Let’s break it down.</p>
<h3 id="heading-infrastructure-as-a-service-iaas">🏠 Infrastructure as a Service (IaaS)</h3>
<p>With IaaS, cloud providers like AWS, Google Cloud, or Microsoft Azure give you the building blocks – virtual servers (also called instances), storage, and networking tools – but it’s still your job to set everything up.</p>
<p>It’s like renting an empty apartment. You get the walls, the doors, and the roof, but you still have to bring your own furniture, set up your Wi-Fi, and clean the place regularly. 🏡🧹</p>
<p>When you choose IaaS, you’re responsible for:</p>
<ul>
<li><p>Configuring the servers (choosing the size, the operating system, and installing software).</p>
</li>
<li><p>Handling updates, patches, and security.</p>
</li>
<li><p>Scaling up or down when traffic changes.</p>
</li>
</ul>
<p><strong>Example:</strong> Amazon EC2 (Elastic Compute Cloud) is a classic IaaS service. You rent a virtual machine, set it up yourself, and manage it like a digital landlord.</p>
<h3 id="heading-platform-as-a-service-paas">🎯 Platform as a Service (PaaS)</h3>
<p>Next up, we’ve got PaaS – a more polished setup.</p>
<p>In this model, the cloud provider takes care of the infrastructure and the underlying operating system, so you don’t have to. You just upload your code, configure a few settings, and the platform runs your app.</p>
<p>It’s like moving into a fully furnished apartment — the kitchen works, the lights are on, and the Wi-Fi is already connected. You just show up with your bags and get to work! 🧳✨</p>
<p><strong>Example:</strong> AWS Elastic Beanstalk, Heroku, or Google App Engine.</p>
<h3 id="heading-serverless-the-special-paas">🌩️ Serverless: The Special PaaS</h3>
<p>Now here’s where things get interesting: Serverless actually falls under the PaaS umbrella, but it deserves its own spotlight. Why? Because it takes the convenience of PaaS and pushes it to the next level.</p>
<p>In a traditional PaaS model (like AWS Fargate or Heroku), your application is running 24/7, whether you have visitors using it or not. You pay for the reserved space and compute power all month long, just like renting an apartment. Even if you didn’t sleep there the entire month, the bill still comes at the end. 💸🏡</p>
<p>But with Serverless, the rules change. You only pay when your code is actually being used.</p>
<h4 id="heading-how-applications-run-in-the-serverless-model">How Applications Run in the Serverless Model ⚙️</h4>
<p>In a Serverless model, your application isn’t just sitting there running all day. It “wakes up” only when it’s needed. But what exactly causes it to wake up? That’s where triggers come in.</p>
<p>Triggers are events that tell your Serverless application, “Hey, it’s time to do something!” These events could be all sorts of things, like:</p>
<ul>
<li><p>A user visiting your website and clicking a button.</p>
</li>
<li><p>Someone uploading a file to your cloud storage (like an image or document).</p>
</li>
<li><p>A new row being added to a database.</p>
</li>
<li><p>An automated schedule (like a reminder that runs every day at 8 AM).</p>
</li>
</ul>
<p>When one of these events happens, your application instantly comes to life, runs the exact task you programmed, and then goes back to “sleep” until the next trigger. This is how Serverless keeps your cloud costs low and your resources efficient – no constant running in the background, only action when there’s actually something to do!.⚡😎</p>
<p>For example, if a user sends a request that triggers your application to run for just 10 seconds and uses 20MB of memory, that’s all you pay for — the exact time and resources consumed.</p>
<p>No users? No requests? No payment. Now that’s a smart way to save money. 🧠💰</p>
<h3 id="heading-quick-comparison-paas-vs-serverless">💡 Quick Comparison: PaaS vs Serverless</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>Traditional PaaS (example: AWS Fargate)</strong></td><td><strong>Serverless PaaS (example: AWS Lambda)</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Server Configuration</td><td>You select compute size &amp; limits.</td><td>No need — AWS handles it all.</td></tr>
<tr>
<td>Scaling</td><td>You configure scaling policies.</td><td>Automatic, event-driven scaling (based on incoming traffic). The higher the traffic, the more compute power is added to your application, and vice versa. 😃</td></tr>
<tr>
<td>Billing</td><td>Charged for running instances 24/7, even when idle.</td><td>Charged only when your code runs. ⏱️💸</td></tr>
<tr>
<td>Deployment</td><td>Deploy full applications.</td><td>Deploy small chunks of code (functions). You can also deploy microservices and full-scale web applications</td></tr>
</tbody>
</table>
</div><hr>
<h2 id="heading-prerequisites-what-you-should-know-before-following-along">🧠 Prerequisites — What You Should Know Before Following Along</h2>
<p>Before we dive in, here’s the best part: I wrote this article to be super beginner-friendly and detailed, so even if you have little to no programming background, you’ll still be able to follow along.</p>
<p>Whether you’re a developer, a tech-curious startup, or a business leader trying to understand modern cloud solutions, this guide was written for you.</p>
<p>That said, having some light knowledge in these areas will make the ride even smoother:</p>
<ul>
<li><p>🧑‍💻 Basic Programming Concepts – like how Node.js apps run and what a server does.</p>
</li>
<li><p>💡 Familiarity with Common Tech Terms – words like “deploy,” “application,” “CPU,” and “software” will pop up, but don’t worry: I’ve done my best to break these down into simple, relatable explanations.</p>
</li>
</ul>
<p>No prior cloud experience? No problem! This guide holds your hand all the way from setup to deployment – all in plain language, no jargon.</p>
<p>So buckle up, and let’s proceed with deploying your very own application to AWS Lambda. 😁</p>
<h2 id="heading-how-to-set-up-the-application-using-git">How to Set Up the Application Using Git 🐙</h2>
<p>Before we jump into writing code or deploying anything, the very first step is to grab the application we’ll be working with — and for that, we’ll be using Git.</p>
<p>But wait... what’s Git? — It’s a Version Control System (VCS) that helps developers track changes to their code, collaborate with teammates without stepping on each other’s toes, and safely store their work in a central place — like GitHub.</p>
<h3 id="heading-clone-the-application-repository">Clone the Application Repository 🧑‍💻</h3>
<p>I’ve already created a simple project for us to use in this tutorial — it’s sitting pretty on GitHub, waiting for you.</p>
<p>To clone the project onto your local machine, open up your terminal and run:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/onukwilip/lambda-tutorial.git
</code></pre>
<p>This command will download all the code from the <code>lambda-tutorial</code> repository into a folder on your computer. 📁</p>
<p>Once the cloning is done, navigate into the project directory like this:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> lambda-tutorial
</code></pre>
<p>Boom — just like that, your local machine is now set up with the same code that’s stored in the GitHub repo. 🏡</p>
<h2 id="heading-understanding-the-codebase">Understanding the Codebase 🔎</h2>
<h3 id="heading-open-the-codebase-in-your-favorite-ide">Open the Codebase in Your Favorite IDE 🧑‍💻</h3>
<p>For this tutorial, we’ll be using Visual Studio Code (VS Code), but feel free to use any editor you’re comfortable with.</p>
<p>Once you open the <code>lambda-tutorial</code> project folder, you’ll notice it’s a simple Node.js web server. Nothing too fancy — just a server that can handle requests and respond with some data.</p>
<p>Now, it’s important to understand what’s going on inside our codebase, especially if you’re coming from deploying on platforms like Render, Vercel, or Google Cloud Run.</p>
<h3 id="heading-deploying-to-lambda-vs-other-serverless-platforms"><strong>Deploying to Lambda vs Other Serverless Platforms ⚡</strong></h3>
<p>When you deploy to platforms like Vercel, Render, or Google Cloud Run, you usually package your web server just the way you wrote it – whether it’s a Node.js Express server or a Next.js app – and the platform handles it pretty much as-is.</p>
<p>Those platforms run your server like a mini container (or microservice) that’s always ready to handle incoming traffic, just like a waiter standing by at your table, waiting for your order.</p>
<p>But AWS Lambda works a little differently.</p>
<p>Lambda expects your code to be organized around functions – not full web servers. Think of Lambda as a chef that only shows up the moment an order is placed, cooks the food, and disappears once the job is done. 👨‍🍳🍽️</p>
<p>So if you’ve got a full-blown Node.js Express server, you’ll need to do a tiny bit of “translation” to fit Lambda’s expectations – and that’s where the lambda.js file comes in.</p>
<h4 id="heading-the-lambdajs-file-your-lambda-translator">The <code>lambda.js</code> File — Your Lambda Translator 🔀</h4>
<p>Here’s what the file looks like:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> serverless = <span class="hljs-built_in">require</span>(<span class="hljs-string">"serverless-http"</span>);
<span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./app"</span>);

<span class="hljs-keyword">const</span> handler = serverless(app);
<span class="hljs-built_in">module</span>.exports.handler = handler;
</code></pre>
<p>Let’s break it down:</p>
<ul>
<li><p><code>const serverless = require("serverless-http");</code>: This imports a handy little library called serverless-http. (The <code>serverless-http</code> library is important for our platform to run properly on AWS Lambda.) It acts like a translator: it takes your regular Express app and wraps it so that AWS Lambda can understand it.</p>
</li>
<li><p><code>const handler = serverless(app);</code>: Here’s the magic. This wraps your Express app into a Lambda-compatible function.</p>
</li>
<li><p><code>module.exports.handler = handler;</code>: This exports your wrapped function so AWS Lambda can call it when the application is triggered.</p>
</li>
</ul>
<p>So, instead of starting your server like this:</p>
<pre><code class="lang-javascript">app.listen(<span class="hljs-number">5000</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Server running on port 5000"</span>);
});
</code></pre>
<p>You’re handing your app over to Lambda and letting it handle incoming requests, scale, and run the app only when it’s needed.</p>
<h4 id="heading-the-appjs-file-your-classic-express-app">The <code>app.js</code> File — Your Classic Express App 💻</h4>
<p>Your <code>app.js</code> is where the main application logic lives. Here is usually where you:</p>
<ul>
<li><p>Set up Express.</p>
</li>
<li><p>Define routes (like <code>/api</code>, <code>/users</code>, <code>/hello</code>).</p>
</li>
<li><p>Apply middleware (like JSON parsing, logging, CORS, and so on).</p>
</li>
<li><p>Handle HTTP requests and send back responses.</p>
</li>
</ul>
<p>In a normal deployment (Render, Google Cloud Run, DigitalOcean, or your own server), you’d start the server using <code>app.listen(PORT)</code> at the bottom of this file.</p>
<p>But since we’re deploying to Lambda, you don’t directly start the server here. Instead, you export the <code>app</code> like this:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = app;
</code></pre>
<p>This way, your application stays “server-agnostic” – it’s not hardcoded to run on a traditional server. Lambda (via the <code>lambda.js</code> file) takes care of starting and stopping your app whenever it’s triggered by an event (like an HTTP request). Smart, right? 💡</p>
<p>Why this setup? 🤔</p>
<p>This little separation gives you flexibility:</p>
<ul>
<li><p>You can write your Node.js app like you always would (using <code>Express</code>) inside <code>app.js</code>.</p>
</li>
<li><p>And you only tweak the entry point (via <code>lambda.js</code>) to fit AWS Lambda’s expectations.</p>
</li>
</ul>
<h2 id="heading-how-to-create-a-docker-image-of-the-application">How to Create a Docker Image of the Application 🐋</h2>
<p>Now that we’ve had a good look at the code, let’s package it up the smart way — using Docker.</p>
<h3 id="heading-what-is-docker">What is Docker? 🐳</h3>
<p>Now, you might be wondering, <em>"Why are we using Docker?"</em></p>
<p>Docker is a software for creating images of your applications and running those images as containers. Just like real-world shipping containers hold goods securely, Docker containers hold your app, bundled with everything it needs to run: its code, libraries, dependencies, and settings. Everything is all wrapped up neatly, so your app runs the same way everywhere, whether on your laptop, AWS Lambda, or even your friend’s machine.</p>
<h3 id="heading-lets-take-a-look-at-the-dockerfile">Let’s Take a Look at the Dockerfile 🔍</h3>
<p>Inside your project folder, you’ll find a file named <code>Dockerfile</code>. This is basically the recipe that Docker uses to build your app’s container image.</p>
<p>Here’s what it looks like:</p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-slim AS builder

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-keyword">COPY</span><span class="bash"> package.json .</span>

<span class="hljs-keyword">RUN</span><span class="bash"> npm i -f</span>

<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-keyword">USER</span> root

<span class="hljs-keyword">FROM</span> amazon/aws-lambda-nodejs

<span class="hljs-keyword">ENV</span> PORT=<span class="hljs-number">5000</span>

<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/ <span class="hljs-variable">${LAMBDA_TASK_ROOT}</span></span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/node_modules <span class="hljs-variable">${LAMBDA_TASK_ROOT}</span>/node_modules</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/package.json <span class="hljs-variable">${LAMBDA_TASK_ROOT}</span></span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/package-lock.json <span class="hljs-variable">${LAMBDA_TASK_ROOT}</span></span>

<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">5000</span>

<span class="hljs-keyword">CMD</span><span class="bash"> [ <span class="hljs-string">"lambda.handler"</span> ]</span>
</code></pre>
<p>Let’s break down the important steps— in plain English: 😎</p>
<ul>
<li><p><code>FROM node:18-slim AS builder</code>: We start by using a lightweight version of Node.js called <code>node:18-slim</code> and give it a tag named <code>builder</code> (think of it as Stage 1). This gives us the tools we need to build a Node.js app, but without extra stuff that makes the image heavy. The tag <code>builder</code> enables us to re-use the content of this build in the next stage</p>
</li>
<li><p><code>WORKDIR /app</code>: We set the working directory inside the container to <code>/app</code>. Think of this as telling Docker: <em>"Hey, this is the folder where I’ll be working from!"</em></p>
</li>
<li><p><code>COPY package.json .</code>: This copies the <code>package.json</code> file (which lists your app’s dependencies) into the <code>/app</code> folder inside the container.</p>
</li>
<li><p><code>RUN npm i -f</code>: This installs all the Node.js dependencies (the packages your app needs to work).<br>  The <code>-f</code> flag forces npm to resolve conflicts if any pop up.</p>
</li>
<li><p><code>COPY . .</code>: This copies the rest of your project files from your computer into the container.</p>
</li>
<li><p><code>USER root</code>: This sets the user to root (administrator level) inside the container. Useful when extra permissions are needed for certain tasks.</p>
</li>
<li><p><code>FROM amazon/aws-lambda-nodejs</code>: Now here’s the switch: we swap to the official AWS Lambda base image for Node.js! That is, Stage 2. This image is designed to work smoothly when deploying containers to Lambda.</p>
</li>
<li><p><code>ENV PORT=5000</code>: We set an environment variable for the server port. Our app will listen on port 5000.</p>
</li>
<li><p><code>COPY --from=builder /app/ ${LAMBDA_TASK_ROOT}</code>: This grabs all the files from the builder stage and copies them into Lambda’s special working directory (<code>${LAMBDA_TASK_ROOT}</code>).</p>
</li>
<li><p><code>COPY --from=builder /app/node_modules ${LAMBDA_TASK_ROOT}/node_modules</code>: Same thing, but this one specifically copies the node_modules folder (all your installed dependencies) into Lambda’s working directory.</p>
</li>
<li><p><code>COPY --from=builder /app/package.json ${LAMBDA_TASK_ROOT}</code>: Copies the <code>package.json</code> file into Lambda’s working directory.</p>
</li>
<li><p><code>COPY --from=builder /app/package-lock.json ${LAMBDA_TASK_ROOT}</code>: Copies the lock file for your dependencies – so Lambda knows exactly which versions of libraries to use.</p>
</li>
<li><p><code>EXPOSE 5000</code>: This tells Docker, <em>“Hey, my app is going to listen for requests on port 5000!"</em> (Though Lambda doesn’t use this directly, it’s useful for local testing.)</p>
</li>
<li><p><code>CMD [ "lambda.handler" ]</code>: This tells AWS Lambda which function to run when the container starts.<br>  In this case, it’s looking for a <code>handler</code> function inside your app – that’s the entry point!</p>
</li>
</ul>
<h3 id="heading-how-to-create-our-own-docker-image">How to Create Our Own Docker Image</h3>
<p>Before we proceed, you need to have Docker running on your machine. If you haven’t installed Docker yet, check out the official installation guide here: <a target="_blank" href="https://docs.docker.com/engine/install/">Docker Installation Tutorial</a>. It’s a great resource to get Docker up and running.</p>
<h4 id="heading-ensure-docker-is-running">Ensure Docker is Running</h4>
<p>Make sure Docker Desktop is installed and running. You can usually tell by the Docker icon in your system tray. If it’s not running, start it up before proceeding.</p>
<h4 id="heading-build-the-docker-image">Build the Docker Image</h4>
<p>Now, it’s time to create a Docker image of our application. In your terminal, navigate to the root directory of your project (where your Dockerfile is located). Then run the following command:</p>
<pre><code class="lang-bash">docker build -t demo-lambda-project:latest .
</code></pre>
<ul>
<li><p>The <code>docker build</code> command tells Docker to create an image.</p>
</li>
<li><p>The <code>-t demo-lambda-project:latest</code> flag assigns a tag (or name) to your image (we’ll change this later to the image naming convention supported by AWS Elastic Container Registry – ECR).</p>
<ul>
<li>Here, <code>demo-lambda-project</code> is the name, and <code>latest</code> is the tag indicating the most recent build.</li>
</ul>
</li>
<li><p>The <code>.</code> at the end tells Docker to look for the Dockerfile in the current directory.</p>
</li>
</ul>
<h4 id="heading-what-this-does">What This Does</h4>
<p>Docker will now follow the instructions in your Dockerfile step-by-step. It starts by building your Node.js app (using the lightweight Node 18 image), installs the dependencies, and then copies everything over to an AWS Lambda-ready image. Once done, you have a neat image tagged as <code>demo-lambda-project:latest</code> that’s ready for deployment.</p>
<h2 id="heading-how-to-create-a-container-registry-on-aws-elastic-container-registry-ecr">How to Create a Container Registry on AWS Elastic Container Registry (ECR) 📁</h2>
<p>Okay, let’s dive into creating an image registry on AWS Elastic Container Registry (ECR). Follow these steps closely to set up your repository named lambda-practice:</p>
<h3 id="heading-step-1-sign-in-and-navigate-to-aws-ecr">Step 1: Sign In and Navigate to AWS ECR</h3>
<p>Log in to your AWS Management Console: <a target="_blank" href="https://console.aws.amazon.com/console/home">https://console.aws.amazon.com/console/home</a>.</p>
<p>In the search bar at the top, type "ECR". You should see Amazon ECR pop up in the dropdown results. Click on it to navigate to the Elastic Container Registry section.</p>
<h3 id="heading-step-2-start-creating-your-repository">Step 2: Start Creating Your Repository</h3>
<p>Once you’re in the ECR section, look for a button that says "Create repository". Click this button to start setting up your new container registry.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744649904087/615bbd21-c6ed-4243-9a18-10042eec9634.png" alt="Create new AWS ECR repository" class="image--center mx-auto" width="1658" height="304" loading="lazy"></p>
<h3 id="heading-step-3-configuring-the-repository-details">Step 3: Configuring the Repository Details</h3>
<p>You’ll need to add some info like:</p>
<ul>
<li><p><strong>Repository name:</strong> In the form that appears, enter <code>lambda-practice</code> as the repository name. This name will be used to reference your repository later when uploading your Docker image.</p>
</li>
<li><p><strong>Tag mutability:</strong> You’ll also see an option for Tag Mutability. For this tutorial, set it to Mutable. This means that if you need to update or change a tag on your image later, you can do so. (Keep in mind that in some scenarios, you might want immutable tags for images used in production environments – but mutable tags are great for testing and development, especially since we want to use the tag <code>latest</code> for our images.)</p>
</li>
</ul>
<p>When you’re happy with the settings, click the "Create repository" button at the bottom of the form.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744650070919/3010590f-f2e3-4d52-9631-8c5d4e1a5239.png" alt="Configure AWS ECR repository" class="image--center mx-auto" width="923" height="335" loading="lazy"></p>
<h3 id="heading-repository-created-now-lets-take-a-look">Repository Created – Now Let's Take a Look</h3>
<p>After creating the repository, AWS will redirect you to the page listing your repositories.</p>
<p>Find the repository named <code>lambda-practice</code> in the list. This is your newly created container registry where you can push Docker images.</p>
<p>Copy the <code>lambda-practice</code> repository URI, which we’ll need later when we push our image from our local machine. The URI should be in a format similar to this - <code>&lt;aws_account_id&gt;.dkr.ecr.&lt;region&gt;.amazonaws.com/lambda-practice</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744650192129/67d724c7-15da-4ff1-8e38-638c3a8d1aa4.png" alt="Completed creation of AWS ECR repository" class="image--center mx-auto" width="1618" height="250" loading="lazy"></p>
<p>And that’s it! You’ve now successfully created a container registry on AWS ECR and have your repository (<code>lambda-practice</code>) ready to receive your Docker image. 🚀</p>
<h2 id="heading-iam-with-aws-how-to-create-a-user-on-aws-iam-to-allow-access-to-your-aws-ecr">IAM with AWS: How to Create a User on AWS IAM to Allow Access to Your AWS ECR 👤🔐</h2>
<p>Now that we’ve successfully created our AWS ECR container registry (the home for our Docker image), it's time to make sure our local machine has the necessary permissions to interact with that registry. Without proper authorization, we won’t be able to upload our image.</p>
<p>To do that, we’ll create an IAM user with the appropriate permissions.</p>
<h3 id="heading-step-1-access-the-iam-console">Step 1: Access the IAM Console</h3>
<p>Start by logging in to your AWS Management Console: <a target="_blank" href="https://console.aws.amazon.com/console/home">https://console.aws.amazon.com/console/home</a>.</p>
<p>In the search bar at the top, type "IAM" and select the IAM service from the dropdown. This brings you to the IAM dashboard where you can manage users, roles, policies, and more.</p>
<h3 id="heading-step-2-navigate-to-the-users-section">Step 2: Navigate to the Users Section</h3>
<p>On the left sidebar of the IAM dashboard, click on "Users". Here you'll see a list of existing users, and this is where you'll add a new one.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744651384601/085a25ca-82eb-447b-8106-46df32264a85.png" alt="Create AWS IAM User" class="image--center mx-auto" width="1907" height="283" loading="lazy"></p>
<h3 id="heading-step-3-create-a-new-user">Step 3: Create a New User</h3>
<p>Click the "Add users" button at the top. In the "Set user details" step, enter the username as <code>lambda-practice</code>.</p>
<h3 id="heading-step-4-attach-permissions-directly">Step 4: Attach Permissions Directly</h3>
<p>In the "Set permissions" step, choose "Attach policies directly". In the search box, type <code>AmazonEC2ContainerRegistryPowerUser</code>. Select the <code>AmazonEC2ContainerRegistryPowerUser</code> policy by ticking its checkbox. This policy grants the necessary permissions to work with AWS ECR, such as pushing and pulling Docker images.</p>
<p>Click Next, and verify that the username is <code>lambda-practice</code> and that the AmazonEC2ContainerRegistryPowerUser policy is attached. If everything looks good, click "Create user".</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744651476901/c6d91c8c-9757-4cc6-a00f-c23d3a72de59.png" alt="Add policy to AWS IAM User" class="image--center mx-auto" width="1789" height="704" loading="lazy"></p>
<h3 id="heading-step-5-generate-access-keys-for-the-user">Step 5: Generate Access Keys for the User</h3>
<p>Once the user is created, you’ll be redirected to the page listing all IAM users. Locate and click on the user <code>lambda-practice</code>. This action will take you to the user’s summary page.</p>
<ul>
<li><p>Navigate to the "Security credentials" tab.</p>
</li>
<li><p>Under "Access keys", click the "Create access key" button.</p>
</li>
<li><p>A page will appear for configuring the new access key.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744652284582/f6a586e9-d09e-467f-ad12-81ccf538bc34.png" alt="Create Access key for AWS IAM User" class="image--center mx-auto" width="1482" height="305" loading="lazy"></p>
<p>In the "Access key best practices &amp; alternatives" step, select "Command Line Interface (CLI)".</p>
<p><strong>Why should you select this option?</strong> Choosing CLI ensures that the generated access key is optimized for use with the AWS CLI and other command-line tools (like Docker commands that push images to ECR), which is exactly what we need for our workflow.</p>
<p>Leave the other configurations as their default settings, and then click "Create access key".</p>
<p>Once the key is created, you’ll see the new Access key ID and Secret access key. Make sure to copy and store these credentials securely. They are essential for authorizing your local machine to access AWS ECR and perform operations with the permissions assigned to the <code>lambda-practice</code> user.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744652339772/c3d94e2a-f823-4d73-9a46-ab4d829289e9.png" alt="Completed creation of Access key for AWS IAM User" class="image--center mx-auto" width="1471" height="523" loading="lazy"></p>
<h3 id="heading-how-to-authorize-your-local-pc-to-publish-images-to-the-aws-ecr-repository"><strong>How to Authorize Your Local PC to Publish Images to the AWS ECR Repository</strong></h3>
<p>Now that we have our IAM user set up and the access keys in hand, it’s time to authenticate our local PC so we can securely push our Docker images to AWS ECR using the AWS CLI. Follow these steps:</p>
<h4 id="heading-step-1-install-the-aws-cli">Step 1: Install the AWS CLI</h4>
<p>If you haven’t installed the AWS CLI on your machine yet, download and install it using the official guide here: <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html">Install the AWS CLI</a>.</p>
<p>This tool allows you to interact with your AWS account right from the command line, which is essential for pushing images to ECR.</p>
<h4 id="heading-step-2-configure-your-aws-cli-credentials">Step 2: Configure Your AWS CLI Credentials</h4>
<p>Once installed, you need to configure your AWS CLI to use the credentials associated with the <code>lambda-practice</code> user. Open your terminal and run the following command to set up a new profile named <code>lambda</code>:</p>
<pre><code class="lang-bash">aws configure --profile lambda
</code></pre>
<p>You’ll be prompted to enter the following details:</p>
<ul>
<li><p><strong>AWS Access Key ID:</strong> Paste the access key ID that you generated for the <code>lambda-practice</code> user.</p>
</li>
<li><p><strong>AWS Secret Access Key:</strong> Paste the corresponding secret access key.</p>
</li>
<li><p><strong>Default region name:</strong> Enter your preferred AWS region (for example, <code>us-east-1</code> or your relevant region).</p>
</li>
<li><p><strong>Default output format:</strong> You can leave this as <code>json</code> or choose your preferred format.</p>
</li>
</ul>
<p>This command configures a new CLI profile called <code>lambda</code> with the credentials of our IAM user.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744652931837/650c93af-25f0-4d7b-a202-50d825a6b77a.png" alt="Authenticate and authorize AWS CLI with AWS IAM User Access key" class="image--center mx-auto" width="798" height="188" loading="lazy"></p>
<h4 id="heading-step-3-verify-the-configuration">Step 3: Verify the Configuration</h4>
<p>To ensure everything is set up correctly, run:</p>
<pre><code class="lang-bash">aws sts get-caller-identity --profile lambda
</code></pre>
<p>This command will return details about the IAM user configured for the <code>lambda</code> profile, confirming that your local PC is now authenticated correctly.</p>
<p>Now you’re all set! Your AWS CLI is configured with the <code>lambda</code> profile, meaning your local machine has the right credentials to interact with your AWS ECR repository and push Docker images using the permissions assigned to your <code>lambda-practice</code> IAM user.</p>
<h2 id="heading-how-to-upload-your-docker-image-to-the-aws-ecr-repository">How to Upload Your Docker Image to the AWS ECR repository ⬆️</h2>
<p>Uploading your Docker image to AWS ECR is the moment when your hard work gets sent off to your repository so AWS Lambda can later grab and run your container. Now that your PC is authorized to talk to ECR, let’s take a look at how to upload the image.</p>
<h3 id="heading-step-1-log-in-to-ecr-with-docker">Step 1: Log in to ECR with Docker</h3>
<p>Before you can push your image, you need to authenticate Docker to your AWS ECR account. You do this by running a command that gets an authentication token from AWS and pipes it to Docker. For example:</p>
<pre><code class="lang-bash">aws ecr get-login-password --region &lt;YOUR_REGION&gt; --profile lambda | docker login --username AWS --password-stdin &lt;YOUR_AWS_ACCOUNT_ID&gt;.dkr.ecr.&lt;YOUR_REGION&gt;.amazonaws.com
</code></pre>
<p>Let’s break it down:</p>
<ul>
<li><p><code>aws ecr get-login-password --region &lt;YOUR_REGION&gt; --profile lambda</code>: This part uses the AWS CLI to get a temporary login password for ECR. Be sure to replace <code>&lt;YOUR_REGION&gt;</code> with the region in which your ECR repository was created (for example, <code>us-east-1</code>).</p>
</li>
<li><p><code>| docker login --username AWS --password-stdin &lt;YOUR_AWS_ACCOUNT_ID&gt;.dkr.ecr.&lt;YOUR_REGION&gt;.</code><a target="_blank" href="http://amazonaws.com"><code>amazonaws.com</code></a>: The pipe (<code>|</code>) takes the password from the AWS CLI command and passes it as input to <code>docker login</code>. The login command then logs Docker into ECR using the provided username (<code>AWS</code>) and the password. Replace <code>&lt;YOUR_AWS_ACCOUNT_ID&gt;</code> with your actual AWS account ID.</p>
</li>
</ul>
<h3 id="heading-step-2-environment-considerations">Step 2: Environment Considerations</h3>
<p>This command works on shell environments like Powershell, zsh, and bash.</p>
<p><strong>Windows Users (CMD)</strong>:<br>If you’re using the classic Windows Command Prompt (CMD), the piping syntax might not work the same way. In that case, you might consider using Windows PowerShell or Git Bash. Alternatively, you can run the command in an environment like Windows Subsystem for Linux (WSL).</p>
<h4 id="heading-why-use-the-correct-region">Why Use the Correct Region?</h4>
<p>It is crucial to use the exact region where your ECR repository was created. The region is a part of your repository URI. If you use the wrong region, the login will fail because it won’t find the correct repository endpoint.</p>
<h4 id="heading-how-to-check-the-region">How to Check the Region:</h4>
<p>Log in to your AWS Console, navigate to the ECR section, and select your repository. The URI will look similar to this: <code>&lt;YOUR_AWS_ACCOUNT_ID&gt;.dkr.ecr.&lt;YOUR_REGION&gt;.amazonaws.com/lambda-practice</code>. Here, <code>&lt;YOUR_REGION&gt;</code> is the region you must use in your login command.</p>
<h3 id="heading-step-3-build-your-docker-image-with-the-correct-tag">Step 3: Build Your Docker Image with the Correct Tag</h3>
<p>Before pushing the image to ECR, you need to build it on your local machine and tag it with your repository’s name. In your terminal, navigate to your project’s root folder (where your Dockerfile is located), then run (replace <code>&lt;YOUR_AWS_ACCOUNT_ID&gt;</code> and <code>&lt;YOUR_REGION&gt;</code> placeholders with your AWS Account ID and AWS ECR repository region):</p>
<pre><code class="lang-bash">docker build -t &lt;YOUR_AWS_ACCOUNT_ID&gt;.dkr.ecr.&lt;YOUR_REGION&gt;.amazonaws.com/lambda-practice:latest
</code></pre>
<h3 id="heading-step-4-push-your-docker-image-to-aws-ecr">Step 4: Push Your Docker Image to AWS ECR</h3>
<p>Once your image is built and tagged, it’s time to push it to your remote ECR repository. Run the following command:</p>
<pre><code class="lang-bash">docker push &lt;YOUR_AWS_ACCOUNT_ID&gt;.dkr.ecr.&lt;YOUR_REGION&gt;.amazonaws.com/lambda-practice:latest
</code></pre>
<p>This command tells Docker to upload (or “push”) your image to the repository you created earlier.</p>
<ul>
<li><p>Make sure the repository URI and tag match what you used in the build command.</p>
</li>
<li><p>Remember, if you use a different region than the one in your repository URI, the push will fail because AWS won’t recognize the repository endpoint.</p>
</li>
</ul>
<h2 id="heading-how-to-deploy-the-application-container-to-aws-lambda-from-the-image-on-aws-ecr">How to Deploy the Application Container to AWS Lambda from the Image on AWS ECR 🚀</h2>
<p>You can deploy your function on AWS Lambda in several ways, each catering to different use cases. Here’s a quick rundown:</p>
<ol>
<li><p><strong>ZIP file upload:</strong> Simply compress your code and dependencies into a ZIP file, then upload it directly via the AWS Lambda console. This traditional method is great for small codebases that don’t require custom runtimes.</p>
</li>
<li><p><strong>Direct editing in the console:</strong> Write or edit your function code directly in the AWS Lambda code editor. Handy for quick tweaks, but not ideal for larger projects.</p>
</li>
<li><p><strong>Container image:</strong> Package your application as a Docker container image and deploy it. This approach is particularly useful if you have complex dependencies, need a custom runtime, or want consistent environments across development and production.</p>
</li>
</ol>
<p>In this tutorial, we’re taking the container image route because it offers flexibility, consistency, and scalability – all while letting us reuse our existing Docker configuration. Let’s walk through the steps for deploying your containerized application to AWS Lambda:</p>
<h3 id="heading-step-1-access-the-aws-lambda-console">Step 1: Access the AWS Lambda Console</h3>
<p>Log into your AWS Management Console. In the search bar at the top, type "Lambda" and select the AWS Lambda service from the dropdown results.</p>
<h3 id="heading-step-2-create-a-new-lambda-function">Step 2: Create a New Lambda Function</h3>
<p>Once on the Lambda page, click the "Create function" button. You’ll see multiple function creation options. For our purposes, select the "Container image" option. This choice tells AWS that you’ll be deploying a containerized application instead of uploading a ZIP file.</p>
<h3 id="heading-step-3-name-your-function">Step 3: Name Your Function</h3>
<p>In the function setup screen, enter <code>lambda-practice</code> as the name of your new Lambda function. This name identifies your function in AWS.</p>
<h3 id="heading-step-4-configure-the-container-image">Step 4: Configure the Container Image</h3>
<p>Under the “Container image” settings, click the "Browse images" button. A new window should appear, listing your available images from AWS Elastic Container Registry (ECR).</p>
<p>Select the repository you previously created (for instance, the one named <code>lambda-practice</code>), and pick the image tagged as <code>latest</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744655907615/df0e3576-5fe6-43a7-8da5-d2964b36a2af.png" alt="Create AWS Lambda function" class="image--center mx-auto" width="1720" height="489" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744655978526/fafd6b35-579a-4439-b15e-dd5e3dba2acf.png" alt="Connect AWS ECR image to AWS lambda Function" class="image--center mx-auto" width="1237" height="377" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744656031049/3de3bcc1-2034-4518-acb6-84adb6136752.png" alt="Select Image from AWS ECR repository" class="image--center mx-auto" width="1809" height="393" loading="lazy"></p>
<h3 id="heading-step-5-finalize-and-create">Step 5: Finalize and Create</h3>
<p>Now you’ll want to review the basic settings. In this step, you might also configure additional options such as memory allocation, timeout limits, and environment variables, depending on your application needs.</p>
<p>Once everything is set, click "Create function" to finalize the deployment.</p>
<h3 id="heading-how-to-enable-access-to-your-lambda-function">How to Enable Access to Your Lambda Function</h3>
<p>Awesome – hurray, you’ve successfully deployed your image from AWS ECR to AWS Lambda! Now the next step is to make sure your function is up and running and can be triggered properly. But you might be wondering, “How do I actually access my Lambda function to see if it’s working?” Let's break it down:</p>
<h4 id="heading-understanding-lambda-function-triggers">Understanding Lambda Function Triggers</h4>
<p>There are several ways to invoke a Lambda function, and AWS supports multiple trigger options. Here are a few:</p>
<ul>
<li><p><strong>Event Source Mapping:</strong> Automatically triggers your function in response to changes in services like DynamoDB, Kinesis, or S3.</p>
</li>
<li><p><strong>Scheduled Events:</strong> Set up cron-like scheduled invocations via Amazon CloudWatch Events.</p>
</li>
<li><p><strong>API Gateway:</strong> Create RESTful APIs that call your function.</p>
</li>
<li><p><strong>AWS SDK/CLI:</strong> Directly invoke the function using the AWS SDK or CLI commands.</p>
</li>
<li><p><strong>Function URLs:</strong> A simple way to expose your function over HTTPS, giving you a public URL that users or applications can call directly.</p>
</li>
</ul>
<p>In this tutorial, we’re going to use a Function URL to trigger our Lambda function via an HTTP event. This method allows you to invoke your function from the public internet and is perfect for testing or building public-facing APIs.</p>
<h3 id="heading-how-to-create-a-function-url-for-your-lambda-function">How to Create a Function URL for Your Lambda Function</h3>
<p>Now that you're on your Lambda function's details page, here’s how to create a Function URL step-by-step:</p>
<p>First, on your Lambda function’s page, click the "Configuration" tab at the top. Within the Configuration section, find and select the "Function URL" sub-tab. This is where you manage the public URL for your function.</p>
<p>Click on the "Create Function URL" button. This will open a new configuration screen for setting up your Function URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744656877335/835422c5-8c88-418a-b1f2-3650360069c3.png" alt="Create Function URL for AWS Lambda Function" class="image--center mx-auto" width="1633" height="701" loading="lazy"></p>
<ul>
<li><p><strong>Authentication type:</strong> Set the Auth type to NONE. This setting allows public, unauthenticated access to your function from the internet, which means anyone with the URL can invoke it. (This is great for testing or building public services, but be cautious with security in production environments!)</p>
</li>
<li><p><strong>Additional settings:</strong> Under the Additional Settings section, enable Configure cross-origin resource sharing (CORS). This is useful if you plan to call your function from client-side applications hosted on different domains. Think of it as opening a window for your app to communicate with other web pages or services.</p>
</li>
</ul>
<p>After configuring your settings, click the appropriate button to create or save the Function URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744656860868/cd98ce34-7fdf-4cb6-be85-a25d3718e2e6.png" alt="Configure AWS Function URL for AWS Lambda Function" class="image--center mx-auto" width="1625" height="760" loading="lazy"></p>
<h4 id="heading-verify-your-function-url">Verify Your Function URL</h4>
<p>Once configured, you’ll see the Function URL displayed on the same page. You can now copy this URL.</p>
<p>Paste the URL into a browser or use tools like <code>curl</code> or Postman to send an HTTP request, triggering your Lambda function and verifying that it works as expected.</p>
<p>You should get a response just like this on your browser:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744656939019/fcda2621-8057-438b-8d5a-8ac8936b6322.png" alt="Deployed application on AWS Lambda" class="image--center mx-auto" width="1307" height="378" loading="lazy"></p>
<p>And that’s it! You’ve successfully set up a public HTTP endpoint that triggers your AWS Lambda function. Whether you're testing your deployment or building a public-facing API, the Function URL makes it easy for anyone to interact with your function.</p>
<h3 id="heading-congrats-you-did-it"><strong>Congrats — You did it!</strong></h3>
<p>You've just walked through the entire journey of deploying a Node.js web server, containerized with Docker, all the way to AWS Lambda using AWS ECR as your image repository. 🚀</p>
<p>From writing and containerizing your Node.js application, creating an AWS ECR repository, setting up IAM users and access keys, pushing your Docker image to ECR, to deploying it on Lambda – you’ve covered it all like a pro. 💪</p>
<p>Not only that, but you also configured a public-facing Function URL so your serverless app can now handle requests from anywhere in the world 🌍.</p>
<p>You’ve just combined modern cloud-native workflows with serverless deployment – giving you flexibility, scalability, and lightning-fast response times without the headache of managing servers 😁.</p>
<p>👏 Give yourself a pat on the back. You’ve officially containerized and deployed your Node.js web server to AWS Lambda!</p>
<h2 id="heading-advantages-of-adopting-the-serverless-model-in-businesses">Advantages of Adopting the Serverless Model in Businesses 💼</h2>
<p>When it comes to deploying applications in the cloud, the serverless model has truly flipped the old playbook and has helped businesses save on Cloud costs! Let’s break it down in simple, real-world terms.</p>
<h3 id="heading-cost-efficiency"><strong>Cost-Efficiency 💰</strong></h3>
<p>For most businesses – especially startups – serverless offers a major financial advantage. Here’s why:</p>
<p>In traditional models like IaaS (Infrastructure as a Service) and PaaS (Platform as a Service), such as using AWS EC2 or AWS Elastic Beanstalk, you provision resources upfront.</p>
<p>For example: You spin up a server with 4 GB RAM and 4 vCPUs, and AWS charges you $100/month (this covers 730 hours – the whole month). Even if your app barely does anything – say it only serves real requests for 120 hours, and uses just 1 GB of memory – you still pay the full $100, because the resources were reserved and waiting for traffic 24/7.</p>
<p>But with Serverless:</p>
<ul>
<li><p>You don’t pre-allocate or reserve compute power.</p>
</li>
<li><p>Your application only runs when someone actually needs it (for example, when a user makes an HTTP request).</p>
</li>
<li><p>You only pay for the actual execution time and the resources used.</p>
</li>
</ul>
<p>For instance, if your function only runs for 50 hours in a month and uses 1.5 GB RAM, you might pay something like $30, compared to the flat $100 you'd have paid on EC2 or Elastic Beanstalk.</p>
<h3 id="heading-scalability-without-stress"><strong>Scalability Without Stress 📈</strong></h3>
<p>Serverless platforms like AWS Lambda automatically handle:</p>
<ul>
<li><p>Scaling up during high demand.</p>
</li>
<li><p>Scaling down to zero when idle.</p>
</li>
</ul>
<p>This means your team won’t need to predict or provision for resources during traffic surges. Whether 1 or 1 million users visit your app, the cloud provider handles the rest.</p>
<h3 id="heading-simplified-operations"><strong>Simplified Operations ⚙️</strong></h3>
<p>For your software team:</p>
<ul>
<li><p>No more babysitting servers, patching security updates, or worrying about load balancers.</p>
</li>
<li><p>You focus purely on writing the business logic and shipping code.</p>
</li>
<li><p>The cloud provider handles the infrastructure behind the scenes.</p>
</li>
</ul>
<p>This frees up your team’s time, cuts maintenance tasks, and speeds up development times.</p>
<h3 id="heading-better-return-on-investment-roi"><strong>Better Return on Investment (ROI) 📊</strong></h3>
<p>Because you only pay for what you use, the cost-to-value ratio improves significantly. Startups and businesses can:</p>
<ul>
<li><p>Launch faster.</p>
</li>
<li><p>Experiment without financial risk.</p>
</li>
<li><p>Scale without surprise bills.</p>
</li>
<li><p>Avoid overpaying for idle resources.</p>
</li>
</ul>
<h2 id="heading-disadvantages-of-the-serverless-model">Disadvantages of the Serverless Model 🚫</h2>
<p>As exciting and cost-friendly as the serverless model seems, the golden rule in tech still applies:<br>every solution comes with trade-offs.</p>
<p>Let’s walk through a few important downsides you should consider:</p>
<h3 id="heading-no-built-in-support-for-background-jobs"><strong>No Built-in Support for Background Jobs ⏰</strong></h3>
<p>Unlike traditional servers where you can run background processes – like sending out newsletters at midnight or cleaning up databases at scheduled times – serverless platforms such as AWS Lambda don’t natively support background tasks or recurring jobs.</p>
<p>For example, let’s say you wanted your app to automatically generate reports every day at 3 AM. In a typical server setup, you’d just write a cron job and call it a day.</p>
<p>But with Lambda or serverless, you can’t do this directly inside your deployed function. Instead, you need external tools like:</p>
<ul>
<li><p>AWS EventBridge (for scheduling and triggering Lambda functions)</p>
</li>
<li><p>Or other cloud-native schedulers.</p>
</li>
</ul>
<p>This adds a bit of extra setup, management, and sometimes extra cost.</p>
<h3 id="heading-unpredictable-cloud-costs"><strong>Unpredictable Cloud Costs 💸</strong></h3>
<p>One of the biggest selling points of serverless is “pay-as-you-use” – but this can also become a financial blind spot, because:</p>
<ul>
<li><p>Costs depend on traffic volume and resource usage.</p>
</li>
<li><p>If your app suddenly goes viral or experiences a traffic spike, your cloud bill could skyrocket without warning.</p>
</li>
</ul>
<p>For example, an app that runs stable at $30/month for low traffic could unexpectedly hit $1000+ if a marketing campaign or external event drives huge numbers of users to your service. While this means your app is succeeding, your budget might take a hit.</p>
<p>In contrast, with traditional models like AWS EC2 or Elastic Beanstalk, your costs are usually predictable – even if your server sits idle all month.</p>
<h2 id="heading-when-to-adopt-the-serverless-model">When to Adopt the Serverless Model 🤔</h2>
<p>So, is Serverless always the right choice? Not necessarily!</p>
<p>If you expect:</p>
<ul>
<li><p><strong>Steady, predictable workloads,</strong> EC2 or Elastic Beanstalk might offer more cost certainty.</p>
</li>
<li><p><strong>Long-running background tasks</strong>, serverless isn’t ideal without extra services.</p>
</li>
<li><p><strong>Real-time control over resource limits</strong>, traditional servers give you more flexibility.</p>
</li>
</ul>
<p>But if your app has burst traffic (users come and go), event-driven logic (like APIs or webhooks), or you want minimal ops overhead, then Serverless can save time, effort, and money.</p>
<h3 id="heading-when-serverless-is-the-perfect-fit-a-startup-building-an-event-driven-api"><strong>When Serverless is the Perfect Fit: A Startup Building an Event-Driven API</strong></h3>
<p>Imagine you’re running a small tech startup that just launched an app for booking fitness classes. Your team is small, budgets are tight, and traffic is unpredictable – some days you have 50 users, some days 5,000.</p>
<p>In this case:</p>
<ul>
<li><p>Your backend mostly handles HTTP requests: new sign-ups, class bookings, cancellations, and payments.</p>
</li>
<li><p>Traffic spikes during lunch breaks and weekends, but is quiet at night.</p>
</li>
<li><p>You don’t want to hire a full-time DevOps engineer just to manage servers.</p>
</li>
</ul>
<p>👉 <strong>Why Serverless is perfect in this case:</strong></p>
<ul>
<li><p>You only pay when people use your app.</p>
</li>
<li><p>No need to manage or provision servers.</p>
</li>
<li><p>AWS Lambda auto-scales based on demand.</p>
</li>
<li><p>Fast to deploy, easy to connect to other AWS services (like DynamoDB for your database, S3 for images, and SES for emails).</p>
</li>
</ul>
<p>By using Serverless in this case, you can save money, scale automatically, and stay laser-focused on features – not infrastructure.</p>
<h3 id="heading-when-serverless-is-not-a-good-fit-a-video-streaming-platform"><strong>When Serverless is Not a Good Fit: A Video Streaming Platform</strong></h3>
<p>Now imagine you’re building the next YouTube-like service for a niche audience – say, education-based content for universities.</p>
<p>In this case:</p>
<ul>
<li><p>Your platform requires continuous background processing: encoding videos, generating thumbnails, and pushing them to CDN.</p>
</li>
<li><p>Users stream content 24/7, meaning your app is always under load.</p>
</li>
<li><p>Background jobs like recommendation engine updates or nightly reports need to run frequently.</p>
</li>
</ul>
<p>👉 <strong>Why Serverless might be a bad idea:</strong></p>
<ul>
<li><p>Functions like AWS Lambda have a timeout limit (for example 15 minutes max per execution).</p>
</li>
<li><p>Continuous processing or streaming doesn’t fit the on-demand, short-lived nature of serverless.</p>
</li>
<li><p>Costs could skyrocket since the app runs almost all the time, making it more expensive than a dedicated EC2 or Kubernetes cluster.</p>
</li>
</ul>
<p><strong>Better alternative:</strong><br>For this kind of use case, a traditional server-based setup – like EC2 or container orchestration via ECS or Kubernetes – would offer more control, predictable pricing, and support for long-running processes</p>
<p>✅ <strong>Bottom line:</strong><br>Serverless is fantastic for modern apps, but like any tool, it’s best used when its strengths match your project’s needs.</p>
<h2 id="heading-conclusion">Conclusion 📝</h2>
<p>Congratulations on making it to the end of this tutorial! 🚀</p>
<p>In this article, we explored the power of serverless computing by walking step-by-step through the process of deploying a Node.js web server using Docker and AWS Lambda.</p>
<p>From building your container image, pushing it to AWS ECR, and finally deploying it on Lambda – you’ve now seen how easy it is to get an app running without the hassle of provisioning servers.</p>
<p>We also discussed the advantages of adopting the Serverless model in deploying your applications, it’s disadvantages, and real-world use cases in which you should adopt the serverless approach.</p>
<h2 id="heading-about-the-author"><strong>About the Author 👨‍💻</strong></h2>
<p>Hi, I’m Prince! I’m a DevOps engineer and Cloud architect passionate about building, deploying, and managing scalable applications and sharing knowledge with the tech community.</p>
<p>If you enjoyed this article, you can learn more about me by exploring more of my blogs and projects on my <a target="_blank" href="https://www.linkedin.com/in/prince-onukwili-a82143233/">LinkedIn profile</a>. You can find my <a target="_blank" href="https://www.linkedin.com/in/prince-onukwili-a82143233/details/publications/">LinkedIn articles here</a>. You can also <a target="_blank" href="https://prince-onuk.vercel.app/achievements#articles">visit my website</a> to read more of my articles as well. Let’s connect and grow together! 😊</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Orchestrating AWS Lambda with GraphQL and Apollo Connectors ]]>
                </title>
                <description>
                    <![CDATA[ AWS Lambda is a computing service that enables you to run arbitrary code functions without needing to provision, manage, or scale servers. It’s often used in the logic tier of a multi-tier architecture to handle tasks such as processing files in S3 o... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-orchestrate-aws-lambda-with-graphql-and-apollo-connectors/</link>
                <guid isPermaLink="false">67e2d5e080e11112e050be00</guid>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Apollo GraphQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rob Walters ]]>
                </dc:creator>
                <pubDate>Tue, 25 Mar 2025 16:12:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742917115054/07184be6-5384-4861-a676-b72c06ff7c65.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>AWS Lambda is a computing service that enables you to run arbitrary code functions without needing to provision, manage, or scale servers. It’s often used in the logic tier of a multi-tier architecture to handle tasks such as processing files in S3 or performing CRUD operations on a database.</p>
<p>AWS also offers an API Gateway, allowing developers to invoke AWS Lambda functions, which provides enhanced security and performance features like rate limiting. But even with the API Gateway, you have to coordinate these microservices, as your client applications likely each have unique data needs. Data might need to be transformed, filtered, or combined before it is returned to the client. </p>
<p>These orchestration tasks can reduce your productivity and take time and effort away from solving the business problem your application is trying to solve. </p>
<p>Apollo GraphQL is an API orchestration layer that helps teams ship new features faster and more independently by composing any number of underlying services and data sources into a single endpoint. This allows clients on-demand access to precisely what the experience needs, regardless of the source of that data.</p>
<p>This article will teach you how to orchestrate AWS Lambda functions using Apollo GraphQL. Specifically, here’s what we will cover:</p>
<ul>
<li><p><a class="post-section-overview" href="#heading-graphql-primer">GraphQL Primer</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tutorial-overview">Tutorial Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-section-1-create-the-aws-resources">Section 1: Create the AWS Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-section-2-create-an-apollo-connector">Section 2: Create an Apollo Connector</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-section-3-how-to-use-apollo-sandbox">Section 3: How to Use Apollo Sandbox</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-graphql-primer">GraphQL Primer</h2>
<p>For those unfamiliar with GraphQL, here’s a primer that offers some background on the challenges GraphQL addresses and how data is typically managed through REST APIs in GraphQL before the emergence of Apollo Connectors. If you’re familiar with GraphQL, feel free to skip this section.</p>
<p>GraphQL is a query language for APIs. This query language and corresponding runtime enable clients to specify exactly the data they require, minimizing over-fetching and under-fetching.</p>
<p>In contrast to REST, which necessitates multiple endpoints for various data requirements, GraphQL streamlines queries into a single request, enhancing performance and reducing network latency.</p>
<p>GraphQL also uses a strongly typed schema. This improves API documentation and makes validation, early error detection, and immersive developer tooling easy. </p>
<p>To illustrate the difference between REST APIs and GraphQL, consider the following REST API call: /user/123</p>
<p>Response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"id"</span>: <span class="hljs-number">123</span>,
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Alice Johnson"</span>,
  <span class="hljs-attr">"email"</span>: <span class="hljs-string">"alice@example.com"</span>,
  <span class="hljs-attr">"phone"</span>: <span class="hljs-string">"555-1234"</span>,
  <span class="hljs-attr">"address"</span>: {
    <span class="hljs-attr">"street"</span>: <span class="hljs-string">"123 Main St"</span>,
    <span class="hljs-attr">"city"</span>: <span class="hljs-string">"Springfield"</span>,
    <span class="hljs-attr">"state"</span>: <span class="hljs-string">"IL"</span>,
    <span class="hljs-attr">"zip"</span>: <span class="hljs-string">"62704"</span>
  },
  <span class="hljs-attr">"createdAt"</span>: <span class="hljs-string">"2022-01-01T12:00:00Z"</span>,
  <span class="hljs-attr">"updatedAt"</span>: <span class="hljs-string">"2022-05-15T14:30:00Z"</span>,
  <span class="hljs-attr">"isAdmin"</span>: <span class="hljs-literal">false</span>
}
</code></pre>
<p>If you were only interested in the name and email, using REST would be a lot of data returned from the network to the client for no reason. Using GraphQL, the GraphQL query to return the name and email would be the following:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> {
  user(<span class="hljs-symbol">id:</span> <span class="hljs-number">123</span>) {
    name
    email
  }
}
</code></pre>
<p>The result set is just the data the client needs:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"user"</span>: {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Alice Johnson"</span>,
      <span class="hljs-attr">"email"</span>: <span class="hljs-string">"alice@example.com"</span>
    }
  }
}
</code></pre>
<p>This is a simple example showing the benefit of not over-fetching data, but GraphQL has many other advantages. One of them is the separation between client and server. Since both parties leverage and respect the GraphQL type schema, both teams can operate more independently with the back end defining where the data resides and the front end only asking for data it needs.    </p>
<p>So how does GraphQL know how to populate data for every field in your schema? It does this through <a target="_blank" href="https://www.apollographql.com/docs/apollo-server/data/resolvers">resolvers</a>. Resolvers can fetch data from a back-end databases or third-party API such as REST APIs, gRPC, and so on. These functions comprise procedural code compiled and maintained for each field in the schema. Thus, one field can have a resolver that queries a REST API and another can query a gRPC endpoint.</p>
<p>To illustrate resolvers, consider the example above. Let’s add a field, status, that queries a REST API to determine if the user is full-time, part-time, or terminated. </p>
<p>First we have defined our schema as:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">type</span> User {
  <span class="hljs-symbol">id:</span> ID!
  <span class="hljs-symbol">name:</span> String!
  <span class="hljs-symbol">email:</span> String!
  <span class="hljs-symbol">status:</span> String!  <span class="hljs-comment"># Need this from an external REST API</span>
}

<span class="hljs-keyword">type</span> Query {
  user(<span class="hljs-symbol">id:</span> ID!): User
}
</code></pre>
<p>The user query in this case will accept a user id and return a type User. The resolver function to support the data fetching resembles the following:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> resolvers = {
  <span class="hljs-attr">Query</span>: {
    <span class="hljs-attr">user</span>: <span class="hljs-keyword">async</span> (_, { id }) =&gt; {
      <span class="hljs-comment">// Fetch user details from one REST API</span>
      <span class="hljs-keyword">const</span> userResponse = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`https://api.company.com/users/<span class="hljs-subst">${id}</span>`</span>);
      <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> userResponse.json();

      <span class="hljs-comment">// Fetch employee status from another REST API</span>
      <span class="hljs-keyword">const</span> statusResponse = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`https://api.company.com/employees/<span class="hljs-subst">${id}</span>/status`</span>);
      <span class="hljs-keyword">const</span> statusData = <span class="hljs-keyword">await</span> statusResponse.json();

      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">id</span>: userData.id,
        <span class="hljs-attr">name</span>: userData.name,
        <span class="hljs-attr">email</span>: userData.email,
        <span class="hljs-attr">status</span>: statusData.status, <span class="hljs-comment">// e.g., "Full-Time", "Part-Time", "Terminated"</span>
      };
    },
  },
};
</code></pre>
<p>Notice that not only are there two fetches needed to obtain the information the query requires, but we also need to write procedural code and deploy it.</p>
<p>A better approach would be to declaratively specify to GraphQL where the REST API is located and what data to return. Apollo Connectors is the solution to this challenge, simplifying the process and allowing you to declaratively integrate REST API data without requiring code compilation and maintenance.</p>
<p>Now that you have a general idea of GraphQL and the challenges it addresses, let’s delve into the example we will build out.</p>
<h2 id="heading-tutorial-overview">Tutorial Overview</h2>
<p>In this tutorial, you will create two AWS Lambda functions that return product information, which are described as follows:</p>
<p>Products Request:</p>
<p>POST /2015-03-31/functions/products/invocations</p>
<p>Response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"statusCode"</span>: <span class="hljs-number">200</span>,
  <span class="hljs-attr">"body"</span>: [
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-string">"RANQi6AZkUXCbZ"</span>,
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"OG Olive Putter - Blade"</span>,
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"The traditional Block in a blade shape is made from a solid block of Olive wood. The head weight is approximately 360 grams with the addition of pure tungsten weights. Paired with a walnut center-line and white accents colors."</span>,
      <span class="hljs-attr">"image"</span>: <span class="hljs-string">"https://keynote-strapi-production.up.railway.app/uploads/thumbnail_IMG_9102_3119483fac.png"</span>
    },
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-string">"RANYrWRy876AA5"</span>,
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Butter Knife Olive Putter- Blade"</span>,
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"The traditional Block in a extremely thin blade shape (~1\") is made from a solid block of Olive wood. The head weight is approximately 330 grams with the addition of pure tungsten weights."</span>,
      <span class="hljs-attr">"image"</span>: <span class="hljs-string">"https://keynote-strapi-production.up.railway.app/uploads/thumbnail_IMG_9104_97c221e79c.png"</span>
    },...
</code></pre>
<p>Product-price request:</p>
<p>POST: /2015-03-31/functions/product-price/invocations</p>
<p>Response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"default_price"</span>: <span class="hljs-number">49900</span>,
  <span class="hljs-attr">"is_active"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"currency"</span>: <span class="hljs-string">"usd"</span>,
  <span class="hljs-attr">"billing_schema"</span>: <span class="hljs-string">"per_unit"</span>,
  <span class="hljs-attr">"recurring"</span>: {
    <span class="hljs-attr">"interval"</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">"interval_count"</span>: <span class="hljs-number">3</span>
  }
}
</code></pre>
<p>To expose these two lambda microservices, you need to create API Gateway triggers. This involves either setting up a distinct API Gateway for each lambda or consolidating them under one or a few API Gateway instances with specified routes for each lambda.</p>
<p>Creating a trigger may feel tedious and repetitive in a microservices setup. But there is an alternative available. You could directly invoke those functions via REST using the InvokeFunction permission assigned to an IAM user. This article will show you this method and guide you through function creation, necessary AWS IAM permissions, and configuring the Apollo Connector to invoke the function.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along in this tutorial, you will need to have a basic understanding of AWS Lambda functions as well as AWS security. You’ll also need access to the following:</p>
<ul>
<li><p>An AWS account with permissions to create IAM Users and Policies</p>
</li>
<li><p>An Apollo GraphQL account, you can <a target="_blank" href="https://studio.apollographql.com/signup">sign up for a free plan here</a>.</p>
</li>
</ul>
<p>We will also use the following tools:</p>
<ul>
<li><p><a target="_blank" href="https://code.visualstudio.com/">VS Code</a>: Microsoft VS Code is a free source code editor from Microsoft</p>
</li>
<li><p><a target="_blank" href="https://www.apollographql.com/docs/rover/getting-started?utm_campaign=2025-03-20_installing-rover-doc-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">Apollo Rover CLI</a>: Rover is the command-line interface for managing and maintaining graphs</p>
</li>
<li><p><a target="_blank" href="https://studio.apollographql.com/signup?utm_campaign=2025-03-19_studio-signup-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">Apollo Studio</a>: A web-based portal used for managing all aspects of your graph </p>
</li>
<li><p><a target="_blank" href="https://www.apollographql.com/connectors-mapping-playground?utm_campaign=2025-03-20_connectors-mapping-playground-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">Apollo Connectors Mapping Playground</a>: A website that takes a JSON document and helps developers create the selection mapping used with Apollo Connectors</p>
</li>
</ul>
<h2 id="heading-section-1-create-the-aws-resources">Section 1: Create the AWS Resources</h2>
<p>First, let’s configure our AWS environment, starting with security. In our scenario, we will create an IAM User, “ConnectorUser,” with access to an AWS Policy, “ConnectorLambdaPolicy,” with the minimum permissions needed to access the AWS Lambda functions.</p>
<p>Note that you could create user groups and assign permission policies to those groups in a production environment. But for this article, we are reducing the number of administrative steps to focus on the core integration with GraphQL.</p>
<h3 id="heading-step-1-create-an-aws-policy">Step 1: Create an AWS Policy</h3>
<p>To create a policy, navigate to IAM within the AWS Management console, then select “Policies” under Access Management. Click “Create Policy”. This will open the policy editor page, as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742755417676/1025d04f-a712-4311-9669-ac38bd2fee50.jpeg" alt="specify permissions" class="image--center mx-auto" width="1558" height="720" loading="lazy"></p>
<p>Choose the “Lambda” service and under the Access level select “InvokeFunction” from the Write drop down menu as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742755482285/1be204db-7b39-4c8f-ac7c-d461032f6887.jpeg" alt="InvokeFunction checkmarked" class="image--center mx-auto" width="1534" height="910" loading="lazy"></p>
<p>Under the Resources menu, you can choose either All ARNs or a specific option. It's a best practice to be as granular as possible when defining security configurations. In this example, let’s limit our selection to the “us-east-1” region by clicking on the “Specific” option and then “Add ARNs.” Enter “us-east-1” in the resource region and select “Any function name.”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742755564740/99e47ac8-4ce9-4ff3-94b6-105308526f56.jpeg" alt="Specify ARN dialog" class="image--center mx-auto" width="1598" height="824" loading="lazy"></p>
<p>With the policy created, we can assign an IAM user to that policy.</p>
<h3 id="heading-step-2-create-the-iam-user-and-attach-a-policy">Step 2: Create the IAM User and Attach a Policy</h3>
<p>Click on Users under “Access Management” then Create User. Provide a name for the user, “ConnectorUser”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742755638499/ad782c39-a78f-4d68-b834-4e58dea9e35b.jpeg" alt="Permission policy" class="image--center mx-auto" width="1566" height="570" loading="lazy"></p>
<p>Next, select “Attach policies directly,” choose the policy we just created, “ConnectorLambdaPolicy,” and click “Create User.”</p>
<h3 id="heading-step-3-create-aws-lambda-functions">Step 3: Create AWS Lambda Functions</h3>
<p>In your AWS console, create a new NodeJS AWS Lambda function, “products”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742754922858/b2a307c2-8b43-4417-b022-0113803a3b5d.jpeg" alt="AWS create function dialog" class="image--center mx-auto" width="1856" height="1036" loading="lazy"></p>
<p>Select “Node.JS” for the runtime then click “Create function”. Once created, paste in the the function code <a target="_blank" href="https://gist.github.com/RWaltersMA/25264ff22a5cbc26814a00dbb78a16e2">from this Gist</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742755096066/90e96036-41cd-4b45-8841-0bb3acb5af6b.jpeg" alt="AWS function showing code source" class="image--center mx-auto" width="1990" height="1000" loading="lazy"></p>
<p>Repeat this process, creating another function for, “product-price” and use the function code <a target="_blank" href="https://gist.github.com/RWaltersMA/d75d9eb02264829c1392dbdf7f238bad">from this Gist</a>.</p>
<h2 id="heading-section-2-create-an-apollo-connector">Section 2: Create an Apollo Connector</h2>
<p>In this section, we will install the Apollo Rover CLI tool, create an Apollo Studio free tier account, and clone the Apollo Connectors repository. If you already have an Apollo environment available, you can skip steps 1 and 2.</p>
<h3 id="heading-step-1-install-rover">Step 1: Install Rover</h3>
<p>Rover is the command-line interface for managing and maintaining graphs. It also provides a modern hot-reloading experience for developing and running your connectors locally. If you don’t have Rover installed, install it by <a target="_blank" href="https://www.apollographql.com/docs/rover/getting-started?utm_campaign=2025-03-20_installing-rover-doc-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">following the steps here</a>.</p>
<h3 id="heading-step-2-create-an-apollo-studio-free-tier-account">Step 2: Create an Apollo Studio Free Tier Account</h3>
<p>Apollo Studio is a cloud-based management platform designed to explore, deliver, and collaborate on graphs. If you do not have an Apollo Studio account, create one on a free plan <a target="_blank" href="https://studio.apollographql.com/signup?utm_campaign=2025-03-19_studio-signup-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">by navigating here</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742755870123/4b38b025-064c-4a9a-b836-53a563152e43.jpeg" alt="Apollo Studio" class="image--center mx-auto" width="1560" height="753" loading="lazy"></p>
<h3 id="heading-step-3-clone-the-apollo-connectors-repository">Step 3: Clone the Apollo Connectors Repository</h3>
<p>To help you start your first Apollo Connector, a GitHub repository provides sample connectors and a template script. When run, this script will create all the necessary files and configurations you need to begin. </p>
<p>Go ahead and <a target="_blank" href="https://github.com/apollographql/connectors-community">clone the repository from here</a>.</p>
<p>Note: While not required, I recommended using VS Code, as this repo leverages VS Code-specific settings files.</p>
<h3 id="heading-step-4-create-a-env-file">Step 4: Create a .env File</h3>
<p>Before you run the Create Connectors template script, create a .env locally with a user API key from your Apollo Studio. You can <a target="_blank" href="https://studio.apollographql.com/user-settings/api-keys">create and obtain this key here</a>. Populating this .env file will add this API key to the connector template you create in the next step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742755977271/860ef610-e802-4ec1-9cca-e881881a0968.jpeg" alt=".env file" class="image--center mx-auto" width="1554" height="109" loading="lazy"></p>
<h3 id="heading-step-5-create-your-new-connector-from-a-template">Step 5: Create Your New Connector from a Template</h3>
<p>Execute <code>npm start</code> and provide a location to create the connector template. You can use default values for the remaining questions.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756030015/e6c3b535-8657-4a77-b353-b8546cfa9ac5.jpeg" alt="npmstart" class="image--center mx-auto" width="1536" height="402" loading="lazy"></p>
<p>This script will create all the necessary files to run a local Apollo GraphQL instance in the specified directory. Load the newly created connector using VS Code or your preferred code editor. You will return to this editor soon, but first, we need to obtain some access keys from AWS.</p>
<h3 id="heading-step-6-create-an-aws-access-key">Step 6: Create an AWS Access Key</h3>
<p>Since we connect to AWS using SigV4, we must create an AWS access key and enter the KEY values in the settings.json file. Return to the AWS IAM Console and select the <em>ConnectorUser</em> you created in Step 1.  Create a new access key by clicking on “Create access key”. </p>
<p>You will be presented with multiple options as far as where the use of this key will originate. Since we are first running locally, select “Third-party service” and then continue the wizard until you are presented with the key and secret key as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756092209/e7b33bd2-f6ca-4e78-bf83-8ed357860abd.jpeg" alt="retrieve access key dialog" class="image--center mx-auto" width="1548" height="344" loading="lazy"></p>
<p>Add the access key and secret access key to the settings.json file as “AWS_ACCESS_KEY_ID” and “AWS_SECRET_ACCESS_KEY” respectively.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756152443/31906e19-625d-446f-adde-b9618e8df61a.jpeg" alt="vscode settings file" class="image--center mx-auto" width="1140" height="624" loading="lazy"></p>
<p>You'll need to reload the window since VS Code only loads these files under the .vscode directory once. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756203631/8fb467fc-e8de-4f16-8907-622a12017d4f.jpeg" alt="vscode task window showing reload option" class="image--center mx-auto" width="1552" height="162" loading="lazy"></p>
<p>Note: In this step, we saved the key to the settings.json file. While this is acceptable for development, consider saving environment variables in .env files.</p>
<h3 id="heading-step-7-configure-the-graph">Step 7: Configure the Graph</h3>
<p>The supergraph.yaml file is used to define all the subgraphs that are part of this federation. Modify the <strong>supergraph.yaml</strong> file as follows:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">federation_version:</span> <span class="hljs-string">=2.10.0</span>
<span class="hljs-attr">subgraphs:</span>
  <span class="hljs-attr">awsconnector:</span>
    <span class="hljs-attr">routing_url:</span> <span class="hljs-string">http://lambda</span>
    <span class="hljs-attr">schema:</span>
      <span class="hljs-attr">file:</span> <span class="hljs-string">connector.graphql</span>
</code></pre>
<h3 id="heading-step-8-configure-apollo-router">Step 8: Configure Apollo Router</h3>
<p>Apollo Router supports AWS SigV4 authentication. To configure the connector to use this, modify the <strong>router.yaml</strong> file and add an authentication section as follows:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">authentication:</span>
  <span class="hljs-attr">connector:</span>
    <span class="hljs-attr">sources:</span>
      <span class="hljs-attr">awsconnector.lambda:</span>   <span class="hljs-comment"># subgraph name . connector source name</span>
        <span class="hljs-attr">aws_sig_v4:</span>
          <span class="hljs-attr">default_chain:</span>
            <span class="hljs-attr">region:</span> <span class="hljs-string">"us-east-1"</span>
            <span class="hljs-attr">service_name:</span> <span class="hljs-string">"lambda"</span>
</code></pre>
<p>There are other AWS security configuration options available, including using assume role. The full documentation for subgraph authentication <a target="_blank" href="https://www.apollographql.com/docs/graphos/routing/security/subgraph-authentication">is available here</a>. </p>
<h3 id="heading-step-9-build-the-connector">Step 9: Build the connector</h3>
<p>Now that we have configured the environment variables and authentication information, we are ready to build the connector. Open the <code>connector.graphql</code> file and erase the contents. Next, copy the following extend schema:</p>
<pre><code class="lang-graphql">extend <span class="hljs-keyword">schema</span>
  <span class="hljs-meta">@link</span>(
    <span class="hljs-symbol">url:</span> <span class="hljs-string">"https://specs.apollo.dev/federation/v2.10"</span>
    <span class="hljs-symbol">import:</span> [<span class="hljs-string">"@key"</span>]
  )
  <span class="hljs-meta">@link</span>(
    <span class="hljs-symbol">url:</span> <span class="hljs-string">"https://specs.apollo.dev/connect/v0.1"</span>
    <span class="hljs-symbol">import:</span> [<span class="hljs-string">"@source"</span>, <span class="hljs-string">"@connect"</span>]
  )

  <span class="hljs-meta">@source</span>(
    <span class="hljs-symbol">name:</span> <span class="hljs-string">"lambda"</span>
    <span class="hljs-symbol">http:</span> { <span class="hljs-symbol">baseURL:</span> <span class="hljs-string">"https://lambda.us-east-1.amazonaws.com"</span> }
  )
</code></pre>
<p><strong>Extend schema</strong> is used to link the Apollo Connectors directives into the current schema. In this article we are defining the base URL of our lambda function. If your REST API has HTTP headers that apply to all references of this source, such as Content-Length restrictions, you can add them here in the @source declaration. Next, let’s define the Product schema:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">type</span> Product {
  <span class="hljs-symbol">id:</span> ID!
  <span class="hljs-symbol">name:</span> String
  <span class="hljs-symbol">description:</span> String
  <span class="hljs-symbol">image:</span> String
  <span class="hljs-symbol">price:</span> Price
    <span class="hljs-meta">@connect</span>(
      <span class="hljs-symbol">source:</span> <span class="hljs-string">"lambda"</span>
      <span class="hljs-symbol">http:</span> {
        <span class="hljs-symbol">POST:</span> <span class="hljs-string">"/2015-03-31/functions/product-price/invocations"</span>
        <span class="hljs-symbol">body:</span> <span class="hljs-string">""</span><span class="hljs-string">"
        product_id: $this.id
        "</span><span class="hljs-string">""</span>
      }
      <span class="hljs-symbol">selection:</span> <span class="hljs-string">""</span><span class="hljs-string">"
      amount: default_price
      isActive: is_active
      currency
      recurringInterval: recurring.interval -&gt; match(
        [0,"</span>ONE_TIME<span class="hljs-string">"],
        [1,"</span>DAILY<span class="hljs-string">"],
        [2,"</span>MONTHLY<span class="hljs-string">"],
        [3,"</span>ANNUALLY<span class="hljs-string">"],
      )
      recurringCount: recurring.interval_count
      "</span><span class="hljs-string">""</span>
    )
}
</code></pre>
<p>Notice our query Products has an @connect directive that defines, at a minimum, the source name. Here, you can add the HTTP-specific configuration you need for this field, such as Authorizations headers. In this scenario, since we only defined a baseUrl in the extend schema section, we need to put the specific URL for the InvokeFunction, which is <strong>/2015-03-31/functions/product-price/invocations</strong>.</p>
<p>The selection field allows you to transform and map values returned from the REST API using the mapping definition defined in the selection field. While a complete discussion of selection mapping is beyond the scope of this article, check out the documentation for a detailed look at <a target="_blank" href="https://www.apollographql.com/docs/graphos/schema-design/connectors/responses?utm_campaign=2025-03-20_mapping-graphql-responses-doc-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">Mapping GraphQL Responses</a>.  Apollo <a target="_blank" href="https://www.apollographql.com/connectors-mapping-playground?utm_campaign=2025-03-20_connectors-mapping-playground-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">provides a free online tool</a> that makes building mappings intuitive and fast.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756290237/91d17c59-a2d0-4a22-8acf-1faec0c0f36f.jpeg" alt="connectors mapping playground" class="image--center mx-auto" width="1560" height="984" loading="lazy"></p>
<p>Next, let’s define the Price schema and products Query.</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">type</span> Price {
  <span class="hljs-symbol">amount:</span> Float
  <span class="hljs-symbol">isActive:</span> Boolean
  <span class="hljs-symbol">currency:</span> String
  <span class="hljs-symbol">recurringInterval:</span> RecurringInterval
  <span class="hljs-symbol">recurringCount:</span> Int
}
<span class="hljs-keyword">enum</span> RecurringInterval {
  ONE_TIME
  DAILY
  MONTHLY
  ANNUALLY
}

<span class="hljs-keyword">type</span> Query {
  <span class="hljs-symbol">products:</span> [Product]
    <span class="hljs-comment"># https://docs.aws.amazon.com/lambda/latest/api/API_Invoke.html</span>
    <span class="hljs-meta">@connect</span>(
      <span class="hljs-symbol">source:</span> <span class="hljs-string">"lambda"</span>
      <span class="hljs-symbol">http:</span> { <span class="hljs-symbol">POST:</span> <span class="hljs-string">"/2015-03-31/functions/products/invocations"</span> }
      <span class="hljs-symbol">selection:</span> <span class="hljs-string">""</span><span class="hljs-string">"
      $.body {
        id
        name
        description
        image
      }
      "</span><span class="hljs-string">""</span>
    )
}
</code></pre>
<p>Now we're ready to run our connector and issue queries to our graph! The complete configuration script is available <a target="_blank" href="https://gist.github.com/RWaltersMA/e44813a89c748e175d6997f659162b33.">at this Gist</a>.</p>
<h3 id="heading-step-10-run-the-connector">Step 10: Run the Connector</h3>
<p>If you're using VS Code, the repository includes a tasks.json file that adds a “rover dev” task, which launches Rover locally. </p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"2.0.0"</span>,
    <span class="hljs-attr">"tasks"</span>: [{
        <span class="hljs-attr">"label"</span>: <span class="hljs-string">"rover dev"</span>,
        <span class="hljs-attr">"command"</span>: <span class="hljs-string">"rover"</span>, <span class="hljs-comment">// Could be any other shell command</span>
        <span class="hljs-attr">"args"</span>: [<span class="hljs-string">"dev"</span>, <span class="hljs-string">"--supergraph-config"</span>,<span class="hljs-string">"supergraph.yaml"</span>, <span class="hljs-string">"--router-config"</span>,<span class="hljs-string">"router.yaml"</span>],
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"shell"</span>,
        <span class="hljs-attr">"problemMatcher"</span>: [],
    }]
}
</code></pre>
<p> If you are not using VS Code, you can start your graph by executing <code>rover dev –supergraph-config supergraph.yaml –router-config router.yaml</code> from a terminal window.</p>
<p>If everything is configured correctly, you’ll see the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756354078/9fab875a-d064-4723-be91-8ca0d6243b59.jpeg" alt="running rover dev command " class="image--center mx-auto" width="1558" height="390" loading="lazy"></p>
<h2 id="heading-section-3-how-to-use-apollo-sandbox">Section 3: How to Use Apollo Sandbox</h2>
<p>The <code>rover dev</code> command you launched in the previous step configures a local Apollo Router instance for <a target="_blank" href="https://www.apollographql.com/docs/graphos/reference/router/configuration?utm_campaign=2025-03-20_router-configuration-doc-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp#-dev">development mode</a>. This mode makes it easy for developers to create, execute, and debug ad-hoc GraphQL queries using the Apollo Sandbox web portal. This portal is located at <a target="_blank" href="http://localhost:4000">http://localhost:4000</a> by default.</p>
<p>Launch the portal and click on the products field. This will populate the Operation pane with all the available fields in the schema. In the operation pane, you can modify and build your GraphQL query. Clicking the Run button (which displays the query name, Products, in our example) will execute the query and show the results in the Response panel, as illustrated in the figure above.</p>
<p>In this example, you can see that data has been returned from our AWS Lambda function. To confirm, you can view the query plan by selecting "Query Plan” from the Response drop-down menu.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756499055/822467d7-0694-423e-baef-450a8d0dd64e.jpeg" alt="query plan menu item" class="image--center mx-auto" width="590" height="566" loading="lazy"></p>
<p>The query plan illustrates the orchestration of our two AWS Lambda functions that fetch product and product price data.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756540147/af32d615-029a-4f6f-a489-96ee9950e630.jpeg" alt="query plan" class="image--center mx-auto" width="1542" height="1010" loading="lazy"></p>
<p>A helpful debugging feature is the Connectors Debugger, available in the drop-down as shown in the previous figure.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742756614481/c9b311e3-ab5d-4927-9457-e9a7d242fbdf.jpeg" alt="debugger showing request overview" class="image--center mx-auto" width="775" height="818" loading="lazy"></p>
<p>The Connection Debugger provides a comprehensive view of the HTTP request, including headers, body, response code, and the selection mapping used in the query. If you’re experiencing difficulties running queries, use this debugger – it will save you a lot of time.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, you learned how to: </p>
<ul>
<li><p>Set up AWS IAM User, Policies, and Lambda functions</p>
</li>
<li><p>Create an Apollo Connector to obtain data from an AWS Lambda function</p>
</li>
<li><p>Configure the Apollo Router </p>
</li>
<li><p>Execute and debug queries using Apollo Sandbox</p>
</li>
</ul>
<p>Integrating AWS Lambda with Apollo Connectors offers a simplified, resolver-free method for incorporating cloud functions into your GraphQL API. By utilizing Apollo Connectors, you can declaratively link REST-based Lambda functions to your supergraph while ensuring secure authentication with AWS SigV4.</p>
<p>You can learn more about Apollo Connectors from the following resources:</p>
<ol>
<li><p>Tutorial: <a target="_blank" href="https://www.apollographql.com/tutorials/connectors-intro-rest?utm_campaign=2025-03-20_connectors-intro-rest-odyssey-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">GraphQL meets REST, with Apollo Connectors</a></p>
</li>
<li><p>Blog: Discover how Apollo Connectors integrate with Apollo Federation through insights from Apollo's Founder &amp; CTO: <a target="_blank" href="https://www.apollographql.com/blog/api-orchestration-with-graphql?utm_campaign=2025-03-20_api-orchestration-with-graphql-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">REST API Orchestration with GraphQL</a>.</p>
</li>
<li><p>Blog: Delve into the engineering journey behind Apollo Connectors and the process of their creation: <a target="_blank" href="https://www.apollographql.com/blog/our-journey-to-apollo-connectors?utm_campaign=2025-03-20_our-journey-to-apollo-connectors-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">Our Journey to Apollo Connectors</a></p>
</li>
<li><p>Webinar: <a target="_blank" href="https://www.apollographql.com/events/new-innovations-from-apollo-dont-miss-out?utm_campaign=2025-03-20_new-innovations-from-apollo-dont-miss-out-march2025awareness&amp;utm_medium=blog&amp;utm_source=freecodecamp">Apollo Connectors GA Launch Webinar</a></p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Application with AWS Lambda ]]>
                </title>
                <description>
                    <![CDATA[ AWS Lambda is a service from Amazon Web Services (AWS) that lets you run your code in response to events without managing servers. It’s a simple and scalable way to build applications. In this tutorial, I’ll show you how to use AWS Lambda with three ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-application-with-aws-lambda/</link>
                <guid isPermaLink="false">6798f596558861e9737fa5af</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ijeoma Igboagu ]]>
                </dc:creator>
                <pubDate>Tue, 28 Jan 2025 15:19:50 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738077341437/939ee844-0f10-4ea6-b031-9c481019e7c6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>AWS Lambda is a service from Amazon Web Services (AWS) that lets you run your code in response to events without managing servers. It’s a simple and scalable way to build applications.</p>
<p>In this tutorial, I’ll show you how to use AWS Lambda with three other services:</p>
<ul>
<li><p><strong>Amazon S3</strong> for storing files, images, and videos</p>
</li>
<li><p><strong>Amazon Simple Notification Service (SNS)</strong> for sending notifications</p>
</li>
<li><p><strong>Amazon EventBridge</strong> for scheduling messages</p>
</li>
</ul>
<p>We’ll go through everything step by step.</p>
<p>By the end, with the integration of the other services, you will have built a Goal Manifestation Quote App that sends random inspirational messages to keep you motivated and focused on your goals.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p>An AWS account: If you don’t have one, sign up <a target="_blank" href="https://aws.amazon.com/">here</a>.</p>
</li>
<li><p>A GitHub repository: This is for storing your source code. If you don’t have a GitHub account, you can create one <a target="_blank" href="https://github.com/">here</a>.</p>
</li>
<li><p>An Integrated Development Environment (IDE) such as <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> or <a target="_blank" href="https://www.sublimetext.com/download">Sublime Text</a>.</p>
</li>
<li><p>A basic knowledge of web development and any programming language of your choice. I used Python for this tutorial.</p>
</li>
<li><p><a target="_blank" href="https://zenquotes.io/">Zenquote Random API</a></p>
</li>
</ul>
<h3 id="heading-what-youll-learn">What You'll Learn</h3>
<ul>
<li><p>How to create an Amazon S3 bucket</p>
</li>
<li><p>How to use Amazon Simple Notification Service (SNS)</p>
</li>
<li><p>How to use Amazon Lambda</p>
</li>
<li><p>How to use Amazon EventBridge</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-step-1-set-up-your-development-environment">Step 1: Set Up Your Development Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-create-an-amazon-simple-storage-service-s3">Step 2: Create an Amazon Simple Storage Service (S3)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-create-an-amazon-simple-simple-notification-service">Step 3: Create an Amazon Simple Notification Service (SNS)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-create-an-iam-policy">Step 4: Create an IAM Policy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-create-an-amazon-lambda-function">Step 5: Create an Amazon Lambda function</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-create-an-eventbridge">Step 6: Create an EventBridge</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-7-upload-your-code">Step 7. Upload Your Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736791948488/38dfe402-1050-410d-869b-0cef2797b792.png" alt="Let's get started 🚀" class="image--center mx-auto" width="600" height="401" loading="lazy"></p>
<h2 id="heading-step-1-set-up-your-development-environment">Step 1: Set Up Your Development Environment</h2>
<p>In this step, you'll get everything set up. Start by signing in to your AWS account, then install <a target="_blank" href="https://www.python.org/downloads/">Python</a> if you don't have it on your IDE.</p>
<h2 id="heading-step-2-create-an-amazon-simple-storage-service-s3"><strong>Step 2: Create an Amazon Simple Storage Service (S3)</strong></h2>
<p>Before we begin creating an S3 bucket, let's first understand what Amazon S3 is:</p>
<p><strong>Amazon S3 (Simple Storage Service)</strong> is a service from Amazon that allows you to store and access any amount or type of data, such as photos, videos, documents, and backups, whenever you need it.</p>
<p>Now that you know the basics of what Amazon S3 is, let's return to the tutorial.</p>
<h3 id="heading-create-an-s3-bucket">Create an S3 Bucket</h3>
<p>There are several ways to create an S3 bucket, but for this tutorial, we'll use the <a target="_blank" href="https://ubuntu.com/download">Ubuntu command line (CMD)</a>, your terminal, or <strong>Amazon CloudShell</strong>, depending on what you're most comfortable with.</p>
<ul>
<li><p>Type boto3 s3 in the web search bar to view a list of related documentation.</p>
</li>
<li><p>Click on the first result.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736792137101/5f38b4ec-fa23-41b3-b108-ca7fc7b390ba.png" alt="Regular Google search" class="image--right mx-auto mr-0" width="848" height="272" loading="lazy"></p>
<ul>
<li>Once the documentation is open, copy the first command you see.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736792202800/5647c731-734f-4134-a558-9d66eee47734.png" alt="boto3 command" width="947" height="376" loading="lazy"></p>
<ul>
<li>Paste it on your CMD OR terminal of your choice – but before then remember to "<strong>cd</strong>" into the right directory.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736792298332/d3384fc3-e31c-4d37-8e17-40ad7e77df28.png" alt="paste command from documentation to your editor" width="725" height="89" loading="lazy"></p>
<ul>
<li>In the documentation, scroll down and click on "create_bucket.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736792399748/0cd59a14-b037-464b-8193-7ec515c4772e.png" alt="0cd59a14-b037-464b-8193-7ec515c4772e" width="223" height="24" loading="lazy"></p>
<ul>
<li><p>Once it's open, scroll down to "Request Syntax." Copy the <strong>bucket name</strong> and the <strong>bucket configuration</strong>.</p>
</li>
<li><p>Other variables listed in the request syntax are optional.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736792898846/eea0f8c4-d153-4bc8-8c78-346fc5bf6a04.png" alt="Request syntax from the documentation" width="887" height="262" loading="lazy"></p>
<ul>
<li>Once this is done, make sure you save.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736793004865/a1c9739c-2d12-4e2d-b057-f09bc61e16a3.png" alt="All command" width="980" height="413" loading="lazy"></p>
<ul>
<li>Go back and call the script:</li>
</ul>
<pre><code class="lang-plaintext">#python3 your file name
</code></pre>
<ul>
<li>Running the script automatically creates an S3 bucket in your Amazon S3.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736793086405/9c0b4671-ea07-4ad7-b1d7-0d785aafa954.png" alt="Automatic creation" width="1904" height="155" loading="lazy"></p>
<ul>
<li>Now you can go to the console to check if it has been created:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736692453693/320318d4-bdf3-4be3-a709-6cff18459c9c.png" alt="Amazon console" class="image--center mx-auto" width="744" height="129" loading="lazy"></p>
<h3 id="heading-upload-files">Upload Files</h3>
<p>With the bucket created, we can now upload files through the console. I believe there's also a programmatic way to upload files and test, but I haven't explored all the methods in the documentation yet.</p>
<p>Click on the bucket name to be redirected to the object page. This is where you will upload your files for storage.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736692660862/355f828f-f83f-4501-a960-f0068cf9d977.png" alt="upload page" class="image--center mx-auto" width="1837" height="369" loading="lazy"></p>
<p>Click on the <strong>Upload button</strong> to upload a file. Remember, we're creating a Goal Manifestation Quote Application.</p>
<p>Now that we've set up a storage bucket:</p>
<ul>
<li><p>Open a tool like Google Drive, MS Word, WPS, or any other document editor.</p>
</li>
<li><p>Write down the goals you want to achieve.</p>
</li>
<li><p>Save the file in either PDF or DOCX format.</p>
</li>
<li><p>Take the document and upload it to your Amazon S3.o</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736693525955/765b6c3a-ae68-4cbc-9df7-e896a1d63cbb.png" alt="upload a document" class="image--center mx-auto" width="1519" height="402" loading="lazy"></p>
<p>To verify if it's the correct file:</p>
<ul>
<li><p>Navigate to the <strong>Permissions</strong> tab.</p>
</li>
<li><p>Scroll down to <strong>Block public access.</strong></p>
</li>
<li><p>Click on <strong>Edit</strong> and uncheck the box.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736738796036/6ab41bc4-72a8-4874-a491-35bcdda49938.png" alt="block access " class="image--center mx-auto" width="1860" height="291" loading="lazy"></p>
<p>As shown above, it is currently set to "on." Uncheck it to turn it "off."</p>
<ul>
<li><p>On the same bucket settings page, modify the policy.</p>
</li>
<li><p>Scroll down, and you'll see that a bucket policy has been auto-generated.</p>
</li>
<li><p>Go ahead and copy the policy.</p>
</li>
</ul>
<pre><code class="lang-plaintext">{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}
</code></pre>
<ul>
<li>Go back to the bucket policy editor and paste the policy.</li>
</ul>
<p>Once you've completed these steps, your object will have public access.</p>
<p>Return to the <strong>Objects</strong> tab and click on the Object URL provided below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736740454730/3b36b380-912d-4a2a-a8c5-bee61bd42765.png" alt="3b36b380-912d-4a2a-a8c5-bee61bd42765" class="image--center mx-auto" width="729" height="89" loading="lazy"></p>
<p>With this URL, your upload is now visible.</p>
<h2 id="heading-step-3-create-an-amazon-simple-notification-service-sns"><strong>Step 3: Create an Amazon Simple Notification Service (SNS)</strong></h2>
<p><strong>SNS</strong> is a fully managed messaging service provided by AWS. It enables communication between applications or directly with users by sending notifications.</p>
<p>To create an SNS, follow these steps:</p>
<h4 id="heading-1-log-in-to-the-aws-management-console"><strong>1. Log in to the AWS management console</strong></h4>
<p>Then go to Amazon SNS. Navigate to the SNS Dashboard and select <strong>Topics</strong> from the left-hand menu.</p>
<p>To create a topic:</p>
<ul>
<li><p>Click <strong>Create topic</strong>.</p>
</li>
<li><p>Choose a <strong>Topic type</strong>: Standard (default) or FIFO (for ordered messages).</p>
</li>
<li><p>Enter a <strong>Name</strong> for your topic. (for example, <code>MyFirstSNSTopic</code>).</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736743311856/fa51ecb9-935a-4567-829c-b84ee3c1bee0.png" alt="sns topic creation" class="image--center mx-auto" width="1225" height="727" loading="lazy"></p>
</li>
<li><p>Configure optional settings like encryption, delivery retry policies, or tags.</p>
</li>
<li><p>Click <strong>Create topic</strong>.</p>
</li>
</ul>
<h4 id="heading-2-add-subscriptions"><strong>2. Add Subscriptions:</strong></h4>
<p>Once the topic is created, click on it to open the details page. Select the <strong>Subscriptions</strong> tab.</p>
<p>Click <strong>Create Subscription</strong> and choose:</p>
<ul>
<li><p><strong>Protocol</strong> can be Email, SMS, HTTP/S, Lambda, or SQS.</p>
</li>
<li><p><strong>Endpoints</strong> such as email address, phone number, or URL.</p>
</li>
</ul>
<p>Click <strong>Create Subscription</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736743374208/8005dc71-68ed-4d26-b79b-7b418959ab6c.png" alt="Subscription created" class="image--center mx-auto" width="1544" height="231" loading="lazy"></p>
<h4 id="heading-3-confirm-the-subscription"><strong>3. Confirm the Subscription:</strong></h4>
<p>If you selected email or SMS, a confirmation link or code will be sent to the provided endpoint. Follow the instructions to confirm the subscription.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736743525222/90c3b61d-eeb2-450d-a397-253b9e3c15db.png" alt="A confirm message from amazon sns" class="image--center mx-auto" width="760" height="372" loading="lazy"></p>
<p>Now that we’ve done this, let's create an Amazon Lambda function that will trigger the SNS so that the message will be sent to your mail.</p>
<h2 id="heading-step-4-create-an-iam-policy"><strong>Step 4: Create an IAM Policy</strong></h2>
<p>This policy is created to authorize Amazon Lambda to trigger the event and to ensure that CloudWatch is automatically triggered to monitor the application's events.</p>
<p>To create a policy, follow these steps:</p>
<h4 id="heading-1-log-in-to-the-aws-management-console-1"><strong>1. Log in to the AWS Management console.</strong></h4>
<p>In the left-hand menu, select <strong>Policies</strong>. Then:</p>
<ul>
<li><p>Click <strong>Create policy</strong>.</p>
</li>
<li><p>Choose the <strong>Visual</strong> tab, then select the <strong>SNS</strong> service.</p>
</li>
<li><p>Next, click the <strong>Choose</strong> tab to create a custom policy.</p>
</li>
</ul>
<pre><code class="lang-plaintext">{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "arn:aws:sns:REGION:ACCOUNT_ID:goal_topic"
        }
    ]
}
</code></pre>
<p>Then, replace the following placeholders with your info:</p>
<ul>
<li><p><code>region</code>: Your AWS region (for example, <code>us-east-1</code>).</p>
</li>
<li><p><code>account-id</code>: Your AWS account ID.</p>
</li>
<li><p><code>topic-name</code>: Your SNS topic name.</p>
</li>
</ul>
<h4 id="heading-2-view-and-create-the-policy"><strong>2. View and create the policy:</strong></h4>
<p>You can do this by following these steps:</p>
<ul>
<li><p>Click on the Review button.</p>
</li>
<li><p>Give your policy a <strong>Name</strong> (for example, <code>LambdaSNSPolicy</code>), and optionally, a <strong>Description</strong>.</p>
</li>
<li><p>Click <strong>Create policy</strong>.</p>
</li>
</ul>
<h4 id="heading-3-attach-policy-to-the-lambda-execution-role">3. Attach policy to the Lambda Execution Role</h4>
<p>Now, you need to attach the policy to your Lambda Execution Role. To do that, follow these steps:</p>
<ul>
<li><p>Go to the <strong>Roles</strong> section in the IAM Console.</p>
</li>
<li><p>Search for and select the execution role.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736749886571/70ea6752-5c13-465c-bc05-22e3da50127e.png" alt="lambdaexecution role" class="image--center mx-auto" width="1527" height="275" loading="lazy"></p>
<ul>
<li><p>Next, search for the policy you just created and select it.</p>
</li>
<li><p>Click <strong>Attach policy</strong>.</p>
</li>
</ul>
<p>Both policies will be automatically attached.</p>
<h2 id="heading-step-5-create-an-amazon-lambda-function"><strong>Step 5: Create an Amazon Lambda function</strong></h2>
<p>Amazon Lambda is a service from AWS that lets you run code without managing servers. You upload your code, and Lambda automatically runs and scales it when needed.</p>
<p>Follow these steps to create an Amazon Lambda function:</p>
<h4 id="heading-1-log-in-to-aws-management-console"><strong>1. Log in to AWS Management Console</strong>:</h4>
<p>Navigate to AWS Lambda.</p>
<h4 id="heading-2-create-a-function"><strong>2. Create a Function</strong>:</h4>
<p>Click on the <strong>Create function</strong> and choose the option <strong>Author from scratch</strong>.</p>
<p>Fill in the details:</p>
<ul>
<li><p><strong>Function name</strong>: Enter a unique name (for example, <code>SNSLambdaFunction</code>).</p>
</li>
<li><p><strong>Runtime</strong>: Select the runtime (for example, Python, Node.js, Java, and so on).</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736751631488/fe254e56-89f1-4938-b2a9-e59b24e54a04.png" alt="creating a function" class="image--center mx-auto" width="1512" height="515" loading="lazy"></p>
<ul>
<li><strong>Role</strong>: Choose or create a role. If you already have a role, select <strong>Use an existing role</strong>. Otherwise, select <strong>Create a new role with basic Lambda permissions</strong>.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736751816631/97475623-10dc-4427-87ab-7e8789526e9e.png" alt="role choosing" class="image--center mx-auto" width="1108" height="346" loading="lazy"></p>
<ul>
<li>Click the <strong>Create function button</strong>.</li>
</ul>
<h4 id="heading-3-paste-in-the-code"><strong>3. Paste in the code</strong>:</h4>
<p>On the Lambda function’s page, go to the <strong>Configuration</strong> tab:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736753706928/b0351665-c506-454e-9c71-b4a52fce5a0a.png" alt="configuration tab" class="image--center mx-auto" width="1517" height="404" loading="lazy"></p>
<p>Remember, we are trying to fetch a quote. I'll add the ARN of the topic we created here and include my API keys. But for this tutorial, I will use the API directly to fetch the data.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736754071465/1322e207-5c51-45f1-979f-05f2388e9557.png" alt="1322e207-5c51-45f1-979f-05f2388e9557" class="image--center mx-auto" width="1517" height="404" loading="lazy"></p>
<h4 id="heading-4-write-the-lambda-code"><strong>4. Write the Lambda Code:</strong></h4>
<p>Go to the <strong>Code</strong> tab in your Lambda function. Then write or paste the code from your IDE to process the incoming SNS messages.</p>
<p>Example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736754319755/60224578-53c2-43f6-9bf1-8cba6afaa1a3.png" alt="Testing code" class="image--center mx-auto" width="1513" height="405" loading="lazy"></p>
<p>Here’s the code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> urllib.request
<span class="hljs-keyword">import</span> boto3

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fetch_random_quote</span>():</span>
    <span class="hljs-string">"""
    Fetches a random quote from the ZenQuotes API.
    """</span>
    api_url = <span class="hljs-string">"https://zenquotes.io/api/random"</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">with</span> urllib.request.urlopen(api_url) <span class="hljs-keyword">as</span> response:
            data = json.loads(response.read().decode())
            <span class="hljs-keyword">if</span> data <span class="hljs-keyword">and</span> isinstance(data, list):
                <span class="hljs-comment"># Format the quote and author</span>
                quote = data[<span class="hljs-number">0</span>].get(<span class="hljs-string">"q"</span>, <span class="hljs-string">"No quote available"</span>)
                author = data[<span class="hljs-number">0</span>].get(<span class="hljs-string">"a"</span>, <span class="hljs-string">"Unknown author"</span>)
                <span class="hljs-keyword">return</span> <span class="hljs-string">f'"<span class="hljs-subst">{quote}</span>" - <span class="hljs-subst">{author}</span>'</span>
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">return</span> <span class="hljs-string">"No quote available."</span>
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Error fetching random quote: <span class="hljs-subst">{e}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Failed to fetch quote."</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    <span class="hljs-string">"""
    AWS Lambda handler function to fetch a random quote and publish it to an SNS topic.
    """</span>
    <span class="hljs-comment"># Get the SNS topic ARN from environment variables</span>
    sns_topic_arn = os.getenv(<span class="hljs-string">"SNS_TOPIC_ARN"</span>)
    sns_client = boto3.client(<span class="hljs-string">"sns"</span>)

    <span class="hljs-comment"># Fetch a random quote</span>
    quote = fetch_random_quote()
    print(<span class="hljs-string">f"Fetched Quote: <span class="hljs-subst">{quote}</span>"</span>)

    <span class="hljs-comment"># Publish the quote to SNS</span>
    <span class="hljs-keyword">try</span>:
        sns_client.publish(
            TopicArn=sns_topic_arn,
            Message=quote,
            Subject=<span class="hljs-string">"Daily Random Quote to help you stay motivated and inspired to achieve your goals"</span>,
        )
        print(<span class="hljs-string">"Quote published to SNS successfully."</span>)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Error publishing to SNS: <span class="hljs-subst">{e}</span>"</span>)
        <span class="hljs-keyword">return</span> {<span class="hljs-string">"statusCode"</span>: <span class="hljs-number">500</span>, <span class="hljs-string">"body"</span>: <span class="hljs-string">"Error publishing to SNS"</span>}

    <span class="hljs-keyword">return</span> {<span class="hljs-string">"statusCode"</span>: <span class="hljs-number">200</span>, <span class="hljs-string">"body"</span>: <span class="hljs-string">"Quote sent to SNS"</span>}
</code></pre>
<h4 id="heading-5-save"><strong>5. Save:</strong></h4>
<p>Click on the deploy button to save.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736756768348/8ec83b6b-874c-47c3-a1ad-8d8b85cd6d48.png" alt="8ec83b6b-874c-47c3-a1ad-8d8b85cd6d48" class="image--center mx-auto" width="336" height="48" loading="lazy"></p>
<h4 id="heading-6-test-your-lambda-function"><strong>6. Test Your Lambda Function</strong>:</h4>
<p>Go to the <strong>Test</strong> tab and create a new test event.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736757103169/834c5b6c-8796-4a52-bcc3-cbd8009b01da.png" alt="TEST EVENT" class="image--center mx-auto" width="1496" height="671" loading="lazy"></p>
<p>Then save and run the test. If it’s successful, a message will be sent:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736757309807/d97f9648-0000-4ec8-b287-df5250b3be0a.png" alt="success msg" class="image--center mx-auto" width="1466" height="365" loading="lazy"></p>
<p>This means the message has been created for you</p>
<p>Finally, check your email or SMS, depending on the endpoint you used for this tutorial. In my case, I used email.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736757884384/d5b949d4-0804-4694-9674-77fc0265e2e8.png" alt="email notification" class="image--center mx-auto" width="1314" height="309" loading="lazy"></p>
<h2 id="heading-step-6-create-an-eventbridge"><strong>Step 6: Create an EventBridge</strong></h2>
<p>Amazon EventBridge is a service that helps you connect applications and AWS services such as the Amazon SNS and Amazon Lambda.</p>
<p>To create an Amazon EventBridge rule, follow these steps:</p>
<h4 id="heading-1-navigate-to-eventbridge"><strong>1. Navigate to EventBridge</strong>:</h4>
<p>In the search bar, type <strong>EventBridge</strong> and select it from the services list.</p>
<h4 id="heading-2-create-a-rule"><strong>2. Create a Rule:</strong></h4>
<p>In the EventBridge console, click <strong>Rules</strong> on the left panel. Then click the <strong>Create rule</strong> button.</p>
<h4 id="heading-3-set-up-the-rule-details"><strong>3. Set Up the Rule Details</strong>:</h4>
<ul>
<li><p><strong>Name</strong>: Enter a unique name for your rule.</p>
</li>
<li><p><strong>Description (optional)</strong>: Add a description to explain what this rule does.</p>
</li>
</ul>
<h4 id="heading-4-choose-the-event-bus"><strong>4. Choose the Event Bus</strong>:</h4>
<p>Select <strong>Default event bus</strong> (or another event bus if you've created one).</p>
<h4 id="heading-5-define-the-event-pattern-or-schedule"><strong>5. Define the Event Pattern or Schedule</strong>:</h4>
<p><strong>For Event Pattern</strong>:</p>
<ul>
<li><p>Choose an <strong>AWS Service</strong> as the event source.</p>
</li>
<li><p>Select the specific <strong>event type</strong> (for example, an S3 file upload or an EC2 instance state change).</p>
</li>
</ul>
<p><strong>For Schedule</strong>:</p>
<ul>
<li>Choose the <strong>Schedule</strong> option to run the rule on a fixed interval (for example, every 5 minutes).</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736759371221/ca28dcf2-061a-4c8f-8191-32bdf8380111.png" alt="rule details" class="image--center mx-auto" width="1513" height="817" loading="lazy"></p>
<ul>
<li>Click on continue. This takes you to the specific details page where:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736759696183/44ba44e8-2a1b-4cd8-928b-6fe87cc35c4e.png" alt="schedule page" class="image--center mx-auto" width="1509" height="795" loading="lazy"></p>
<ul>
<li><p>Scroll down and click on the cron scheduler. The cron scheduler specifies what time the message will be sent.</p>
</li>
<li><p>Select <strong>"Off"</strong> for the flexible time window option.</p>
</li>
<li><p>Review the rule details to confirm everything is correct.</p>
</li>
<li><p>Click the <strong>"Next"</strong> button to proceed to the <strong>Target</strong> page.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736760213450/c4f6cccf-7f89-485d-802e-051284c89f9a.png" alt="cron scheduler " width="1475" height="798" loading="lazy"></p>
<p>  The picture above shows when the time the messages will be sent.</p>
<ul>
<li>On the Target page, select <strong>AWS Lambda</strong> to invoke your function.</li>
</ul>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736761288843/e35a7153-48d7-4b34-86ce-9cba68bd5161.png" alt="the target page" class="image--center mx-auto" width="1485" height="511" loading="lazy"></p>
<ul>
<li>Scroll down to invoke and choose the function you created.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736761539389/4faa1dbc-5635-4de3-8087-15a444298b2e.png" alt="invoke the function" class="image--center mx-auto" width="1114" height="193" loading="lazy"></p>
<ul>
<li>Click the "Next" button to proceed. This will take you to the settings page. Under the permissions section, select "Use existing rule."</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736782488383/ffbbcc4e-6379-4c9c-af16-f4c017e7426c.png" alt="setting page" class="image--center mx-auto" width="1509" height="366" loading="lazy"></p>
<ul>
<li>Lastly, go to the review and create a schedule:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736783029778/2d0e7ff9-bb7d-462d-a644-23517f47e53e.png" alt="2d0e7ff9-bb7d-462d-a644-23517f47e53e" class="image--center mx-auto" width="1463" height="776" loading="lazy"></p>
<ul>
<li>The next page shows you all the details:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736783558777/4545d2da-5020-46c3-a265-5499e6b4e74f.png" alt="4545d2da-5020-46c3-a265-5499e6b4e74f" class="image--center mx-auto" width="1512" height="828" loading="lazy"></p>
<p>Using the EventBrige creates a scheduler for the users.</p>
<h2 id="heading-step-7-upload-your-code"><strong>Step 7: Upload Your Code</strong></h2>
<p>Finally, upload your code to GitHub and include proper documentation to help explain how the code works.</p>
<p>Check this documentation out if you don’t know how: <a target="_blank" href="https://docs.github.com/en/get-started/start-your-journey/uploading-a-project-to-github">Uploading a project to GitHub</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>If you’ve followed all these steps, you will have created a Goal Manifestation Quote App using AWS Lambda, Amazon S3, Amazon SNS, and Amazon EventBridge. This app fetches motivational quotes and sends them to subscribers on a schedule.</p>
<p>You can find the repository link <a target="_blank" href="https://github.com/ijayhub/goal-manifestation-quote">here</a>.</p>
<p>Feel free to share your progress or ask questions if you have any issues.</p>
<p>If you found this article helpful, share it with others.</p>
<p>Stay updated with my projects by following me on <a target="_blank" href="https://https//twitter.com/ijaydimples">Twitter</a>, <a target="_blank" href="https://www.linkedin.com/in/ijeoma-igboagu/">LinkedIn</a> and <a target="_blank" href="https://github.com/ijayhub">GitHub</a></p>
<p>Thank you for reading 💖.</p>
<p><strong>Disclaimer:</strong><br>The resources shown in this article, including the S3 bucket and its ARN, have been deleted and no longer exist. The details visible in the screenshots are used solely for demonstration purposes.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy an AWS Lambda Function with Serverless Framework ]]>
                </title>
                <description>
                    <![CDATA[ Serverless computing has revolutionized the way developers build and deploy applications in the cloud. It takes away the complexities of server management, allowing developers to focus solely on writing code and delivering value to their users. In th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-aws-lambda-with-serverless/</link>
                <guid isPermaLink="false">66b906ab53c4132f77b5c301</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Destiny Erhabor ]]>
                </dc:creator>
                <pubDate>Mon, 18 Sep 2023 23:52:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/09/joshua-woroniecki-lzh3hPtJz9c-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Serverless computing has revolutionized the way developers build and deploy applications in the cloud. It takes away the complexities of server management, allowing developers to focus solely on writing code and delivering value to their users.</p>
<p>In the realm of serverless computing, AWS Lambda stands out as a leading platform for running serverless functions in a scalable and cost-effective manner.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-serverless-framework">What is Serverless Framework?</a></li>
<li><a class="post-section-overview" href="#heading-purpose-and-scope-of-the-guide">Purpose and Scope of the Guide</a></li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-the-aws-cli">How to Configure the AWS CLI</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-the-iam-role">How to Create the IAM Role</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-serverless-project">How to Create a Serverless Project</a></li>
<li><a class="post-section-overview" href="#heading-how-to-write-the-python-function">How to Write the Python Function</a></li>
<li><a class="post-section-overview" href="#heading-how-to-define-serverless-configuration">How to Define Serverless Configuration</a></li>
<li><a class="post-section-overview" href="#heading-how-to-deploy-the-python-function">How to Deploy the Python Function</a></li>
<li><a class="post-section-overview" href="#heading-how-to-test-the-api">How to Test the API</a></li>
<li><a class="post-section-overview" href="#heading-monitoring-and-logging">Monitoring and Logging</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-what-is-serverless-framework">What is Serverless Framework?</h2>
<p>Serverless Framework is a powerful tool that simplifies the deployment and management of serverless applications across various cloud providers, including Amazon Web Services (AWS). This guide aims to walk you through the process of using the Serverless Framework to deploy a simple Python function to AWS Lambda, expose it via API Gateway, and monitor it using AWS CloudWatch.</p>
<h3 id="heading-purpose-and-scope-of-the-guide">Purpose and Scope of the Guide</h3>
<p>The purpose of this guide is to provide you with a step-by-step tutorial on deploying a serverless Python function on AWS using the Serverless Framework. Whether you're new to serverless computing or looking to expand your skills, this tutorial is designed to help you with the following:</p>
<ul>
<li>How to set up the necessary prerequisites, including AWS account configuration.</li>
<li>How to create a new serverless project using the Serverless Framework.</li>
<li>How to write a Python function that will be deployed to AWS Lambda.</li>
<li>How to define the serverless configuration in a  <code>serverless.yml</code> file.</li>
<li>How to deploy the Python function and API Gateway.</li>
<li>How to test the deployed API using various tools like cURL or Postman.</li>
<li>How to set up monitoring and logging with AWS CloudWatch.</li>
</ul>
<p>By the end of this article, you'll have a clear understanding of how to leverage the Serverless Framework to deploy and manage serverless applications on AWS. </p>
<p>You'll also gain practical experience in deploying serverless functions and exposing them through an API endpoint, paving the way for building and scaling serverless applications in your projects.</p>
<p>Now, let's dive into the prerequisites needed to get started with this tutorial.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, you'll need the following:</p>
<ul>
<li><a target="_blank" href="https://www.console.aws.amazon.com">An AWS account</a>.</li>
<li><a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html">The AWS CLI</a> (Command Line Interface).</li>
<li><a target="_blank" href="https://www.serverless.com/framework/docs/getting-started/">The Serverless Framework</a>.</li>
</ul>
<h2 id="heading-how-to-configure-the-aws-cli">How to Configure the AWS CLI</h2>
<p>You'll need to set the AWS credentials for the AWS CLI if you have not done that already. You'll be using it along with the Serverless Framework to deploy the resources on AWS. </p>
<p>You can create the AWS credentials file by entering the following command in the terminal:</p>
<pre><code class="lang-bash"> cat &lt;&lt;EOF &gt; ~/.aws/credentials
    [default]
    aws_access_key_id = &lt;REPLACE_WITH_YOUR_SECRET_KEY&gt;
    aws_secret_access_key = &lt;REPLACE_WITH_YOUR_ACCESS_KEY&gt; 
  EOF

  cat &lt;&lt;EOF &gt; ~/.aws/config
    [default]
    region = eu-west-1
    output = json
  EOF
</code></pre>
<h2 id="heading-how-to-create-the-iam-role">How to Create the IAM Role</h2>
<p>The IAM role is also used by the Serverless Framework to deploy the resources on AWS. Enter the following command to create the role:</p>
<pre><code class="lang-bash"> aws iam create-role --role-name serverlessLabs --assume-role-policy-document <span class="hljs-string">'{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}'</span>
</code></pre>
<p>This policy allows the role to be used by the AWS Lambda service.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/create-role-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Enter the following command to attach the <strong><code>AWSLambdaBasicExecutionRole</code></strong> policy to the role:</p>
<pre><code class="lang-bash">aws iam attach-role-policy --role-name serverlessLabs --policy-arn arn:aws:iam::aws:policy/AWSLambda_FullAccess
</code></pre>
<p>To verify that the role has been created successfully, you can run the following command to get information about the IAM role:</p>
<pre><code class="lang-bash">aws iam get-role --role-name serverlessLabs
</code></pre>
<p>Here's what the information should look like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/role-aws-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-create-a-serverless-project">How to Create a Serverless Project</h2>
<p>This project is a simple python function that is deployed to AWS Lambda, API Gateway, and CloudWatch using the Serverless Framework. </p>
<p>The function is triggered by an HTTP GET request and returns a simple string. The function is deployed to the eu-west-1 region.</p>
<p>First, install Serverless Framework using <code>npm</code>:</p>
<pre><code class="lang-bash">npm install -g serverless
</code></pre>
<p>Next, create a new Serverless Framework project using the <code>serverless</code> command and then follow the prompt:</p>
<pre><code class="lang-bash">serverless
</code></pre>
<p>Then choose AWS Python Starter from the template list. Give it any name of your choice – I used <strong>serverless-lab</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/serverless-template-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After the command runs successfully, you will see the two main components created: <code>serverless.yaml</code>, and <code>handler.py.</code></p>
<h2 id="heading-how-to-write-the-python-function">How to Write the Python Function</h2>
<p>To keep things organized, let's create a folder named <strong>functions</strong>, and create a file named <code>__init__.py</code> inside it. You can do that using this command:</p>
<pre><code class="lang-bash">mkdir <span class="hljs-built_in">functions</span>  touch <span class="hljs-built_in">functions</span>/__init__.py
</code></pre>
<p>Create your first function by creating a file named <code>first_function.py</code> inside the <strong>functions</strong> folder:</p>
<pre><code class="lang-bash">touch <span class="hljs-built_in">functions</span>/first_function.py
</code></pre>
<p>Then open the <code>first_function.py</code> file, and paste the following Python code to define the function you'll deploy:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">first_function</span>(<span class="hljs-params">event, context</span>):</span>
  print(<span class="hljs-string">"The first function has been invoked!!"</span>)
  <span class="hljs-keyword">return</span> {
    <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
    <span class="hljs-string">'body'</span>: <span class="hljs-string">"Hello, World!.\n This is the first function."</span>
  }
</code></pre>
<p>This code above is a simple Python function that returns a JSON object with status code and body values. As you can see, we inserted the two parameters — <code>event</code> and <code>context</code> — required from the functions as a Serverless Framework convention.</p>
<p>Next, open the <code>handler.py</code> file and delete its content and paste the following Python code to define the handler that will be invoked when the function is triggered:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> functions.first_function <span class="hljs-keyword">import</span> first_function
</code></pre>
<p>The code above exposes the function you created in the <code>first_function.py</code> file. We imported the function, and exposed it to the framework.</p>
<h2 id="heading-how-to-define-serverless-configuration">How to Define Serverless Configuration</h2>
<p>To start with the configuration, open the <code>serverless.yaml</code> file and delete all of its content and paste the following YAML code to define the microservice you will deploy:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">service:</span> <span class="hljs-string">serverless-lab</span>

<span class="hljs-attr">provider:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">aws</span>
  <span class="hljs-attr">runtime:</span> <span class="hljs-string">python3.7</span>
  <span class="hljs-attr">lambdaHashingVersion:</span> <span class="hljs-number">20201221</span>
  <span class="hljs-attr">region:</span> <span class="hljs-string">eu-west-1</span>
  <span class="hljs-attr">timeout:</span> <span class="hljs-number">10</span> <span class="hljs-comment"># You set a timeout of 10 seconds for the functions</span>
  <span class="hljs-attr">role:</span> <span class="hljs-string">arn:aws:iam::155318317806:role/serverlessLabs</span> <span class="hljs-comment"># Enter your Arn role here</span>
  <span class="hljs-attr">memorySize:</span> <span class="hljs-number">512</span>

<span class="hljs-attr">functions:</span>
  <span class="hljs-attr">first_function:</span>
    <span class="hljs-attr">handler:</span> <span class="hljs-string">handler.first_function</span>
    <span class="hljs-attr">events:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">http:</span>
        <span class="hljs-attr">path:</span> <span class="hljs-string">first</span>
        <span class="hljs-attr">method:</span> <span class="hljs-string">get</span>
</code></pre>
<p>Let's break down each section line by line:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">service:</span> <span class="hljs-string">serverless-lab</span>
</code></pre>
<p><code>**service**</code> specifies the name of your Serverless service or project. In this case, it's named "serverless-lab," which will be used as the service name when deploying to AWS.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">provider:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">aws</span>
  <span class="hljs-attr">runtime:</span> <span class="hljs-string">python3.7</span>
  <span class="hljs-attr">lambdaHashingVersion:</span> <span class="hljs-number">20201221</span>
  <span class="hljs-attr">region:</span> <span class="hljs-string">eu-west-1</span> <span class="hljs-comment"># enter your region</span>
  <span class="hljs-attr">profile:</span> <span class="hljs-string">personalCaesarAcc</span>
  <span class="hljs-attr">timeout:</span> <span class="hljs-number">10</span> <span class="hljs-comment"># You set a timeout of 10 seconds for the functions</span>
  <span class="hljs-attr">role:</span> <span class="hljs-string">arn:aws:iam::155318317806:role/serverlessLabs</span> <span class="hljs-comment"># Enter your Arn role here</span>
  <span class="hljs-attr">memorySize:</span> <span class="hljs-number">512</span>
</code></pre>
<p><code>**provider**</code> defines the AWS provider for your service. It specifies various configuration settings for AWS Lambda functions and other AWS resources.</p>
<ul>
<li><code>name: aws</code> specifies that you are using AWS as your cloud provider.</li>
<li><code>runtime: python3.7</code> sets the runtime for AWS Lambda functions to Python 3.7.</li>
<li><code>lambdaHashingVersion: 20201221</code> specifies the Lambda function hashing version. This is an internal AWS setting.</li>
<li><code>region: eu-west-1</code> specifies the AWS region where your service will be deployed. You can replace "eu-west-1" with your desired AWS region.</li>
<li><code>timeout: 10</code> sets a timeout of 10 seconds for AWS Lambda functions. This means that each function should complete its execution within 10 seconds.</li>
<li><code>role: arn:aws:iam::155318317806:role/serverlessLabs</code> specifies the AWS IAM role ARN that your Lambda functions will assume. This role defines the permissions your functions have within AWS services. You can replace this with the ARN of your desired IAM role.</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">functions:</span>
  <span class="hljs-attr">first_function:</span>
    <span class="hljs-attr">handler:</span> <span class="hljs-string">handler.first_function</span>
    <span class="hljs-attr">events:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">http:</span>
        <span class="hljs-attr">path:</span> <span class="hljs-string">first</span>
        <span class="hljs-attr">method:</span> <span class="hljs-string">get</span>
</code></pre>
<p><code>**functions**</code> defines the AWS Lambda functions in your service.</p>
<ul>
<li><code>first_function</code> denotes the name of your AWS Lambda function.</li>
<li><code>handler: handler.first_function</code> specifies the entry point for this function, which is <code>handler.first_function</code> in the <code>handler</code> module. This is typically in the <code>&lt;module_name&gt;.&lt;function_name&gt;</code> format.</li>
<li><code>events</code> specifies the events that triggers the function.</li>
<li><code>- http</code> indicates that the function is triggered by an HTTP event (API Gateway).</li>
<li><code>path: first</code> specifies the API endpoint path (<code>/first</code>) that triggers the function.</li>
<li><code>method: get</code> specifies that this function is triggered when an HTTP GET request is made to the specified path.</li>
</ul>
<h2 id="heading-how-to-deploy-the-python-function">How to Deploy the Python Function</h2>
<p>You can use the command below to deploy the microservice on AWS:</p>
<pre><code class="lang-bash">serverless deploy
</code></pre>
<p>After a while, the deployment will be completed and you can see information like the endpoint, hosted on API Gateway, to trigger the function you just deployed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/deploy-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The framework deployed the function on AWS Lambda and, because you attached an HTTP trigger to it. It has deployed an API on API Gateway to let the function be reachable.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/function-on-aws-api-gateway-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-test-the-api">How to Test the API</h2>
<p>From the deployment, you have a single function named <code>first_function</code>, and a single HTTP GET endpoint. </p>
<p>Using the GET endpoint (the endpoint generated in the terminal after deploying the function) in your browser, you can call the function:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/url-test-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The image above shows the functionality created in the deployed function running in the browser.</p>
<h2 id="heading-monitoring-and-logging">Monitoring and Logging</h2>
<p>The log group is automatically saved on AWS CloudWatch because there is a print statement defined in the function. Enter the following command to access the function's logs:</p>
<pre><code class="lang-bash">serverless logs -f first_function
</code></pre>
<p>AWS CloudWatch is the native AWS logging service that lets you monitor and access logs from your applications. You can find log groups, and you can also apply filter expressions on logs to retrieve those you need.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/cloudwatch-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can delete the microservice and resources you just deployed using the <code>serverless remove</code> command. </p>
<p>Check out <a target="_blank" href="https://github.com/Caesarsage/Devops-projects/tree/main/project-08">my GitHub repository</a> to see the full code</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this comprehensive guide, we've explored the powerful world of serverless computing and demonstrated how to harness its capabilities using the Serverless Framework and Amazon Web Services (AWS). </p>
<p>You've embarked on a journey from setting up your development environment to deploying a simple Python function as an AWS Lambda-backed API, all while gaining insights into monitoring and logging with AWS CloudWatch.</p>
<p>This guide serves as a starting point for your serverless journey. As you become more proficient with the Serverless Framework and AWS, you'll be able to build and deploy sophisticated serverless applications that scale dynamically and meet the demands of modern, cloud-native architectures.</p>
<p>As always, I hope you enjoyed the article and learned something new. If you want, you can also follow me on <a target="_blank" href="https://www.linkedin.com/in/destiny-erhabor">LinkedIn</a> or <a target="_blank" href="https://twitter.com/caesar_sage">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ AWS Lambda Interview Questions and Answers ]]>
                </title>
                <description>
                    <![CDATA[ By Mugilan Ragupathi In this article, I'll go over some of the most commonly asked questions that come up in interviews about AWS Lambda.  Note that this is not an exhaustive list – but you can use this guide as a reference to refresh your knowledge ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/aws-lambda-interview-questions/</link>
                <guid isPermaLink="false">66d46040d7a4e35e3843498f</guid>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ interview questions ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 07 Dec 2022 20:54:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/12/AWS-Lambda-Interview-Questions.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mugilan Ragupathi</p>
<p>In this article, I'll go over some of the most commonly asked questions that come up in interviews about AWS Lambda. </p>
<p>Note that this is not an exhaustive list – but you can use this guide as a reference to refresh your knowledge and get pointers for further study.</p>
<p>Most of the questions will be based on your experience or on certain scenarios. The questions are in the headings and you'll find the notes for the rationale behind asking the questions just below them.</p>
<h2 id="heading-explain-your-last-project-involving-aws-lambda">Explain your last project involving AWS Lambda</h2>
<p><em>The interviewer wants to know about your real-life experience using AWS Lambda. Don't bluff here, as the interviewer may ask further questions based on the answers to this question.</em> </p>
<p>You might have built a serverless API, systems involving microservices, image/video conversion, log analysis, and many more. Just explain your project in detail and tell about the business benefits of that project so that the interviewer knows you're seeing the big picture.</p>
<h2 id="heading-what-services-have-you-integrated-with-aws-lambda">What services have you integrated with AWS Lambda?</h2>
<p><em>This is an extension of the previous question. This is not a laundry list of all event sources that can connect to AWS Lambda. Only tell about the services that you've really used.</em> </p>
<p>You may have used S3, SNS, SQS, Kinesis, DynamoDB, SES, or others. Not all projects will be completely serverless. </p>
<p>If you've used any non-serverless component along with AWS Lambda, mention those too. For example, you might've used AWS Lambda with RDS. If you've used such a configuration,  you can explain about that along with your reasoning.</p>
<h2 id="heading-explain-the-concept-of-cold-and-warm-starts-in-aws-lambda">Explain the concept of cold and warm starts in AWS Lambda</h2>
<p><em>There are 2 reasons for asking this question. They want to know the runtimes that you've used, and they want to know if you know the other runtimes that could cause a cold start.</em></p>
<p>Lambda services receive a request to run a lambda function. The service prepares the execution environment by downloading the handler function code and by allocating memory along with other configuration. </p>
<p>Even though you're not billed for this <code>execution environment</code> preparation time, you'll have to face the delay in invoking your lambda function. This delay is called a "cold start".</p>
<p>The cold start timing is less significant for TypeScript and Python runtime environments, whereas it's a bit higher for Java or C# runtime environments.</p>
<p>To improve performance, the lambda service will keep the execution environment for some time. When you receive the request for the same lambda function again during this period, your handler can start executing immediately. This type of invocation is called a "<em>warm start</em>".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-18.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://aws.amazon.com/blogs/compute/operating-lambda-performance-optimization-part-1/">Image source</a></em></p>
<h2 id="heading-whats-the-difference-between-synchronous-and-asynchronous-invocation-in-aws-lambda">What's the difference between synchronous and asynchronous invocation in AWS Lambda?</h2>
<p><em>Even though this seems to be straightforward question, this has many implications for your design and error handling.</em> </p>
<p>In synchronous invocation, the caller will wait for the execution to complete. But in asynchronous invocation, the caller will put the event in an internal queue which will later be processed in the lambda function.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-19.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html">Image source</a></em></p>
<p>An important point to note here is that you can't dictate the type of invocation and it depends on the service that you use with AWS Lambda.</p>
<p>For example, if you're building serverless APIs using API Gateway, it'll be a synchronous invocation. But if you're using S3, it'll be an asynchronous invocation.</p>
<h2 id="heading-how-do-you-implement-error-handling-and-retry-logic-in-lambda">How do you implement error handling and retry logic in Lambda?</h2>
<p><em>Any component in event driven system that may fail will fail. So interviewer wants to know how you've handled the error and how you retried in your previous projects. Below are some examples. Always explain with concrete examples.</em></p>
<p>This depends on the service that you're using with AWS Lambda. Let's discuss this with some examples.</p>
<p>If you're building a serverless API, it is better to return that error to the calling client (could be a front end app in this case). Then you let your front end logic decide what to display to the user based on the type of the error.</p>
<p>If you're using Lambda with SQS, it is better to use a Dead Letter Queue so that you know what messages have failed to process. For this same reason, many of the systems that use SNS may use SQS, too.</p>
<p>In the below code, we're using a dead letter queue. If any message fails to process after a certain number of times (as specified by <code>maxReceiveCount</code>), it's sent to the dead letter queue. This behaviour is specific to lambda when used along with queues.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> queue = <span class="hljs-keyword">new</span> sqs.Queue(<span class="hljs-built_in">this</span>, <span class="hljs-string">'AwsLambdaSqsQueue'</span>, {
      visibilityTimeout: cdk.Duration.seconds(<span class="hljs-number">300</span>),
      receiveMessageWaitTime: cdk.Duration.seconds(<span class="hljs-number">20</span>),
      deadLetterQueue: {
        queue: <span class="hljs-keyword">new</span> sqs.Queue(<span class="hljs-built_in">this</span>, <span class="hljs-string">'AwsLambdaDlq'</span>),
        maxReceiveCount: <span class="hljs-number">5</span>,
      },
    });
</code></pre>
<p>When lambda is invoked with any other services, you can configure the number of retries with a maximum value of 2. This means that you can have maximum of 2 retries apart from the initial invocation. For example, you want to trigger based on the S3 object upload and your lambda will try at most 3 times.</p>
<h2 id="heading-explain-your-workflows-for-development-and-deployment-of-aws-lambda-functions">Explain your workflows for development and deployment of AWS Lambda functions</h2>
<p><em>Talk about the frameworks that you've used. The interviewer may expect you to talk about testing lambda functions as well.</em> </p>
<p>You can explain which framework(s) you've used to develop and deploy lambda functions. You can also talk about any IaC (Infrastructure as Code) tool that you've used. </p>
<p>Below is non-exhaustive list of the most commonly used frameworks:</p>
<ul>
<li>Serverless</li>
<li>AWS CDK</li>
<li>AWS SAM</li>
<li>CloudFormation</li>
<li>Pulumi</li>
</ul>
<p>If you've used Terraform, you can talk about that as well.</p>
<h2 id="heading-can-lambda-be-invoked-when-you-get-an-email-to-a-particular-support-email-address-if-yes-design-that-system-if-not-explain-why">Can Lambda be invoked when you get an email to a particular support email address? If yes, design that system. If not, explain why.</h2>
<p>Yes, you can. You can create receipt rule set and add a rule which triggers the lambda function.</p>
<p>You should store the email to S3 and trigger the lambda after that so that you can have the copy of the email for any further reference.</p>
<p>You can refer to <a target="_blank" href="https://www.freecodecamp.org/news/how-to-receive-emails-via-your-sites-contact-us-form-with-aws-ses-lambda-api-gateway/">this article</a> on how to receive emails from a contact form.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-20.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://www.freecodecamp.org/news/how-to-receive-emails-via-your-sites-contact-us-form-with-aws-ses-lambda-api-gateway/">Image source</a></em></p>
<h2 id="heading-can-one-lambda-function-call-another-lambda-function">Can one lambda function call another lambda function?</h2>
<p><em>The interviewer wants to know whether you know this anti-pattern.</em></p>
<p>You can do this, but it is not recommended. If you want to design a workflow which involves multiple lambda functions, you can use step functions. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-21.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://aws.amazon.com/step-functions/">Image source</a></em></p>
<p>You can read more about step functions <a target="_blank" href="https://aws.amazon.com/step-functions/">here</a>.</p>
<p>Another standard approach is to emit an event and trigger a lambda based on the event. You can use SQS, SNS, or EventBridge as intermediary for these events.</p>
<h2 id="heading-can-you-execute-queries-on-an-rds-instance-in-a-private-subnet-using-lambda">Can you execute queries on an RDS instance (in a private subnet) using Lambda?</h2>
<p>Yes, you can execute the query in RDS using AWS Lambda. For that you can have your lambda inside the same VPC. </p>
<p>There may be some performance implications if you use AWS Lambda directly with RDS because of the database connection creation time. To avoid those, you can use an RDS Proxy.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-22.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://www.freecodecamp.org/news/aws-lambda-rds/">Image source</a></em></p>
<p>Here's a <a target="_blank" href="https://www.freecodecamp.org/news/aws-lambda-rds/">detailed step by step guide</a> that shows you how to do that.</p>
<h2 id="heading-aws-lambda-provides-many-benefits-what-are-the-cons-of-using-aws-lambda">AWS Lambda provides many benefits. What are the cons of using AWS Lambda?</h2>
<p><em>The interviewer wants to know about your thought process. Don't say AWS Lambda solves all the problems :-)</em> </p>
<p>Yeah, Lambda provides many benefits such as cost and scalability without any need to maintain the servers. But it's not the answer to everything – like any service, it has its own problems (and you should be able to discuss them):</p>
<ul>
<li>Debugging: If you're using serverless architectures using Lambda, you might have to rely on logging to find the root cause of the issue. This is because your application will be distributed across many services/ lambda functions. </li>
<li>Testing: You can mock AWS services in your local testing. But it is better to have a separate environment in AWS to test your lambdas. This makes testing a bit complex.</li>
<li>Background jobs: Lambda has a timeout limit of 15 minutes. If you want any particular task to take more than 15 minutes, you might have to move to Fargate or some other solution.</li>
<li>Cost: If you're running a high traffic application which processes the requests 24/7, using lambda can be expensive. It is better to use Fargate, EC2 or other services, if you have constant high traffic.</li>
</ul>
<h2 id="heading-how-do-you-manage-concurrency-and-scaling-in-aws-lambda">How do you manage concurrency and scaling in AWS Lambda?</h2>
<p><em>Bonus points if you talk about the issues that you've faced in these situations.</em></p>
<p>Concurrency is the ability to execute multiple lambda functions at the same time. Scaling is the process of increasing the number of copies of your lambda function to handle the incoming requests.</p>
<p>You can control concurrency by setting the value of <code>reserved concurrency</code> so that only the mentioned number of lambda functions will be invoked.</p>
<p>Below is the high level diagram of how lambda scales in accordance with number of messages in the queue.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-23.png" alt="AWS Lambda with SQS" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://www.cloudtechsimplified.com/aws-lambda-sqs/">Image source</a></em></p>
<p>Note: There is some <a target="_blank" href="https://www.cloudtechsimplified.com/aws-lambda-sqs/#weird-behavior">weird behavior</a> if you try to throttle AWS Lambda when used with SQS standard queue. You can use a FIFO queue to solve that issue.</p>
<h2 id="heading-how-do-you-pass-environment-variables-to-aws-lambda">How do you pass environment variables to AWS Lambda?</h2>
<p><em>The interviewer might want to know how you pass sensitive information, for example.</em></p>
<p>There are different ways to pass environment variables to AWS Lambda, and it depends on the type of value is getting passed.</p>
<p><strong>Non-sensitive data</strong>: If you want to pass any non-sensitive information, you can pass the values directly to your lambda function environment variables. But these values would be visible in the AWS console in the Lambda service.  </p>
<p>In the below code example, we're passing the name of the DynamoDB table directly as environment variable, as it is not sensitive data:</p>
<pre><code>   <span class="hljs-keyword">const</span> readDDBLambdaFn = <span class="hljs-keyword">new</span> NodejsFunction(<span class="hljs-built_in">this</span>, <span class="hljs-string">'readDDBLambdaFn'</span>, {
      <span class="hljs-attr">entry</span>: path.join(__dirname, <span class="hljs-string">'../src/lambdas'</span>, <span class="hljs-string">'read-ddb.ts'</span>),
      ...nodeJsFunctionProps,
      <span class="hljs-attr">functionName</span>: <span class="hljs-string">'readDDBLambdaFn'</span>,
      <span class="hljs-attr">environment</span>: {
        <span class="hljs-attr">tableName</span>: table.tableName,
      },
    });
</code></pre><p><strong>Sensitive data</strong>: If you want to pass sensitive data such as passwords and API keys, you can use either a Secret Manager or Parameter store. But, you need to make sure you provide necessary roles to Lambda for accessing and decrypting secrets from the respective services.</p>
<p>In the below code snippet, we're NOT passing the actual secret. Instead, we're just passing the ARN (Amazon Resource Name) of the secret.  </p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> rdsLambdaFn = <span class="hljs-keyword">new</span> NodejsFunction(<span class="hljs-built_in">this</span>, <span class="hljs-string">'rdsLambdaFn'</span>, {
      entry: path.join(__dirname, <span class="hljs-string">'../src/lambdas'</span>, <span class="hljs-string">'rds-lambda.ts'</span>),
      ...nodeJsFunctionProps,
      functionName: <span class="hljs-string">'rdsLambdaFn'</span>,
      environment: {
        DB_ENDPOINT_ADDRESS: dbInstance.dbInstanceEndpointAddress,
        DB_NAME: databaseName,
        DB_SECRET_ARN: dbInstance.secret?.secretFullArn || <span class="hljs-string">''</span>,
      },
      vpc,
      vpcSubnets: vpc.selectSubnets({
        subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
      }),
    });
</code></pre>
<p>Then, in lambda you can get the actual secret dynamically within the lambda function as shown below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">async</span> (event: <span class="hljs-built_in">any</span>, context: <span class="hljs-built_in">any</span>): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">any</span>&gt; =&gt; {
    <span class="hljs-keyword">const</span> host = process.env.DB_ENDPOINT_ADDRESS || <span class="hljs-string">''</span>;
    <span class="hljs-keyword">const</span> database = process.env.DB_NAME || <span class="hljs-string">''</span>;
    <span class="hljs-keyword">const</span> dbSecretArn = process.env.DB_SECRET_ARN || <span class="hljs-string">''</span>;
    <span class="hljs-keyword">const</span> secretManager = <span class="hljs-keyword">new</span> AWS.SecretsManager({
      region: <span class="hljs-string">'us-east-1'</span>,
    });
    <span class="hljs-keyword">const</span> secretParams: AWS.SecretsManager.GetSecretValueRequest = {
      SecretId: dbSecretArn,
    };
    <span class="hljs-keyword">const</span> dbSecret = <span class="hljs-keyword">await</span> secretManager.getSecretValue(secretParams).promise();
    <span class="hljs-keyword">const</span> secretString = dbSecret.SecretString || <span class="hljs-string">''</span>;

    <span class="hljs-keyword">const</span> { password } = <span class="hljs-built_in">JSON</span>.parse(secretString);

}
</code></pre>
<p>I've written a detailed tutorial <a target="_blank" href="https://www.cloudtechsimplified.com/environment-variables-secrets-database-password-aws-lambda/">here</a> on the same topic.</p>
<h2 id="heading-say-you-have-a-windows-dependent-executable-sometoolexe-you-can-upload-it-into-an-s3-bucket-can-you-execute-this-binary-with-some-parameters-using-aws-lambda">Say you have a Windows-dependent executable sometool.exe. You can upload it into an S3 bucket. Can you execute this binary with some parameters using AWS Lambda?</h2>
<p><em>This is more of a question to make sure you understand the AWS Lambda execution environment – specifically the operating system it uses.</em></p>
<p>No, you would not be able to do that as AWS Lambda uses Linux as its operating system. Linux would not be able to execute a Windows-dependent binary.</p>
<h2 id="heading-how-do-you-re-use-code-across-aws-lambda-functions">How do you re-use code across AWS Lambda functions?</h2>
<p>There are 2 ways to re-use code across many AWS Lambda functions:</p>
<ul>
<li>Use Lambda Layers: You can store your code or logic in lambda layers, which you can re-use across different lambda functions. </li>
</ul>
<p>Below is some high level code for creating and consuming lambda layers using <code>aws cdk</code>:</p>
<pre><code>    <span class="hljs-keyword">const</span> logicLayer = <span class="hljs-keyword">new</span> lambda.LayerVersion(<span class="hljs-built_in">this</span>, <span class="hljs-string">'logic-layer'</span>, {
      <span class="hljs-attr">compatibleRuntimes</span>: [
        lambda.Runtime.NODEJS_14_X,
        lambda.Runtime.NODEJS_16_X,
      ],
      <span class="hljs-attr">layerVersionName</span>: <span class="hljs-string">'business-logic-layer'</span>,
      <span class="hljs-attr">code</span>: lambda.Code.fromAsset(<span class="hljs-string">'src/layers/business-logic'</span>),
      <span class="hljs-attr">description</span>: <span class="hljs-string">'Business logic layer'</span>,
    });


    <span class="hljs-keyword">const</span> lambdaWithLayer = <span class="hljs-keyword">new</span> NodejsFunction(<span class="hljs-built_in">this</span>, <span class="hljs-string">'lambdaWithLayer'</span>, {
      <span class="hljs-attr">entry</span>: path.join(__dirname, <span class="hljs-string">'../src/lambdas'</span>, <span class="hljs-string">'lambda.ts'</span>),
      ...nodeJsFnProps,
      <span class="hljs-attr">functionName</span>: <span class="hljs-string">'lambdaWithLayer'</span>,
      <span class="hljs-attr">handler</span>: <span class="hljs-string">'handler'</span>,
      <span class="hljs-attr">layers</span>: [logicLayer, utilsLayer],
    });
</code></pre><ul>
<li>Use <code>monorepo</code>: You can use mono repo and dynamically build packages at deploy time.</li>
</ul>
<h2 id="heading-what-happens-to-your-lambda-functions-if-you-delete-a-lambda-layer">What happens to your lambda functions if you delete a Lambda Layer?</h2>
<p><em>In this question, the interviewer wants to see how well you understand lambda layers.</em> </p>
<p>Existing lambda functions which use that deleted layer will continue to work – as lambda layers are merged with lambda functions at deployment time. </p>
<p>But you can't create a new lambda function using that deleted lambda layer.</p>
<p>You can learn more about Lambda layers <a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html">here</a> and I've written a guide on the same topic <a target="_blank" href="https://www.cloudtechsimplified.com/aws-lambda-layers/">here</a>.</p>
<h2 id="heading-can-you-increase-the-size-of-a-deployment-package-if-you-use-lambda-layers">Can you increase the size of a deployment package if you use Lambda Layers?</h2>
<p>No, you can't increase the size of the deployment package if you use Lambda Layers. The maximum deployment size of 50 MB zipped includes both the size of the lambda function and its associated lambda layers.</p>
<p>If you have a large codebase and want to increase the deployment, you can run containers in AWS Lambda.</p>
<h2 id="heading-can-i-take-my-existing-dockerized-web-application-and-run-it-using-lambda">Can I take my existing dockerized web application and run it using Lambda?</h2>
<p>Nope. You can't take any Express, Springboot, or .NET Core application (or any other application for that matter) as they are and put them inside lambda. </p>
<p>But that being said, there are a few libraries which allow you to put applications that use these web frameworks into AWS Lambda. Internally, those libraries convert those web applications into AWS lambda-compatible APIs. You can see one such example <a target="_blank" href="https://aws.amazon.com/blogs/aws/running-express-applications-on-aws-lambda-and-amazon-api-gateway/">here</a>.</p>
<p>By using these frameworks, the size of your lambda functions will be bigger and will result in longer start up times.</p>
<p>Remember that even when using containers with Lambda, the existing runtime API of Lambda remains the same. Lambda will still:</p>
<ul>
<li>be a single function </li>
<li>be invoked by an event or manually</li>
<li>have a timeout of 15 minutes.</li>
</ul>
<p>As you can see in the below code, there will be no change to the lambda API. The advantage of using Docker is that you would be able to use large packages without worrying about size.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Context, APIGatewayProxyResult, APIGatewayEvent } <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-lambda'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">async</span> (
  event: APIGatewayEvent,
  context: Context
): <span class="hljs-built_in">Promise</span>&lt;APIGatewayProxyResult&gt; =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Event: <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(event, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>)}</span>`</span>);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Context: <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(context, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>)}</span>`</span>);
  <span class="hljs-keyword">return</span> {
    statusCode: <span class="hljs-number">200</span>,
    body: <span class="hljs-built_in">JSON</span>.stringify({
      message: <span class="hljs-string">'Running this handler from docker'</span>,
    }),
  };
};
</code></pre>
<p>And, this is how you use it:</p>
<pre><code> <span class="hljs-keyword">const</span> repo = ecr.Repository.fromRepositoryName(
      <span class="hljs-built_in">this</span>,
      <span class="hljs-string">'dockerLambda'</span>,
      <span class="hljs-string">'docker-lambda'</span>
    );

    <span class="hljs-keyword">const</span> dockerLambda = <span class="hljs-keyword">new</span> lambda.DockerImageFunction(
      <span class="hljs-built_in">this</span>,
      <span class="hljs-string">'DockerLambdaFunction'</span>,
      {
        <span class="hljs-attr">code</span>: lambda.DockerImageCode.fromEcr(repo),
      }
    );
</code></pre><p>I wrote a step-by-step tutorial on running Docker containers for your application in <code>aws lambda</code> <a target="_blank" href="https://www.cloudtechsimplified.com/run-docker-containers-images-from-ecr-in-aws-lambda-along-with-cicd/">here</a>.</p>
<h2 id="heading-how-do-you-share-large-files-between-lambda-functions">How do you share large files between lambda functions?</h2>
<p>You can use the Elastic File System (EFS) to share large files between different functions.  </p>
<p>You can create an <code>access point</code> in the created EFS with the appropriate permissions and use that <code>access point</code> in your <code>mount path</code> in your lambda. </p>
<p>Any files written on that mount path will be accessible to all other lambda functions provided they have the mount path with appropriate permissions.</p>
<p>Below is the high level logical diagram on how to use AWS Lambda with Elastic File System (EFS):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-24.png" alt="Using EFS with Lambda" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://www.cloudtechsimplified.com/elastic-file-system-efs-aws-lambda/">Image source</a></em></p>
<p>You can read about this <a target="_blank" href="https://aws.amazon.com/blogs/compute/using-amazon-efs-for-aws-lambda-in-your-serverless-applications/">here</a> (bit old). I wrote a more recent practical step-by-step guide <a target="_blank" href="https://www.cloudtechsimplified.com/elastic-file-system-efs-aws-lambda/">here</a> on EFS with Lambda functions.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>I hope this article helped you to prepare for interviews involving AWS Lambda.</p>
<p>Thanks for reading to this point. I write about <code>aws</code> and serverless technologies at <a target="_blank" href="https://www.cloudtechsimplified.com/">https://www.cloudtechsimplified.com</a>. If you're interested, you can <a target="_blank" href="https://www.cloudtechsimplified.com/">subscribe</a> to my blog.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Connect to AWS RDS from AWS Lambda ]]>
                </title>
                <description>
                    <![CDATA[ By Mugilan Ragupathi In this article, we're going to learn about how to communicate with AWS RDS from AWS Lambda.  In this tutorial, we'll be using AWS CDK. It's an open source software development framework that lets you define cloud infrastructure.... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/aws-lambda-rds/</link>
                <guid isPermaLink="false">66d46042787a2a3b05af43ea</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 09 Nov 2022 23:22:38 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/AWS-Lambda-RDS-Proxy-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mugilan Ragupathi</p>
<p>In this article, we're going to learn about how to communicate with AWS RDS from AWS Lambda. </p>
<p>In this tutorial, we'll be using <code>AWS CDK</code>. It's an open source software development framework that lets you define cloud infrastructure. </p>
<p><code>AWS CDK</code> supports many languages including TypeScript, Python, C#, Java and others. We're going to use TypeScript in this tutorial. </p>
<p>When deploying (using the <code>cdk deploy</code> command), your code is converted to Cloudformation templates, and all the corresponding AWS resources are created. Only basic knowledge of CDK and TypeScript is required for trying this tutorial. Of course, you need to have an AWS account to create AWS resources.</p>
<p>You can learn more about AWS CDK from the docs <a target="_blank" href="https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html">here</a>, and I wrote a beginner's guide to it on my blog <a target="_blank" href="https://www.cloudtechsimplified.com/the-beginners-guide-to-aws-cdk/">here</a>.</p>
<h2 id="heading-introduction-to-aws-lambda-and-rds">Introduction to AWS Lambda and RDS</h2>
<p>AWS Lambda is a serverless, event-driven compute service which lets you run your code without having to provision servers.</p>
<p>AWS RDS is a managed relational database service from AWS and supports various RDMBS such as MySQL, Postgres, Oracle, SQL Server and so on. AWS takes care of setting up, patching, and maintaining these database servers.</p>
<h3 id="heading-why-would-you-use-rds-with-lambda">Why would you use RDS with Lambda?</h3>
<p>AWS Lambda is just a compute service and it does not have any recommendation about data stores. In fact, some of your lambda functions will not even interact with data stores of any kind. Even if you want to use a data store, you could use any type of database based on your needs.</p>
<p>However, most of the serverless architectures use DynamoDB as a data store just to reduce costs and eliminate the need to maintain database servers.   </p>
<p>DynamoDB is great and has its use cases. But using DynamoDB for all projects involving lambda is not possible for the following reasons:</p>
<p><strong>Dynamic access patterns:</strong> If you're using DynamoDB, you would have to design your querying patterns in advance. This isn't always possible, as your product (and its associated requirements) might evolve based on customer feedback. </p>
<p><strong>Limited access patterns:</strong> DynamoDB does not provide flexibility in writing your queries. You can't do <code>group by</code> functionality just as you do in RDBMS. You need to export the data and have some other system to provide that functionality.</p>
<p><strong>Existing database:</strong> If you have an existing RDBMS database, you wouldn't want to migrate to DynamoDB unless there is a compelling reason to do so. Even if you want to use DynamoDB, you might have to re-write your entire data access layer to use DynamoDB instead of a regular RDBMS. </p>
<h3 id="heading-pros-for-using-rdbms">Pros for using RDBMS:</h3>
<p><strong>Relationship between entities:</strong> RDBMS allows relationships between entities. You can define foreign keys to restrict any invalid data from getting stored in the database.</p>
<p><strong>Access patterns:</strong> RDMBS allows you to use dynamic access patterns. A new entity can be brought in without much change to any of the existing models. And, it has many functionalities such as <code>group by</code> – so that you don't need to have any external system to do such functionalities.</p>
<p><strong>Familiarity with SQL:</strong> Most developers are familiar with SQL to query databases, and you have a wide range of databases to choose from, including Oracle, Postgres, and MySQL.</p>
<p>You might choose RDMBS if you have following requirements:</p>
<ul>
<li>You have an existing RDBMS database and you'd like to adapt serverless compute provided by AWS Lambda</li>
<li>You have dynamic access patterns and you don't want to change much of your existing models</li>
</ul>
<p>With that out of our way, let's discuss how we're going to connect to RDS from Lambda.</p>
<h2 id="heading-project-architecture">Project Architecture</h2>
<p>In almost all cases, your RDBMS database will be in a private subnet of the Virtual Private Cloud (VPC) so that no one from outside of the VPC can access it. As your lambda function will contain business logic, this lambda might also be in a private subnet.  </p>
<p>We're going to use Postgres as our database in this tutorial. But the process here is applicable for any database (MySQL, Oracle, MS SQL and so on) that you want to use. The architecture will remain the same.</p>
<p>Below is the architecture for this project:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/AWS-Lambda-RDS-Latest.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We're going to use <code>AWS CDK</code> as an Infrastructure as Code (IaC) tool to create the AWS resources</p>
<h3 id="heading-how-to-create-a-virtual-private-cloud-to-host-our-lambda-and-rdbms">How to Create a Virtual Private Cloud to Host our Lambda and RDBMS</h3>
<p>We're going to create 2 subnets – a private subnet and a public subnet. In the private subnet, we'll have our Postgres database.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> vpc = <span class="hljs-keyword">new</span> ec2.Vpc(<span class="hljs-built_in">this</span>, <span class="hljs-string">'VpcLambda'</span>, {
      maxAzs: <span class="hljs-number">2</span>,
      subnetConfiguration: [
        {
          cidrMask: <span class="hljs-number">24</span>,
          name: <span class="hljs-string">'privatelambda'</span>,
          subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
        },
        {
          cidrMask: <span class="hljs-number">24</span>,
          name: <span class="hljs-string">'public'</span>,
          subnetType: ec2.SubnetType.PUBLIC,
        },
      ],
    });
</code></pre>
<p>When you create a subnet of type <code>PRIVATE_WITH_EGRESS</code> in AWS CDK, it will also create a NAT Gateway and will place that NAT Gateway in the public subnet. </p>
<p>The purpose of the NAT Gateway is to allow only the outbound connections from your private subnet to the internet. No one can initiate connections to your private subnet from the public internet.</p>
<h3 id="heading-why-are-we-using-nat-gateway-for-internet-connectivity">Why Are We Using NAT Gateway for Internet Connectivity?</h3>
<p>You may be wondering why you need an internet connection, as we have both lambda and the RDS database in the same private subnet.</p>
<p><code>Secrets Manager</code> is a service from AWS for storing and managing secrets such as database passwords, certificates, and so on. The password for connecting to the database is stored in <code>secrets manager</code> which is accessible by public endpoint.   </p>
<p>Either you can use <code>NAT Gateway</code> to access the public endpoint of the <code>secrets manager</code> service or you can create an interface endpoint to connect to <code>secrets manager</code> using AWS Network without going to the public internet.</p>
<p>Both will cost money, but NAT Gateway can be re-used for making internet connections from the lambda too (say if you're calling any public external API) whereas in an interface endpoint you would not be able to do that.</p>
<h3 id="heading-how-to-create-an-rds-database-instance-to-store-our-data">How to Create an RDS Database Instance to Store Our Data:</h3>
<p>We're going to use a <code>small</code> instance type for the database – just for the sake of this tutorial. But in production environments, you'll likely be using instances of larger sizes. </p>
<p>We're creating a new security group for the database so that we can control who can access the database instance and through which port.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> dbSecurityGroup = <span class="hljs-keyword">new</span> ec2.SecurityGroup(<span class="hljs-built_in">this</span>, <span class="hljs-string">'DbSecurityGroup'</span>, {
      vpc,
    });

    <span class="hljs-keyword">const</span> databaseName = <span class="hljs-string">'cloudtechsimplified'</span>;

    <span class="hljs-keyword">const</span> dbInstance = <span class="hljs-keyword">new</span> rds.DatabaseInstance(<span class="hljs-built_in">this</span>, <span class="hljs-string">'Instance'</span>, {
      engine: rds.DatabaseInstanceEngine.postgres({
        version: rds.PostgresEngineVersion.VER_13,
      }),
      <span class="hljs-comment">// optional, defaults to m5.large</span>
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.BURSTABLE3,
        ec2.InstanceSize.SMALL
      ),
      vpc,
      vpcSubnets: vpc.selectSubnets({
        subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
      }),
      databaseName,
      securityGroups: [dbSecurityGroup],
      credentials: rds.Credentials.fromGeneratedSecret(<span class="hljs-string">'postgres'</span>),
      maxAllocatedStorage: <span class="hljs-number">200</span>,
    });
</code></pre>
<p>The above <code>CDK</code> code will create a database instance and place it in the private subnet (as per the subnet selection that we've made).  </p>
<p>The method <code>fromGeneratedSecret</code> will create the secret in the secret manager service with the user name passed as a parameter. We want the database username to be <code>postgres</code>, so we're passing that value.  </p>
<p>And finally, we're allocating 200GB of storage space for the database.</p>
<h3 id="heading-how-to-configure-lambda-function-properties">How to Configure Lambda Function Properties</h3>
<p>We're using Node16 for writing our lambda function, and below are the generic properties for the lambda. </p>
<p>We want timeout to be 3 minutes instead of the default 3 seconds, and we want to allocate 256 MB for the lambda function. </p>
<p>As <code>aws-sdk</code> is provided by the lambda runtime itself, we want to exclude the <code>aws-sdk</code> library while bundling the lambda. </p>
<p>We've installed the <code>pg</code> npm package for communicating with the Postgres database and we're excluding the <code>pg-native</code> package as we don't need it.</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> nodeJsFunctionProps: NodejsFunctionProps = {
      bundling: {
        externalModules: [
          <span class="hljs-string">'aws-sdk'</span>, <span class="hljs-comment">// Use the 'aws-sdk' available in the Lambda runtime</span>
          <span class="hljs-string">'pg-native'</span>,
        ],
      },
      runtime: Runtime.NODEJS_16_X,
      timeout: Duration.minutes(<span class="hljs-number">3</span>), <span class="hljs-comment">// Default is 3 seconds</span>
      memorySize: <span class="hljs-number">256</span>,
    };
</code></pre>
<p>Next, we'll create a security group for the lambda function. Our lambda function should have information about the endpoint, user name, and password of the database so that lambda can connect to the database. </p>
<p>We're going to pass these values as environment variables to the lambda function.</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> lambdaSG = <span class="hljs-keyword">new</span> ec2.SecurityGroup(<span class="hljs-built_in">this</span>, <span class="hljs-string">'LambdaSG'</span>, {
      vpc,
    });

    <span class="hljs-keyword">const</span> rdsLambdaFn = <span class="hljs-keyword">new</span> NodejsFunction(<span class="hljs-built_in">this</span>, <span class="hljs-string">'rdsLambdaFn'</span>, {
      entry: path.join(__dirname, <span class="hljs-string">'../src/lambdas'</span>, <span class="hljs-string">'rds-lambda.ts'</span>),
      ...nodeJsFunctionProps,
      functionName: <span class="hljs-string">'rdsLambdaFn'</span>,
      environment: {
        DB_ENDPOINT_ADDRESS: dbInstance.dbInstanceEndpointAddress,
        DB_NAME: databaseName,
        DB_SECRET_ARN: dbInstance.secret?.secretFullArn || <span class="hljs-string">''</span>,
      },
      vpc,
      vpcSubnets: vpc.selectSubnets({
        subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
      }),
      securityGroups: [lambdaSG],
    });
</code></pre>
<p><strong>Important note:</strong> We're not passing the database password as an environment variable to the lambda. Instead, we're passing the <code>ARN</code>(Amazon Resource Name) to the lambda and we'll be fetching the actual password (dynamically at runtime ) from the secret manager within lambda for better security.</p>
<h3 id="heading-permissions-for-lambda-to-access-the-database-password">Permissions for Lambda to Access the Database Password</h3>
<p>Even though we pass the <code>secret arn</code> to lambda as an environment variable, lambda should have the necessary permissions to fetch the secret (database password, in our case) from the <code>secrets manager</code> service. </p>
<p>The below line of code provides those permissions:</p>
<pre><code class="lang-typescript">dbInstance.secret?.grantRead(rdsLambdaFn);
</code></pre>
<p>The above <code>cdk</code> line will create a role for the lambda with 2 permissions <code>DescribeSecret</code> and <code>GetSecretValue</code> of the secrets manager so that our lambda will have permissions to get the secret value (database password, in our case) before talking to the database.  </p>
<p>You can see the same in the AWS console in Lambda service.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-23.png" alt="AWS Lambda Permissions for Secrets Manager" width="600" height="400" loading="lazy">
<em>AWS Lambda Permissions for Secrets Manager</em></p>
<h3 id="heading-security-group-for-rds-database-instance">Security Group for RDS Database Instance</h3>
<p>We don't want to allow the connection to the database to be open for all and we want the database connections to be allowed from lambda. </p>
<p>The below <code>cdk</code> code adds an ingress rule for allowing connectivity from our lambda function to the RDS instance through the port <code>5432</code> (port for the Postgres database):</p>
<pre><code class="lang-typescript">  dbSecurityGroup.addIngressRule(
      lambdaSG,
      ec2.Port.tcp(<span class="hljs-number">5432</span>),
      <span class="hljs-string">'Lambda to Postgres database'</span>
    );
</code></pre>
<h3 id="heading-lambda-function-code-to-communicate-with-the-database">Lambda Function Code to Communicate with the Database</h3>
<p>The actual lambda function code where it talks to the database is pretty simple. As we're using a Postgres database, we're using the <code>pg</code> package to communicate with Postgres from the <code>nodejs</code> environment.  </p>
<p>Before initiating the connection to the database, we're fetching the secret string from the <code>secrets manager</code> service. This secret string is a JSON string which contains both username and password. Just parse the JSON string and take only the password.</p>
<pre><code><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> AWS <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-sdk'</span>;
<span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">'pg'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">async</span> (event: any, <span class="hljs-attr">context</span>: any): <span class="hljs-built_in">Promise</span>&lt;any&gt; =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> host = process.env.DB_ENDPOINT_ADDRESS || <span class="hljs-string">''</span>;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`host:<span class="hljs-subst">${host}</span>`</span>);
    <span class="hljs-keyword">const</span> database = process.env.DB_NAME || <span class="hljs-string">''</span>;
    <span class="hljs-keyword">const</span> dbSecretArn = process.env.DB_SECRET_ARN || <span class="hljs-string">''</span>;
    <span class="hljs-keyword">const</span> secretManager = <span class="hljs-keyword">new</span> AWS.SecretsManager({
      <span class="hljs-attr">region</span>: <span class="hljs-string">'us-east-1'</span>,
    });
    <span class="hljs-keyword">const</span> secretParams: AWS.SecretsManager.GetSecretValueRequest = {
      <span class="hljs-attr">SecretId</span>: dbSecretArn,
    };
    <span class="hljs-keyword">const</span> dbSecret = <span class="hljs-keyword">await</span> secretManager.getSecretValue(secretParams).promise();
    <span class="hljs-keyword">const</span> secretString = dbSecret.SecretString || <span class="hljs-string">''</span>;

    <span class="hljs-keyword">if</span> (!secretString) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'secret string is empty'</span>);
    }

    <span class="hljs-keyword">const</span> { password } = <span class="hljs-built_in">JSON</span>.parse(secretString);

    <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
      <span class="hljs-attr">user</span>: <span class="hljs-string">'postgres'</span>,
      host,
      database,
      password,
      <span class="hljs-attr">port</span>: <span class="hljs-number">5432</span>,
    });
    <span class="hljs-keyword">await</span> client.connect();
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'SELECT $1::text as message'</span>, [
      <span class="hljs-string">'Hello world!'</span>,
    ]);
    <span class="hljs-built_in">console</span>.log(res.rows[<span class="hljs-number">0</span>].message); <span class="hljs-comment">// Hello world!</span>
    <span class="hljs-keyword">await</span> client.end();
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error while trying to connect to db'</span>);
  }
};
</code></pre><p>And, finally we're running a simple select query in our database.</p>
<h3 id="heading-how-to-test-the-project">How to Test the Project</h3>
<p>Now, you can log-in to your AWS console for testing. Select the <code>Lambda</code> service and select your lambda function – in our case, it would be <code>rdsLambdaFn</code> . </p>
<p>You don't need to worry about the <code>event</code> property of lambda for this tutorial, as we're not using it in our lambda function code. Click the Test button, and you'll be able to see the logs.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Lambda-perf-without-proxy.png" alt="Test Lambda function" width="600" height="400" loading="lazy">
<em>Test Lambda function</em></p>
<h3 id="heading-the-performance-problem">The Performance Problem</h3>
<p>Lambda is well-suited for functions which don't take much time. In fact, the maximum timeout limit of a lambda function is 15 minutes. </p>
<p>As you can see from the lambda code, we're initiating the connection to the database every time the lambda function is invoked. </p>
<p>Depending on the event source for the lambda (SQS queue, for instance), this would create connections at a higher rate and would disconnect at the end of the lambda function.  </p>
<p>This increases the load on the RDS server significantly which in turn reduces the performance. So how do we fix this problem?</p>
<h2 id="heading-how-to-use-rds-proxy">How to Use RDS Proxy</h2>
<p>Instead of directly creating connections from lambda to the database, we can have an RDS Proxy sit in between the lambda and the RDS database. </p>
<p>The purpose of RDS proxy is to maintain pool of connections so that any consumer can connect to proxy and can get the database connection. Note that we're NOT creating a connection here – we're just getting connecting which were created already.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/AWS-RDS-Proxy-Logical-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Using RDS Proxy with Lambda</em></p>
<p>There are 2 advantages of following this approach:</p>
<ol>
<li><strong>Reduced load on the database server</strong>: As we don't need to create a connection for every lambda invocation in the database server, the load on the server is reduced significantly.</li>
<li><strong>Improved lambda performance</strong>: From lambda, we're just getting a connection from the RDS proxy and we're not creating a new connection. This increases performance of the lambda function.</li>
</ol>
<h3 id="heading-required-changes-to-use-the-rds-proxy">Required Changes to Use the RDS Proxy</h3>
<p>We don't need to make many changes either to our architecture or to our code. We just need to do a couple of things:</p>
<ul>
<li>Create the RDS proxy and associate the db security group that we've created earlier</li>
<li>Update the environment variable of the lambda endpoint so that lambda can connect to the RDS proxy instead of RDS directly</li>
</ul>
<p>You don't need to change your lambda code.</p>
<h3 id="heading-updated-architecture">Updated Architecture</h3>
<p>Below is the updated architecture diagram</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/AWS-Lambda-RDS-Proxy.png" alt="RDS Proxy with Lambda - Architecture" width="600" height="400" loading="lazy">
<em>RDS Proxy with Lambda - Architecture</em></p>
<h3 id="heading-how-to-create-the-rds-proxy">How to Create the RDS Proxy</h3>
<p>We need to create the RDS Proxy and add the database instance as the proxy target.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> dbProxy = <span class="hljs-keyword">new</span> rds.DatabaseProxy(<span class="hljs-built_in">this</span>, <span class="hljs-string">'Proxy'</span>, {
      proxyTarget: rds.ProxyTarget.fromInstance(dbInstance),
      secrets: [dbInstance.secret!],
      securityGroups: [dbSecurityGroup],
      vpc,
      requireTLS: <span class="hljs-literal">false</span>,
      vpcSubnets: vpc.selectSubnets({
        subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
      }),
    });
</code></pre>
<p>Note that we're passing the database secret to this proxy too, as it is responsible for maintaining connections. We're using the same security group of the database as we did to open the port 5432 .</p>
<h3 id="heading-how-to-update-the-endpoint-for-lambda">How to Update the Endpoint for Lambda</h3>
<p>We don't need to change the lambda code. We just need to update the endpoint which is passed as the environment variable.</p>
<pre><code> environment: {
        <span class="hljs-attr">DB_ENDPOINT_ADDRESS</span>: dbProxy.endpoint,
        <span class="hljs-attr">DB_NAME</span>: databaseName,
        <span class="hljs-attr">DB_SECRET_ARN</span>: dbInstance.secret?.secretFullArn || <span class="hljs-string">''</span>,
      },
</code></pre><p>There will be no change to any other code.</p>
<h3 id="heading-performance-improvements">Performance Improvements</h3>
<p>When you test the lambda function, you can see that the lambda is getting connected to the proxy instead of the database instance (as we're printing endpoint information as <code>host</code>). </p>
<p>You should also notice that performance is improved significantly. Before, it took around 500ms to connect. Now, it is taking around 50 ms.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-22.png" alt="Performance of lambda with RDS Proxy" width="600" height="400" loading="lazy">
<em>Performance of lambda with RDS Proxy</em></p>
<p>Note that it may take additional time when you're getting the initial connection from RDS proxy. And, getting any further connections will be fast, as shown above.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope this tutorial helped you learn how to connect to RDS from AWS Lambda.</p>
<p>Thanks for reading to this point. I write about <a target="_blank" href="https://www.cloudtechsimplified.com/tag/aws-lambda/">aws lambda</a>, <a target="_blank" href="https://www.cloudtechsimplified.com/tag/fargate/">fargate,</a>  <a target="_blank" href="https://www.cloudtechsimplified.com/tag/ci-cd-pipeline/">ci/cd pipeline</a> and serverless technologies at <a target="_blank" href="https://www.cloudtechsimplified.com">https://www.cloudtechsimplified.com</a>. If you're interested, you can subscribe <a target="_blank" href="https://www.cloudtechsimplified.com/subscribe/">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Setup a Basic Serverless REST API with AWS Lambda and API Gateway ]]>
                </title>
                <description>
                    <![CDATA[ By Nyior Clement As developers, we are always trying to optimize everything from how people communicate to how people buy things. The goal is to make humans arguably more productive.  In the spirit of making humans more productive, the software devel... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-setup-a-basic-serverless-backend-with-aws-lambda-and-api-gateway/</link>
                <guid isPermaLink="false">66d46089d1ffc3d3eb89de32</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 14 Feb 2022 19:10:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/01/Serverless-computing-768x402-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Nyior Clement</p>
<p>As developers, we are always trying to optimize everything from how people communicate to how people buy things. The goal is to make humans arguably more productive. </p>
<p>In the spirit of making humans more productive, the software development landscape has seen a dramatic rise in the emergence of developer productivity tools.</p>
<p>This is especially true of the software infrastructure space. Innovators are creating solutions that allow developers to focus more on writing actual business logic and less on mundane deployment concerns.</p>
<p>The need to improve developer experience and cut down costs are some of the core drivers of serverless computing. But what is serverless computing? That's the crux of this guide. It's the one question that matters here, and we will get to it.</p>
<p>To help you walk through this guide, I've divided it into two parts: </p>
<ul>
<li>In part one, we are going to start off by learning what serverless is. </li>
<li>In part two, we will set up a rudimentary serverless REST API with AWS Lambda and API Gateway.</li>
</ul>
<p>Let's get started already, shall we?</p>
<h2 id="heading-what-is-serverless">What is Serverless?</h2>
<p>To understand what serverless is, first we need to understand what servers are and how they had evolved over time. Wait, servers?</p>
<p>Yes, when we build software systems, most times we build them for people. For that reason, we make these applications findable on the interwebs.</p>
<p>Making an application discoverable, ideally, entails uploading that application to a special computer that runs 24/7 and is super fast. This special computer is called a <strong>server.</strong></p>
<h3 id="heading-the-evolution-of-servers">The Evolution of Servers</h3>
<p>Two decades ago, when companies wanted to upload a piece of software they'd built to a server, they'd have to purchase a physical computer, configure the computer, and then deploy their application to that computer. </p>
<p>In cases where they needed to upload several applications, they'd have to get and setup multiple servers, too. Everything was done on-premise.</p>
<p>But it didn't take long for people to notice the many problems that came with approaching servers that way. </p>
<p>There is the problem of developer productivity: a developer's attention is divided between writing code and dealing with the infrastructure that serves the code. This issue could easily be addressed by getting other people to deal with the infrastructure issues – but this leads to a second problem:</p>
<p>The problem of cost. These people dealing with the infrastructure concerns would have to be paid, right? In fact, just having to purchase a server even for seemingly basic test applications is in itself a costly affair.</p>
<p>Furthermore, if we begin to factor in other elements like scaling a server's computing capability when there's a spike in traffic or simply just updating the server's OS and drivers over time...well, you'll begin to see how exhausting it is to keep an in-house server. People craved something better. There was a need.</p>
<p>Amazon responded to that need when it announced the release of Amazon Web Services (AWS) in 2006. AWS notoriously disrupted the software infrastructure domain. It was a revolutionary shift from traditional servers.</p>
<p>AWS took away the need for organizations to set up their in-house server. Instead, organizations and even individuals could just upload their applications to Amazon's computers over the internet for some fee. And this <em>server-as-a-service</em> model announced the start of cloud computing.</p>
<h2 id="heading-what-is-cloud-computing">What is Cloud Computing?</h2>
<p>Cloud Computing is fundamentally about storing files or executing code (sometimes both) on someone else's computer, usually, over a network.</p>
<p>Cloud computing platforms like AWS, Microsoft Azure, Google Cloud Platform, Heroku, and others exist to save people the stress of having to setup and maintain their own servers.</p>
<p>What is outstandingly unique about cloud computing is the fact that you could be in Nigeria, and rent a computer that's in the US. You can then access that computer you've rented and do stuff with it over the internet. Essentially, cloud vendors provide us with compute environments to upload and run our custom software.</p>
<p>The environment we get from these providers is something that has progressed, and now exists in different forms.</p>
<p>In cloud computing parlance, we use the term <strong>cloud computing models</strong> to refer to the different environments most cloud vendors offer. Each new model in the cloud computing scene is usually created to enhance developer productivity and shrink infrastructure and labor costs.</p>
<p>For example, when AWS was first launched, it had the Elastic Compute Cloud (EC2) service. The EC2 is structurally a bare-bones machine. People that pay for EC2 service would have to do a lot of configuration like installing an OS, a database, and maintaining these things for as long as they own the service. </p>
<p>While EC2 offers lots of flexibility (for example, you can install whatever OS you want), it also requires lots of effort to work with. EC2 and other services like it across other cloud providers fall under the cloud computing model called <strong>Infrastructure as a Service (IaaS).</strong></p>
<p>Not having to purchase a physical machine and set it up on-premise makes the IaaS model quite superior to the on-premise server model. But still, the fact that owners have to configure and maintain lots of things makes the IaaS model makes it a not so easy model to work with. </p>
<p>The somewhat labor intensive requirements (from the developer's/customer's perspective) of the IaaS model eventually also became a source of concern. To address that, the next cloud computing model, <strong>Platform as a Service (PaaS),</strong> was born.</p>
<p>The major problem with IaaS is having to configure and maintain a lot of things. For example, OS installation, patch upgrades, discovery, and so on. </p>
<p>In the PaaS model, all that is abstracted. A machine in the PaaS model comes pre-installed with an OS, and patch upgrades on the machine amongst others are the vendor's responsibility. </p>
<p>In the PaaS model, developers only deploy their applications, and the cloud providers handle some of the low level stuff. While this model is easier to work with, it also implies less flexibility. Elastic Beanstalk from AWS and Heroku are some of the examples of offerings in this model. </p>
<p>The PaaS model undeniably took away most of the dreary configuration and maintenance tasks. But beyond just the tasks that make our software available on the internet, people began to recognize some of the limitations of the PaaS model too. </p>
<p>For example, in both the IaaS and PaaS models, developers had to manually deal with scaling up/down a server's computing capability. Additionally, with both the IaaS and PaaS platforms, most vendors charge a flat fee (Think Heroku) for their services – it's not based on usage. </p>
<p>In situations where the fee is based on usage, it's usually not very precise. Lastly, in both the IaaS and PaaS models, applications are long-lived (always running even when there are no requests coming in). That in turn results in the inefficient use of server resources.</p>
<p>The above concerns stirred the next evolution of the cloud.</p>
<h3 id="heading-the-rise-of-serverless-computing">The Rise of Serverless Computing</h3>
<p>Just like IaaS and PaaS, Serverless is a cloud computing model. It is the most recent evolution of the cloud just after PaaS. Like IaaS and PaaS, with serverless, you don't have to buy physical computers. </p>
<p>Furthermore, just as it is in the PaaS model, you don't have to significantly configure and maintain servers. Additionally, the serverless model went a step further: it took away the need to manage long lived application instances, and to manually scale server resources up or down based on traffic. Payment is precisely based on usage, and security concerns are also abstracted. </p>
<p>In the serverless model, you no longer have to worry about anything infrastructure-related because the cloud providers handle all that. And this is exactly what the serverless model is all about: allowing developers and whole organizations to focus on the dynamic aspects of their project while leaving <em>all</em> the infrastructure concerns to the cloud provider.</p>
<p>Literally <em>all</em> the infrastructure concerns from setting up and maintaining the server to auto scaling server resources up from and down to zero and security concerns. And in fact one of the killer features of the serverless model is the fact that users are charged precisely based on the number of requests the software they deployed has handled. </p>
<p>So we can now say that serverless is the term we use to refer to any cloud solution that takes away all the infrastructure concerns we normally would have to worry about. For example, AWS Lambda, Azure functions, and others.</p>
<p>We also use the term serverless to describe applications that are designed to be deployed to or interact with a serverless environment. Hmmm, how so?</p>
<h3 id="heading-function-as-a-service-vs-backend-as-a-service">Function as a Service vs Backend as a Service</h3>
<p>All serverless solutions belong to one of two categories: </p>
<ul>
<li><strong>Function as a Service (FaaS)</strong> and </li>
<li><strong>Backend as a Service (BaaS).</strong></li>
</ul>
<p><strong>BaaS, FaaS –</strong> Nyior, this is getting quite complicated :-(</p>
<p>I know, but don't worry – you'll get it &lt;3</p>
<p>A cloud offering is considered a <strong>BaaS</strong> and by extension serverless if it replaces certain components of our application that we'd normally code or manage ourselves. </p>
<p>For example, when you use Google's Firebase authentication service or Amazon's Cognito service to handle user authentication in your project, then you've leveraged a <strong>BaaS</strong> offering.</p>
<p>A cloud offering is considered a <strong>FaaS</strong> and by extension serverless if it takes away the need to deploy our applications as single instances that are then run as processes within a host. Instead, we break down our application into granular functions (with each function ideally encapsulating the logic of a single operation). Each function is then deployed to the <strong>FaaS</strong> platform. </p>
<p>Way too abstract? Okay, see the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/app-deployment-methods.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>From Left-to-Right: Deploying an Application to a FaaS platform, Deploying an application to a non-FaaS platform. Source: Author</em></p>
<p>From the image above you can see that FaaS platforms offer an entirely different way of deploying applications. We've seen nothing like it before: there are no hosts and application processes. As a result, we don't have some code that's constantly running and listening for requests.</p>
<p>Instead, we have functions that only run when invoked, and they're being torn down as soon as they're done processing the task they were called upon to perform. </p>
<p>If these functions aren't always running and listening for requests, how then are they invoked, you might ask?</p>
<p>All FaaS platforms are event-driven. Essentially, every function we deploy is mapped to some event. And when that event occurs, the function is triggered.</p>
<p>Summarily, we use the term serverless to describe a Function as a Service or Backend as Service cloud solution where:</p>
<ol>
<li>We don't have to manage long lived application instances or hosts for applications we deploy.</li>
<li>We don't have to manually scale up/down computing resources depending on traffic because the server automatically does that for us.</li>
<li>The pricing is precisely based on usage</li>
</ol>
<p>Also, any application that's built on a significant number of BaaS solutions or designed to be deployed to a FaaS platform or even both can also be considered serverless.</p>
<p>Now that we're fully grounded in what serverless is, let's see how we can set up a minimal serverless REST API with AWS Lambda in tandem with AWS API Gateway.</p>
<h2 id="heading-serverless-example-project">Serverless Example Project</h2>
<p>Here, we will be setting up a minimal, perhaps uninteresting serverless REST API with AWS lambda and API Gateway. </p>
<p><strong>AWS Lambda, API Gateway –</strong> What are these things please?</p>
<p>They're serverless cloud solutions. Remember we stated that all serverless cloud solutions belong to one of two categories: BaaS and FaaS. AWS Lambda is a Function as a Service platform and API Gateway is a Backend as a Service solution.</p>
<p>How is API Gateway a Backend as a Service platform, you might ask?</p>
<p>Well, normally we implement routing in our applications ourselves. With API Gateway, we don't have to do that – instead, we cede the routing task to API Gateway.</p>
<p><strong>AWS Lambda, API Gateway, Our Application –</strong> How are all these connected?</p>
<p>The connection is simple. AWS Lambda is where we will be deploying our actual application code. But because AWS Lambda is a FaaS platform, we are going to break our application into granular functions, with each function handling a single operation. We'll then deploy each function to AWS Lambda.</p>
<p>Oh okay, but where does API Gateway come in?</p>
<p>AWS Lambda, like all other FaaS platforms, is event-driven. What that means is, when you deploy a function to the platform, that function only does something when some event it's tied to happens. </p>
<p>An event could be anything from an HTTP request to a file being uploaded to s3.</p>
<p>In our case, we will be deploying a minimal REST API backend. Because we are going serverless and more specifically the FaaS way, we are going to break down our REST backend into independent functions. Each function will be tied to some HTTP request. </p>
<p>Actually, we will only be writing one function.</p>
<p><em>API Gateway is the tool that we will use to tie a request to a function we've deployed.</em> So when that particular request comes in, the function is invoked.</p>
<p>Think of API Gateway as routing-as-a-service-tool :-). The image below depicts the relationship between the above entities.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Blank-diagram--3-.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>API Gateway + AWS Lambda. Source: Author</em></p>
<p>Ok, I get the connection. What's next?</p>
<h3 id="heading-lets-configure-stuff-on-aws-xd">Let's Configure Stuff on AWS xD</h3>
<p>The primary task here is to build a minimal paraphrasing tool. We are going to create a REST endpoint that accepts a POST request with some text as the payload. Our endpoint will then return a paraphrased version of that text as the response.</p>
<p>To achieve that, we will code a lambda function that does the actual paraphrasing of text blocks. We will then connect our function to the API gateway, just so whenever there is a POST request, our function will be triggered. </p>
<p>But first, there are certain things we need to configure. Follow the steps in the following sections to configure all that you'd need to complete the task in this part.</p>
<p>Note: Some of the steps in the subsequent sections were adapted directly from <a target="_blank" href="https://aws.amazon.com/getting-started/hands-on/build-serverless-web-app-lambda-apigateway-s3-dynamodb-cognito/">AWS' tutorial on serverless.</a></p>
<h3 id="heading-step-1-create-an-aws-account">Step 1: Create an AWS Account</h3>
<p>To create an account on AWS, follow the steps in module 1 of <a target="_blank" href="https://aws.amazon.com/getting-started/guides/setup-environment/">this guide.</a></p>
<h3 id="heading-step-2-code-our-lambda-function-on-aws">Step 2: Code our Lambda Function on AWS</h3>
<p>Remember, we are going to need a function that does the actual text paraphrasing, and this is where we do that. Follow the steps below to create the lambda function:</p>
<ol>
<li>Login to your AWS account using the credentials in step 1. In the search field, input 'lambda', and then select <strong>Lambda</strong> from the list of services displayed.</li>
<li>Click the <strong>create function</strong> button on the Lambda page.</li>
<li>Keep the default <strong>Author from scratch</strong> card selected.</li>
<li>Enter _paraphrase<em>text</em> in the <strong>Name</strong> field.</li>
<li>Select <strong>Python 3.9</strong> for the <strong>Runtime</strong>.</li>
<li>Leave all the other default settings as they are and click on <strong>create function</strong>.</li>
<li>Scroll down to the <strong>Function code</strong> section and replace the exiting code in the <strong>lamda_function.py</strong> code editor with the code below:</li>
</ol>
<pre><code class="lang-Python"><span class="hljs-keyword">import</span> http.client

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    <span class="hljs-comment"># TODO implement</span>
    conn = http.client.HTTPSConnection(<span class="hljs-string">"paraphrasing-tool1.p.rapidapi.com"</span>)

    payload = event[<span class="hljs-string">'body'</span>]
    headers = {
        <span class="hljs-string">'content-type'</span>: <span class="hljs-string">"application/json"</span>,
        <span class="hljs-string">'x-rapidapi-host'</span>: <span class="hljs-string">"paraphrasing-tool1.p.rapidapi.com"</span>,
        <span class="hljs-string">'x-rapidapi-key'</span>: <span class="hljs-string">"your api key here"</span>
    }

    conn.request(<span class="hljs-string">"POST"</span>, <span class="hljs-string">"/api/rewrite"</span>, payload, headers)
    res = conn.getresponse()
    data = res.read()

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">'body'</span>: data
    }
`
</code></pre>
<p>We are using <a target="_blank" href="https://rapidapi.com/healthytechguy/api/paraphrasing-tool1/">this API</a> for the paraphrasing functionality. Head over to that page, subscribe to the basic plan, and grab the API key (it's free).</p>
<h3 id="heading-step-3-test-our-lambda-function-on-aws">Step 3: Test our Lambda Function on AWS</h3>
<p>Here, we'll test our lambda function with a sample input to see that it produces the expected behavior: paraphrasing whatever text it is passed. </p>
<p>To test your just created lambda function, follow the following steps:</p>
<ol>
<li>From the main edit screen for your function, select <strong>Configure test event</strong> from the <strong>Test</strong> dropdown.</li>
<li>Keep <strong>Create new test event</strong> selected.</li>
<li>Enter <em>TestRequestEvent</em> in the <strong>Event name</strong> field</li>
<li>Copy and paste the following test event into the editor:</li>
</ol>
<pre><code class="lang-Python">{
  <span class="hljs-string">"path"</span>: <span class="hljs-string">"/paraphrase"</span>,
  <span class="hljs-string">"httpMethod"</span>: <span class="hljs-string">"POST"</span>,
  <span class="hljs-string">"headers"</span>: {
    <span class="hljs-string">"Accept"</span>: <span class="hljs-string">"*/*"</span>,
    <span class="hljs-string">"content-type"</span>: <span class="hljs-string">"application/json; charset=UTF-8"</span>
  },
  <span class="hljs-string">"queryStringParameters"</span>: null,
  <span class="hljs-string">"pathParameters"</span>: null,
  <span class="hljs-string">"body"</span>: <span class="hljs-string">"{\r\"sourceText\": \"The bone of contention right now is how to make plenty money. \"\r\r\n    }"</span>
}
</code></pre>
<p>You can replace the body text with whatever content you want. Once you're done pasting the above code, proceed with the following steps:</p>
<ol>
<li>Click <strong>create.</strong></li>
<li>On the main function edit screen, click <strong>Test</strong> with <em>TestRequestEvent</em> selected in the dropdown.</li>
<li>Scroll to the top of the page and expand the <strong>Details</strong> section of the <strong>Execution result</strong> section.</li>
<li>Verify that the execution succeeded and that the function result looks like the following:</li>
</ol>
<pre><code class="lang-Python">Response
{
  <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">200</span>,
  <span class="hljs-string">"body"</span>: <span class="hljs-string">"{\"newText\":\"The stumbling block right now is how to make big bucks.\"}"</span>
}
</code></pre>
<p>As seen above, the original text we passed our lambda function has been paraphrased.</p>
<h3 id="heading-step-3-exposing-our-lambda-function-via-the-api-gateway">Step 3: Exposing our Lambda Function Via the API Gateway.</h3>
<p>Now that we've coded our lambda function and it works, here, we'll expose the function through a REST endpoint that accepts a POST request. Once a request is sent to that endpoint, our lambda function will be called.</p>
<p>Follow the steps below to expose your lambda function via the API Gateway:</p>
<h4 id="heading-create-the-api">Create the API</h4>
<ol>
<li>In the search field, search and select <strong>API Gateway</strong></li>
<li>On the API Gateway page, there are four cards under the <strong>choose an API type</strong> heading. Go to the <strong>REST API</strong> card and click <strong>build.</strong></li>
<li>Next, provide all the required information as shown in the image below and click <strong>Create API.</strong></li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/api-gateway.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>For endpoint type, select Edge optimized</em></p>
<h4 id="heading-create-the-resource-and-method">Create the Resource and Method</h4>
<p><strong>Resource, Method?</strong> In the steps above we created an API. But an API usually has endpoint(s). An endpoint usually specifies a path and the HTTP method it supports. For example GET /get-user. Here, we call the path resource, and the HTTP verb tied to a path a method. Thus, resource + method = REST endpoint.</p>
<p>Here, we are going to create one REST endpoint that will allow users pass a text block to be paraphrased to our lambda function. Follow the steps below to accomplish that:</p>
<ul>
<li>First from the <strong>Actions</strong> dropdown select <strong>Create Resource.</strong> Next, fill the input fields and tick the check-box as shown in the image below and click <strong>Create Resource.</strong></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/resource.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>You could replace resource name and resource path with anything you see fit. Notice how we've also enable CORS.</em></p>
<ul>
<li>With the newly created <em>/paraphrase</em> resource selected, from the <strong>Action</strong> dropdown select <strong>Create Method</strong>. </li>
<li>Select <em>POST</em> from the new dropdown that appears, then <strong>click the checkmark</strong>.</li>
<li>Provide all the other info shown in the image below and click <strong>save.</strong></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/method.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>lambda function is the name of the function we create in one of the previous steps.</em></p>
<h4 id="heading-deploy-the-api">Deploy the API</h4>
<ul>
<li>In the <strong>Actions</strong> drop-down list, select <strong>Deploy API</strong>.</li>
<li>Select <strong>[New Stage]</strong> in the <strong>Deployment stage</strong> drop-down list.</li>
<li>Enter <em>production</em> or whatever you wish for the <strong>Stage Name</strong>.</li>
<li>Choose <strong>Deploy</strong>.</li>
<li>Note the invoke URL is your API's base URL. It should look something like this: <a target="_blank" href="https://wrl34unbe0.execute-api.eu-central-1.amazonaws.com/production">https://wrl34unbe0.execute-api.eu-central-1.amazonaws.com/</a>{stage name}</li>
<li>To test your endpoint, you can use postman or curl. Append the path to your endpoint to the end of the invoke URL like so: <a target="_blank" href="https://wrl34unbe0.execute-api.eu-central-1.amazonaws.com/production">https://wrl34unbe0.execute-api.eu-central-1.amazonaws.com/</a>{stage name}/paraphrase. And of course the request method should be POST.</li>
<li>When testing, also add the expected payload to the request like so:</li>
</ul>
<pre><code class="lang-Python">{
    <span class="hljs-string">"sourceText"</span>: <span class="hljs-string">"The bone of contention right now is how to make plenty money.   
}</span>
</code></pre>
<h2 id="heading-wrapping-things-up">Wrapping Things up</h2>
<p>Well that's it. First we learned all about the term serverless and then we went on to set up a light weight serverless REST API with AWS Lambda and API Gateway.</p>
<p>Serverless doesn't imply the total absence of servers, though. It is essentially about having a deployment flow where you don't have to worry about servers. The servers are still present, but they are being taken care of by the cloud provider.</p>
<p>We are going serverless each time if we build a significant components of our application on top of BaaS technologies or whenever we structure our application to be compatible with any FaaS platform (or when we do both).</p>
<p>Thank you for reading to this point. Want to connect? You can find me on <a target="_blank" href="https://twitter.com/nyior_clement">Twitter</a>, <a target="_blank" href="https://www.linkedin.com/in/nyior/">LinkedIn</a>, or <a target="_blank" href="https://github.com/Nyior">GitHub.</a></p>
<h3 id="heading-references">References</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://learning.oreilly.com/library/view/what-is-serverless/9781491984178/">https://learning.oreilly.com/library/view/what-is-serverless/9781491984178/</a></div>
<p>Cover Image: <a target="_blank" href="https://www.hestabit.com/blog/serverless-architecture-explained/">hestabit.com</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Become a Serverless Developer ]]>
                </title>
                <description>
                    <![CDATA[ By Sam Williams Serverless Development has been around since the release of AWS Lambda in 2014, but in the last few years things have exploded. Startups and smart tech companies have started taking advantage of the scalability, reliability, and power... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/become-a-serverless-developer/</link>
                <guid isPermaLink="false">66d460cbbd438296f45cd3aa</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 19 Jan 2022 20:57:22 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/01/sls_talks_3_B.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sam Williams</p>
<p>Serverless Development has been around since the release of AWS Lambda in 2014, but in the last few years things have exploded.</p>
<p>Startups and smart tech companies have started taking advantage of the scalability, reliability, and power of serverless to rapidly grow – and now they need more serverless developers than ever.</p>
<p>Being a "Serverless Developer" means you build solutions with managed services from the likes of AWS, Google Cloud (GCP), or Azure. You build solutions by piecing together different services and running all of your business logic in AWS Lambda or CGP Cloud Functions instead of on a server.</p>
<p>With your cloud platform dealing with almost all of the operations (security, redundancy, scalability, and networking), you're left to focus on building the best solutions you can. This means features can be built quicker and companies don't need to hire operations specialists.</p>
<p>This article will cover the 5 steps to learning how to become a serverless developer so you can build some kick-butt products.</p>
<h2 id="heading-in-summary">In Summary</h2>
<ol>
<li><p>Have solid JavaScript or Python skills. You don't have to be a wizard but being comfortable write an Express or Flask server will make the rest much easier.</p>
</li>
<li><p>Pick your framework – choose either <a target="_blank" href="https://www.serverless.com/">The Serverless Framework</a> or <a target="_blank" href="https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html">AWS CDK.</a></p>
</li>
<li><p>Follow a tutorial to start learning to build with your framework. Start with building an API with API Gateway and Lambda.</p>
</li>
<li><p>Learn more about the services you're using. What are the benefits, limitations, good use cases and bad ones?</p>
</li>
<li><p>Build what you've learnt in the tutorials into your own project.</p>
</li>
</ol>
<p>Now you've learnt to build an API, add some more services, repeating steps 3-5 each time. A good order could be:</p>
<ul>
<li><p>DynamoDB – getting and writing data</p>
</li>
<li><p>S3 – reading and writing files</p>
</li>
<li><p>DynamoDB – with secondary indexes and queries</p>
</li>
<li><p>Cognito – Authorisation for your API</p>
</li>
<li><p>AppSync – GraphQL API</p>
</li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/u-UaP8XONgg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-1-solid-javascript-or-python-skills">1. Solid JavaScript or Python Skills</h2>
<p>Before trying to build serverless systems, you really need to have a good grasp on the fundamentals of one of the common programming languages. You don't need to be a wizard, but being comfortable using your chosen language is key.</p>
<p>The reason you need to be comfortable writing good code is that it has a massive effect on the software you build. Serverless is like building blocks and the Lambda code is like the glue. In that code, you write the logic for connecting each part.</p>
<blockquote>
<p>You could have the perfect architecture but if your Lambda code is buggy, your solution will be, too.</p>
</blockquote>
<p>I recommend either JavaScript (TypeScript) or Python. The reason I recommend these two languages is that most of the companies that will be using Serverless Architecture will be using one of these two languages. Luckily they're also the two languages taught here on FreeCodeCamp 🎉 .</p>
<p>Being the most widely used they also have more tutorials and a bigger community to help when you get stuck.</p>
<p>Another reason that I recommend these languages is that you can write the framework code with the same language you write the Lambda code with. You'll be constantly jumping between lambda code and frameworks config. Not having to switch between languages will save a LOT of you brain juice 🧠</p>
<h2 id="heading-2-pick-your-framework">2. Pick your Framework</h2>
<p>With a solid grasp of your language, you need a tool to help you create the serverless components in AWS.</p>
<p>There are LOT out there, but I would say you should pick either the Serverless Framework or AWS CDK. I have a bias towards the Serverless Framework as I have a <a target="_blank" href="https://www.youtube.com/CompleteCoding"><strong>Youtube channel</strong></a> with over 50 videos on building with it. If you're a Python developer then maybe the AWS CDK might be better suited for you.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/slsVsCdk.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-why-use-a-framework">Why use a framework?</h3>
<p>When building a solution it is possible to do it all in the AWS Console. That's how I started my AWS journey.</p>
<p>The issue is that it is not controllable, manageable or scalable. If you want to copy this setup to another account (separate dev and prod accounts) you have to remember all the steps you've done. Working with multiple team members can get messy.</p>
<p>That is why it's helpful to use a framework to allow us to write Infrastructure-as-Code (IaC). This allows us to use Git for version control. This makes working as a team much easier, enables multi-environment deployments, even continuous integration and deployment. All things that are required when running production workloads</p>
<h3 id="heading-the-serverless-framework">The Serverless Framework</h3>
<ul>
<li><p>Proven in large, production workloads</p>
</li>
<li><p>Easy to get started</p>
</li>
<li><p>Large community</p>
</li>
<li><p>Lots of tutorials</p>
</li>
<li><p>Lots of plugins to make many jobs easier</p>
</li>
</ul>
<ul>
<li><p>Not as easy when doing things that aren't serverless</p>
</li>
<li><p>If you're using Python, you'll have to configure it with YAML</p>
</li>
</ul>
<h3 id="heading-aws-cdk">AWS CDK</h3>
<ul>
<li><p>From AWS and is actively developed by them</p>
</li>
<li><p>Good support for almost anything in AWS</p>
</li>
<li><p>Some cool ways to create re-usable constructs</p>
</li>
<li><p>Growing community and pool of tutorials</p>
</li>
<li><p>Can define the config using Python</p>
</li>
</ul>
<ul>
<li>Not the same 'Plugins' ecosystem as the Serverless Framework</li>
</ul>
<h3 id="heading-other-frameworks">Other Frameworks</h3>
<p>Some of you may say "What about all of the other frameworks?" and I'll address those.</p>
<h4 id="heading-aws-sam-amp-aws-amplify">AWS SAM &amp; AWS Amplify</h4>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/SAM-AMPLIFY.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>These frameworks are designed to be very easy to do simple things. If you're reading this then you could probably use these to create an API and website quickly and easily.</p>
<p>This is great if that is all you want to do. But if you want more control over <strong>how</strong> they are deployed or want to deploy <strong>more complex systems</strong> then you're going to struggle.</p>
<h4 id="heading-terraform-amp-ansible">Terraform &amp; Ansible</h4>
<p>These frameworks have been around for a long time and are used in enterprise as their Infrastructure as Code (IaC) tool.</p>
<p>The reasons I wouldn't learn these as my first framework are:</p>
<ol>
<li><p>You have to learn a new language. Terraform uses HCL (Hashcorp Language) and Ansible uses YAML. Leaning a new language whilst trying to learn about Serverless architecture isn't ideal. Also switching into that language when you need to create infrastructure is brain zapping.</p>
</li>
<li><p>They are opinionated and strict. Getting a small thing not configured perfectly will just not work, often with a hard to understand error.</p>
</li>
<li><p>They're not as flexible or powerful as CDK or Serverless Framework.</p>
</li>
</ol>
<h4 id="heading-webiny-amp-serverless-cloud">Webiny &amp; Serverless Cloud</h4>
<p>These are very new frameworks that are trying to make IaC as simple but as powerful as possible.</p>
<p>The reason that I would avoid these for now is that they're just too new. This has two drawbacks:</p>
<ol>
<li><p>The community isn't as big. This means fewer tutorials and fewer people to ask if you get stuck</p>
</li>
<li><p>Things change quickly. Best practices and common structure, but sometimes APIs, methods and parameters too. When learning a framework you don't want to have to deal with this stuff.</p>
</li>
</ol>
<p>If there is a feature you absolutely need to use and you have someone who is very experienced with that specific framework, then it could work. I would still recommend one of the main two.</p>
<h2 id="heading-3-follow-a-tutorial">3. Follow a tutorial</h2>
<p>With your framework chosen, you can now start building things with it.</p>
<p>The first thing you should build is an API using just Lambda and API Gateway. This is very simple but will get you practice with the core fundamentals of the framework. Understanding the fundamentals will make learning more advanced things much easier.</p>
<h3 id="heading-why-follow-a-tutorial">Why Follow a Tutorial</h3>
<p>When learning a new service, it may be tempting to try and learn it by adding it straight into an existing project. I would recommend always trying to follow a tutorial the first time you work with any new service or tool.</p>
<p>When you follow a tutorial it should work without any issues. This means you can focus on learning about the service and how it fits in with everything else.</p>
<p>Adding it into your own project means if something doesn't work you've got to debug it on your own. You might not know whether you've used the service wrong or if there is a bug connecting it to your existing system.</p>
<h2 id="heading-4-learn-more-about-the-services-youre-using">4. Learn more about the services you're using</h2>
<p>Now that you've used the new service, it's a good idea to learn a bit more about it. The key things I would want to know about a service I use are:</p>
<ol>
<li><p>What are its strengths, weaknesses, and limitations?</p>
</li>
<li><p>What are some ideal use cases for using the service?</p>
</li>
<li><p>What are some use cases where you should avoid using this service?</p>
</li>
</ol>
<p>Knowing these three things, you will be much better able to decide whether a service will be a good fit for the solution you are currently building.</p>
<p>You can learn from a range of places: tutorials, articles, and even the AWS Docs.</p>
<p>For example, AWS Lambda is great for most APIs, but can't run for more than 15 minutes. If I need to build an API that does some batch processing that takes 10-20 minutes then it would time out half of the time. Therefore I need to find another solution.</p>
<p>Again you don't need to understand every little detail about every service, just enough to know when it is a good idea to use and when not to.</p>
<h2 id="heading-5-build-your-own-projects">5. Build your own projects</h2>
<p>Now you know how to build with the new service (from the tutorials) and have a good understanding of when to use the service.</p>
<p>From here it is time to use these services in your own projects.</p>
<p>I would recommend starting with a personal project that you use just for practicing using new services in. That way you don't have to worry about breaking things and you can focus on how the service is working.</p>
<p>You can now start using it in production apps and this is where you'll learn a lot about the details of a service. You learn how to make it meet the business requirements and how it works with other services. If you can do this as part of your job, even better. Else have a deployed app that you treat with the same care.</p>
<h2 id="heading-repeat-steps-3-5">Repeat steps 3-5</h2>
<p>Congratulations! You've learnt how to build an API using a framework.</p>
<p>That isn't the end, as there's always more to learn and a way to become a better serverless developer. Pick a new topic, service, or design pattern and repeat steps 3-5.</p>
<p>If you've just made your first serverless API, then here are the next topics and services I would learn to continue your journey.</p>
<ul>
<li><p>DynamoDB – creating a simple table, getting and writing data</p>
</li>
<li><p>S3 – creating an S3 bucket, reading and writing files</p>
</li>
<li><p>DynamoDB – with secondary indexes and queries</p>
</li>
<li><p>Cognito – Authorisation for your API</p>
</li>
<li><p>AppSync – GraphQL API</p>
</li>
</ul>
<p>After that, focus on creating features for your solutions and use that to guide what to learn next.</p>
<p>If you're a JavaScript developer wanting to use the Serverless Framework, then a great place to start is my <a target="_blank" href="https://www.youtube.com/CompleteCoding">Youtube channel</a> where we create tutorials on becoming the best Serverless Developer that you can be.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy Your freeCodeCamp Project on AWS – A Beginner's Guide to the Cloud ]]>
                </title>
                <description>
                    <![CDATA[ By Luke Miller One night in June, 2017 I stumbled across a website called freecodecamp.org. I was a teacher at the time who was looking for a career change. But I assumed being a programmer was out of my reach. After all, I didn’t consider myself a m... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-your-freecodecamp-project-on-aws/</link>
                <guid isPermaLink="false">66d460133bc3ab877dae220c</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 08 Jun 2021 23:47:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/new-cover-1.PNG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Luke Miller</p>
<p>One night in June, 2017 I stumbled across a website called <a target="_blank" href="http://freecodecamp.org/">freecodecamp.org</a>. I was a teacher at the time who was looking for a career change. But I assumed being a programmer was out of my reach.</p>
<p>After all, I didn’t consider myself a math whiz, never went to school for computer science, and felt I was too old to get into coding. </p>
<p>Thankfully, freeCodeCamp helped me take my first steps toward becoming a developer, and I soon shed each of those self-limiting thoughts. </p>
<p>Now, as a Samsung software engineer and three-time certified AWS developer, I look back at those nights and weekends I spent working through the freeCodeCamp curriculum and see them as a major contributor to my successful work transition and my accomplishments as a programmer. </p>
<p>With my teacher-heart still present, though, I wrote this post to encourage other freeCodeCamp students to continue with their learning as well as to supplement their freeCodeCamp curriculum. </p>
<p>Familiarity with cloud computing, like AWS, Azure, or Google Cloud, is an increasingly present requirement in job postings. </p>
<p>This post will give any newbie who is still working through freeCodeCamp’s curriculum a simple introduction into the vast world of cloud computing. I'll show you how to take one of your freeCodeCamp front end projects and deploy it on AWS in a variety of ways. </p>
<p>Enjoy, and thank you freeCodeCamp!</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>The point of this post is to extend your learning from the <a target="_blank" href="https://www.freecodecamp.org/learn/front-end-libraries/front-end-libraries-projects/">Front End Development Libraries Projects</a> portion of the freeCodeCamp curriculum by deploying them to AWS. </p>
<p>So the prerequisites are:</p>
<ol>
<li>finish one of the Front End Libraries Project challenges</li>
<li>have an AWS account (go <a target="_blank" href="https://portal.aws.amazon.com/billing/signup#/start">here</a> to sign up)</li>
</ol>
<h2 id="heading-why-deploy-your-project-to-aws">Why Deploy Your Project to AWS?</h2>
<p>As you work through the freeCodeCamp curriculum and you finish your Front End Libraries Projects, you submit those projects via CodePen where freeCodeCamp has a series of <a target="_blank" href="https://www.guru99.com/unit-testing-guide.html">unit tests</a> to verify that you correctly completed their user stories. </p>
<p>Confetti falls, you see some inspirational phrase, and you’re probably very proud of your project. You might even want to share that accomplishment with your friends and family. </p>
<p>Sure, you could share your CodePen link with them, but in the real world when a company finishes a project they don’t make it available via CodePen – they deploy it. So, let’s do that too!</p>
<p>Just a quick note: when I say “deploy”, I mean taking your code from a local environment (your computer, or the freeCodeCamp or CodePen editors) and putting it on a server. That server, which is just another computer, is networked in such a way for the world to access your site.</p>
<p>If you wanted to, you could <a target="_blank" href="https://makeawebsitehub.com/host-website-computer/">set up</a> a pc in your home and do the proper networking for it to serve your project to the world. Or, you could subscribe to a <a target="_blank" href="https://www.pcmag.com/picks/the-best-web-hosting-services">hosting</a> company for them to serve your website for you. </p>
<p>There are a variety of approaches to deploy code, but one very popular method is to use a cloud computing platform like Amazon Web Services (AWS). More on that next.</p>
<h2 id="heading-what-is-aws-a-brief-intro-to-the-cloud">What is AWS? A Brief Intro to the Cloud</h2>
<p>Amazon Web Services (AWS) offers a “cloud computing platform”. That’s a little jargon with a lot packed into it. Let’s unpack.</p>
<p>AWS has services ranging from <a target="_blank" href="https://aws.amazon.com/s3/?nc=sn&amp;loc=1">simply storing files</a>, <a target="_blank" href="http://aws.amazonaws.com/aws/ec2">running servers</a>, <a target="_blank" href="https://aws.amazon.com/transcribe/">converting speech to text</a> or <a target="_blank" href="https://aws.amazon.com/polly/">text to speech</a>, <a target="_blank" href="https://aws.amazon.com/machine-learning/">machine learning</a>, <a target="_blank" href="https://aws.amazon.com/vpc/">private cloud networks</a>, and about 200+ other services.</p>
<p>The basic idea behind cloud computing is to gain on-demand IT resources. The alternative, like we talked about earlier, is for you to own those IT resources. </p>
<p>There are a number of benefits to using a cloud computing platform instead of owning, one of the main ones being the cost savings.</p>
<p>Imagine trying to pay for all the physical IT resources that it would take to run the 200+ services AWS offers. That upfront cost is something most companies cannot afford, and fewer could pay for the engineers to configure. </p>
<p>Also, since the resources are on-demand, cloud platforms allows you to launch resources much faster than an IT department could. </p>
<p>In short, with a cloud computing platform like AWS you save on cost and deployment time, plus many more benefits not the least of which is security. Needless to say, cloud computing is the new sexy approach to IT and AWS is leading the pack.</p>
<p>Why does this matter to you? If you intend to pursue a career in development, regardless of your focus (backend, frontend, web technologies, mobile apps, gaming, desktop applications, and so on) you will find that many job postings include a reference to cloud compute platform experience. </p>
<p>So the more you can become familiar with one, like AWS, the more you’ll separate yourself from other candidates.</p>
<h1 id="heading-how-to-deploy-your-freecodecamp-project-with-aws-s3">How to Deploy your freeCodeCamp Project with AWS S3</h1>
<h2 id="heading-what-is-aws-s3">What is AWS S3?</h2>
<p>Throughout this post we’ll deploy your freeCodeCamp front end project using various services on AWS. </p>
<p>Our first approach is through the AWS service called S3. S3 stands for Simple Storage Solution. As you might have guessed from that name, the service lets you simply store stuff, more specifically <strong>objects</strong>.</p>
<p>You can find tutorials and courses on S3 that are hours and days long. On the surface though, it’s just a place where you can store objects. Objects can be things like image files, video files, even HTML, CSS, and JavaScript files. </p>
<p>As you dive deeper into S3, you’ll learn that S3 allows you to do a lot of things to and with those objects. But for our project, we just want to learn how to store objects and look at a single one of those deeper features – using S3 as a website hosting service. </p>
<p>Thats right, AWS S3 is a service we can use to deploy a website.</p>
<h2 id="heading-how-to-get-your-project-ready-for-deployment">How to Get Your Project Ready for Deployment</h2>
<p>Throughout this post, I’ll be using the <a target="_blank" href="https://www.freecodecamp.org/learn/front-end-libraries/front-end-libraries-projects/build-a-random-quote-machine">Random Quote Machine</a> project. I am using the code from the example provided in the instructions. </p>
<p>We need to take your CSS, HTML, and JS code from CodePen and put them into their own separate files in a text editor.</p>
<h3 id="heading-open-your-ide-for-example-visual-studio-code">Open Your IDE (for example, Visual Studio Code)</h3>
<p>In the CodePen environment, CodePen links your CSS to your HTML code, as well as your JS file to your HTML. We need to account for this before we deploy to S3, so we are going to test locally first.</p>
<p>Open your editor of choice, and create a new directory (folder) to hold your three CodePen files. I called mine <em>random-quote-machine</em>. Next, create three new files:</p>
<ul>
<li>index.html</li>
<li>styles.css</li>
<li>main.js</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-8.08.42-PM--1-.png" alt="Visual Studio Code with three empty files" width="600" height="400" loading="lazy">
<em>Three empty files: main.js, styles.css, and index.html</em></p>
<p>Go ahead and copy your CodePen HTML file into index.html, CodePen JS file into main.js, and CodePen CSS file into styles.css.</p>
<h3 id="heading-check-your-tags">Check your  tags</h3>
<p>CodePen does not require the <code>&lt;body&gt;</code> tag, but it’s good practice to add that. Make sure your HTML content has one. Look back at your fCC curriculum if you don’t remember where these go.</p>
<h3 id="heading-add-and">Add  and </h3>
<p>You will now need to link your styles.css and main.js to your index.html, since CodePen no longer does it for you.</p>
<p>Above your <code>&lt;body&gt;</code> opening tag, add <code>&lt;link rel="stylesheet" href="style.css" /&gt;</code> which will make your css in styles.css take effect.</p>
<p>Below your <code>&lt;body&gt;</code> closing tag add <code>&lt;script src="main.js"&gt;&lt;/script&gt;</code> which will link your JavaScript in main.js file accessibly to index.html.</p>
<h3 id="heading-verify-that-your-site-still-works-locally">Verify that Your Site Still Works Locally</h3>
<p>It’s time to test that our code is working. Open a web browser, and then type <code>ctrl+o</code> to select a local file to display in the browser. Navigate to your folder with our three files, and then double click on index.html to open it.</p>
<p>If everything is working, great! It wasn’t for me though. The code I used had SASS styling, which I needed to make some adjustments to. </p>
<p>If you imported any libraries via CodePen, those would need to be imported, likely through the CDN. A simple Google search for those libraries’ documentation should help you to find out how to import them.</p>
<p>Make whatever changes you find necessary to get your project in working order, but remember, the purpose of this exercise is to learn about deploying a website on AWS S3. So if you’re getting bogged down with something small but the site mostly works, continue on with this tutorial to keep the momentum going and then at a later time resolve whatever CSS or JS issues you have.</p>
<p>Once your site works locally, even if not 100% to your liking, let's get into AWS.</p>
<h2 id="heading-how-to-work-with-the-s3-management-console">How to Work with the S3 Management Console</h2>
<p>After logging into AWS, in the top search bar type “S3” and then select the option that says “S3”, not “S3 Glacier”.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-8.41.52-PM--1-.png" alt="AWS Management Console S3 search" width="600" height="400" loading="lazy">
<em>Searching for S3 in the AWS Management Console</em></p>
<p>Alternatively, instead of the search bar you could expand the “Services” dropdown and enjoy looking at all the AWS service offerings. Either way, let’s click on S3.</p>
<p>You should now see the S3 Management Console. Something like this…</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-8.46.20-PM--1-.png" alt="The AWS S3 Management Console" width="600" height="400" loading="lazy">
<em>The S3 Management Console</em></p>
<p>This is what AWS calls the Management Console. It’s the website interface to interact with AWS and create resources and services. </p>
<p>There is a Management Console for all of the AWS services, but this console is not the only way you can interact with AWS. There is also a <a target="_blank" href="https://aws.amazon.com/cli/">CLI</a> (command line interface), which allows you to script to AWS what you would like to do. </p>
<p>Instead of clicking on buttons, in the CLI you type what you would otherwise have clicked on (though less verbosely). We are going to stick with the console for now.</p>
<p>Here you can view all of your S3 buckets. S3 is composed of buckets, and a bucket is basically a big container where we get to put files. Think of a bucket as a drive on your computer (like your C: drive). It technically isn’t – it’s a method for routing in S3 – but for now it’s fine to see the bucket in this way.</p>
<h3 id="heading-create-your-s3-bucket">Create Your S3 Bucket</h3>
<p>Click on the <strong>Create bucket</strong>. Then, enter a unique name for your bucket (no spaces allowed). </p>
<p>The name of your buckets must be globally unique. So, if you try to create one named <em>test</em>, there is probably someone in the world who used that already so it won’t be available.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-8.56.19-PM--1-.png" alt="Input form for creating an S3 bucket" width="600" height="400" loading="lazy">
<em>Entering my new S3 bucket name</em></p>
<p>After inputting your bucket’s name, scroll down to the section called <strong>Block Public Access settings for this bucket.</strong> We want to uncheck the <strong>Block <em>all</em></strong> public access checkbox, and then further down check to acknowledge the warning box that appears.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-9.13.44-PM--1-.png" alt="Public access checkbox options during create S3 bucket process" width="600" height="400" loading="lazy">
<em>Your settings should match this</em></p>
<p>Security and permissions in AWS is a lengthy topic, but, as you can see by the warning, you typically do not want to unblock public access to your files. </p>
<p>In our case, we want our website’s visitors’ web browsers to be able to access our index.html file so that they can see our project. </p>
<p>There are other methods we could use to unblock access to our bucket’s content, but for now this is sufficient for our goal of introducing you to S3 and deploying a project on AWS.</p>
<p>Scroll down to the bottom of the form. Click the <strong>Create bucket</strong> button at the bottom. If your name was unique, you just made your first AWS S3 bucket, congrats!</p>
<p>It’s pretty amazing to stop and think that in that short amount of time, AWS just made space available for you in their network to store an unlimited amount of data. That’s right, unlimited! </p>
<p>Although the maximum size for a single object in your bucket is 5 terabytes (good luck hitting that), your new bucket can hold as much as you want in it.</p>
<h3 id="heading-upload-your-files-to-your-bucket">Upload Your Files to Your Bucket</h3>
<p>Next, back in the S3 console view, click on the link to your newly created S3 bucket. Once inside, we want to click on the <strong>Upload</strong> button. Select your three files (index.html, styles.css, and main.js).</p>
<p>After you see their names show up in the list of items about to be uploaded, scroll down to the bottom of the upload form. </p>
<p>Expand the <strong>Additional upload options</strong>, and then scroll down to the <strong>Access control list (ACL).</strong> Check the <strong>Read</strong> boxes for <strong>Everyone (public access)</strong>, and then check the acknowledgement box that appears underneath, just like when we created our bucket.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-9.11.52-PM.png" alt="S3 file upload ACL options" width="600" height="400" loading="lazy">
<em>Your selections should look like this</em></p>
<p>Scroll down the rest of the way and click the <strong>Upload</strong> button.</p>
<h3 id="heading-enable-hosting-on-the-s3-bucket">Enable Hosting on the S3 Bucket</h3>
<p>Now we have our S3 bucket, we have our files, we have them publicly accessible, but we’re not quite done.</p>
<p>S3 buckets by default are not configured to be treated like web servers. Most of the time companies want the contents of S3 buckets kept private (like hospitals storing medical records, or banks saving financial statements). To make the bucket act like a web server, providing us with a URL to access our files, we need to adjust a setting.</p>
<p>Back at your bucket’s main menu view, click on the <strong>Properties</strong> tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-9.43.43-PM--1-.png" alt="Main view for S3 bucket" width="600" height="400" loading="lazy">
<em>Select the Properties tab</em></p>
<p>Scroll down to the bottom until you find <strong>Static website hosting</strong>, and click the <strong>Edit</strong> button. Set <strong>Static website hosting</strong> to <strong>Enable</strong>, and then in the <strong>Index document</strong> and <strong>Error document</strong> type index.html<em>.</em> Then click the <strong>Save changes</strong> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-9.15.08-PM--1-.png" alt="S3 Static Web Hosting Property Settings" width="600" height="400" loading="lazy">
<em>Your selections should match this</em></p>
<p>You’ll return to the Properties tab of your bucket. Scroll back down to the <strong>Static website hosting</strong> section, and there you’ll find a link. </p>
<p>Reading that URL you’ll notice the name of your bucket is in it. By typing index.html in the two fields during the configuration, we told AWS that when this bucket’s URL is opened to use the index.html page to load.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-20-at-9.15.22-PM--1-.png" alt="S3 Properties' static hosting url" width="600" height="400" loading="lazy">
<em>You should see your bucket’s website endpoint now</em></p>
<p>If you click on that link, your project should now be viewable.</p>
<h3 id="heading-troubleshooting">Troubleshooting</h3>
<p>If your site worked locally, but is not working when you open the S3 website endpoint link, there are a few common problems to try and resolve.</p>
<p>First, make sure you selected the same files that worked locally. Reopen the local files in a web browser to make sure they work. If it works locally but not through S3, try re-uploading them and ensure you select the same files.</p>
<p>Next, go to your bucket’s <strong>Permissions</strong> tab and make sure you have the <strong>Block <em>all</em></strong> public access set to <strong>Off</strong>.</p>
<p>Lastly, delete your files you uploaded, and re-upload them, ensuring that you select the <strong>Read</strong> checkboxes described above, as well as the acknowledgement box.</p>
<p>If you are still having problems, feel free to comment with your issue and I’ll be happy to help. Don’t get too discouraged either. AWS can take a while to learn so go easy on yourself if things aren’t falling into place the first time.</p>
<h2 id="heading-you-did-it">You Did It!</h2>
<p>Now, instead of sharing a CodePen URL to your friends and family, you have your very own S3 bucket website endpoint to share. </p>
<p>Granted, it’s still not your own personalized domain, but hey, it’s still cool to know you just deployed a website the same way thousands of businesses do.</p>
<p>You’ve now not only learned the front end skills associated with freeCodeCamp’s Front End Libraries Projects, but you also took your first steps into cloud computing with a deployment of a website. You should be very proud!</p>
<h2 id="heading-more-on-s3">More on S3</h2>
<p>Configuring an S3 bucket to be a website endpoint is just one of many ways to use S3. We could also use S3 to store data that we want to pull into an application. For example, the quotes on our Random Quote Machine could be stored in S3 as a <a target="_blank" href="https://www.w3schools.com/whatis/whatis_json.asp">JSON</a> file, and then our front end could request them. </p>
<p>That might seem like an odd adjustment to make when they could just be listed in our main.js file. But if we had other apps that needed access to those quotes then S3 could act as a central repository for them. </p>
<p>This is in fact the most popular way to use S3, as a data store for applications. We could also use the Glacier option of S3 to archive objects that we don’t anticipate accessing frequently, which would save us money from the standard S3 bucket configuration. </p>
<p>One last idea, though not the last way to use S3, is that we could save logs from a running application to an S3 bucket so that if there was a bug we could inspect those logs to help identify the source of the problem.</p>
<p>Whatever the use case, the concept of S3 is the same: we are storing objects in a bucket and those objects have permission settings to determine who can view, edit, or delete them (remember, we set our files to be publicly readable).</p>
<h2 id="heading-next-up">Next Up!</h2>
<p>As stated before, AWS has hundreds of services, which means there are sometimes multiple ways to do a job. We walked through one way to host a site, but there are two more worth noting that will help you gain more exposure to AWS as a whole.</p>
<h1 id="heading-how-to-deploy-your-freecodecamp-project-with-aws-elastic-beanstalk">How to Deploy your freeCodeCamp Project with AWS Elastic Beanstalk</h1>
<p>Now we're going to learn more about the AWS cloud platform services and see an alternative way to deploy your code so you can experience more that AWS has to offer. </p>
<p>Specifically, we are going to get an introduction into several of the core AWS services and resources: EC2s, Load Balancers, Auto Scaling Groups, and Security Groups. </p>
<p>Wow, that’s a lot of topics to cover. In the first part above, we only looked at S3 – how are we going to learn about all these other services? </p>
<p>Well, thankfully AWS offers a service called <a target="_blank" href="https://aws.amazon.com/elasticbeanstalk/">Elastic Beanstalk</a> which <a target="_blank" href="https://techterms.com/definition/bootstrap">bootstraps</a> the deployment process for web applications. By deploying your freeCodeCamp project via Elastic Beanstalk you can quickly gain experience with these core AWS services and resources.</p>
<p><img src="https://i0.wp.com/lukemiller.dev/wp-content/uploads/2021/04/download.png?resize=400%2C223&amp;ssl=1" alt="Image" width="600" height="400" loading="lazy">
<em>We will use the AWS service called Elastic Beanstalk to deploy</em></p>
<h2 id="heading-before-we-jump-back-into-aws">Before We Jump Back into AWS</h2>
<p>We discussed in Part 1 that to “deploy” code means to configure a computer in a way to serve the project and make it accessible to visitors. The nice thing about cloud computing platforms is that the configuration of that server can be done for us if we want.</p>
<p>Consider our S3 deployment for a second. Our files were stored on a machine owned by AWS, and AWS took care of configuring that computer to serve the index.html page and give us an endpoint for viewing the project. </p>
<p>Aside from telling S3 the name of our index.html file, we did none of that configuration and yet we still walked away with a deployed project and an endpoint to view it.</p>
<p>When we use Elastic Beanstalk, it will similarly include AWS handling a lot of configuration for us. But this time we are also going to do a bit of configuring ourselves. </p>
<p>By doing this, we will see an alternative and very popular method for deploying a web application. This time, we will add code that “serves” the index.html but we’ll let AWS handle the launching of the hardware and giving us an endpoint to view our project.</p>
<h2 id="heading-lets-configure">Let’s Configure!</h2>
<p>We are going to add some code to our project to serve our index.html. Remember, S3 did this behind the scenes for us.</p>
<p>I have created a Git repository for you to clone or download and use for this tutorial. If you’re familiar with Git, then feel free to clone the <a target="_blank" href="https://github.com/dalumiller/fcc-to-aws-part2">repo</a> and jump ahead to <strong>Paste Your Code</strong>. But if you’re not familiar with Git, then follow the instructions for downloading this repo.</p>
<h3 id="heading-download-the-repo">Download the Repo</h3>
<ul>
<li>Go to <a target="_blank" href="https://github.com/dalumiller/fcc-to-aws-part2">https://github.com/dalumiller/fcc-to-aws-part2</a></li>
<li>Click on the green <strong>Code</strong> dropdown menu and then select <strong>Download Zip</strong></li>
<li>Unzip/extract the files from the zipped download</li>
</ul>
<h3 id="heading-paste-your-code">Paste Your Code</h3>
<p>In the newly downloaded/cloned folder (which I'll refer to as <code>fcc-to-aws-part2</code> folder), we want to paste your <code>index.html</code>, <code>main.js</code>, and <code>styles.css</code> files that we uploaded to S3. </p>
<p>In the <code>fcc-to-aws-part2</code> folder, there is another folder named <code>public</code> which has empty index.html, main.js, and styles.css files. Go ahead and paste your code into those.</p>
<p>After pasting, let’s confirm everything is working so far.</p>
<p>Open a new browser tab, and then type <strong>ctrl+o</strong> and navigate to <code>fcc-to-aws-part2/public/index.html</code>. Open index.html in that new tab. Your app should be running now. </p>
<p>If it is not, stop and make sure you pasted the correct code into the correct .html, .js, and .css files. Also, make sure you’re using that same code that worked during your S3 deployment.</p>
<p>With that working, we are now ready to discuss what this extra stuff is in the <code>fcc-to-aws-part2</code> folder. Make sure you do not make any changes to any files other than the index.html, main.js, and styles.css.</p>
<h3 id="heading-configuration-with-nodejs-amp-expressjs">Configuration with Node.js &amp; Express.js</h3>
<p>Remember I said we were going to do some configuration this time? <a target="_blank" href="http://nodejs.org/">Node.js</a> (aka Node) and <a target="_blank" href="http://expressjs.org/">Express.js</a> are what we’re using to accomplish that configuration. </p>
<p>freeCodeCamp has a <a target="_blank" href="https://www.freecodecamp.org/learn/apis-and-microservices/#basic-node-and-express">tutorial on Node and Express</a>, so if you’d like to pause and go through that feel free. But I’ll also provide a brief introduction explaining why and how we are using Node and Express.</p>
<h3 id="heading-why-use-node">Why Use Node?</h3>
<p><img src="https://i1.wp.com/lukemiller.dev/wp-content/uploads/2021/04/download-3.png?resize=480%2C240&amp;ssl=1" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Node.js is a JavaScript runtime for server-side applications. That is a lot of jargon, so let’s break it down a bit.</p>
<p>“Node is a JavaScript runtime” – a runtime, in the world of programming, is an execution model. In other words, it is a process that implements how to execute code. So, Node, is a process that implements how to execute JavaScript code. </p>
<p>When we have JavaScript code like this:</p>
<pre><code><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello World"</span>)
</code></pre><p>Node knows what to do with that code.</p>
<p>“…for server-side applications”- remember that we are trying to deploy our code to be served by a server, and that a server is just a computer. Also, note that a server is different from a client, and in the case of a website the client is the web browser. </p>
<p>When you open your project in a web browser, the browser handles reading the index.html, main.js, and styles.css files. That’s right, the browser (client) knows how to read and execute JavaScript code, so that when it sees <code>console.log("Hello World")</code> it knows what to do. </p>
<p>Our main.js file is JavaScript code that is being run client-side, by the browser. But, Node is for server-side JavaScript. So, your browser knows how to run that JavaScript code, but how does the computer that serves our website know how to run JavaScript? Node.</p>
<p><img src="https://i0.wp.com/lukemiller.dev/wp-content/uploads/2021/04/download-4.png?resize=596%2C289&amp;ssl=1" alt="Image" width="600" height="400" loading="lazy">
<em>JavaScript can run on the client (ie: web browser) and server</em></p>
<p>So, all together, “Node is a JavaScript runtime for server-side applications” means that Node is a process for implementing how to execute JavaScript code on a server, as opposed to a client like a web browser. </p>
<p>Without Node, the computer/server does not know how to run JavaScript code.</p>
<h3 id="heading-why-use-express">Why Use Express?</h3>
<p><img src="https://i1.wp.com/lukemiller.dev/wp-content/uploads/2021/04/download-5.png?resize=435%2C157&amp;ssl=1" alt="Image" width="600" height="400" loading="lazy">
<em>Expres.js a web application framework for Node.js</em></p>
<p>While Node gives us the server-side runtime, which is that environment where we can run JavaScript code, Express gives us a framework for serving web applications.</p>
<p>When we deployed via S3, we told S3 the file name we wanted served (index.html). Now, Express will be where we configure what file we want served. </p>
<p>If we had a website with different routes (ie: www.example.com/home, /media, /about, /contact), then we might have different HTML files for each of those pages. We would use Express to manage serving those pages if the web browser requested them. </p>
<p>For example, when the web browser requested www.example.com/contact, Express would get that request from the browser and respond with contact.html.</p>
<h3 id="heading-appjs">app.js</h3>
<p>Now we know that Node is what lets us run JavaScript code on the server and Express is handling our requests from the browser. So let’s look in <code>fcc-to-aws-part2</code> at our <code>app.js</code> file and read it line by line to understand what we’ve added to our project.</p>
<p>First…</p>
<pre><code><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
</code></pre><p>This declares a variable called <code>express</code>, and then the <code>=</code> sign means we are assigning it a value. But what’s this <code>require(“express”)</code>?</p>
<p>The <code>require</code> function is just a function Node is aware of, and when Node sees it then Node will look for a folder in the <code>node_modules</code> folder by the same name. You have this <code>node_modules</code> folder in your <code>fcc-to-aws-part2</code> folder. </p>
<p>Once Node finds <code>express</code> inside the <code>node_modules</code> folder, it will import the code from the <code>express</code> folder and assign it to the variable we declared. This lets us use the code that comes from the <code>express</code> folder without having to write it ourselves. This importing code into our code is the concept of modules.</p>
<p>A module is just a bundle of code. It could be one line long or much longer. Therefore, a <code>NODE_module</code> (emphasis added to node) is a bundle of code that can run in the Node runtime. We are using, or <code>require</code>-ing, the <code>express</code> module.</p>
<p>The <code>express</code> module is the Express.js web application framework we discussed earlier. </p>
<p>I won’t got into how to add modules to the <code>node_modules</code> folder, but for now know that by declaring the variable <code>express</code> and using the <code>require(“express”)</code> syntax, we are bringing in that Express.js framework and making it accessible via the variable we assigned it to.</p>
<p>There was a lot packed into those previous four paragraphs, I recommend slowing down and reading it again to make sure you really got it!</p>
<p>Next up…</p>
<pre><code><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);
</code></pre><p>Ah! This should look familiar now. We are bringing in another module, this time the <code>path</code> module. We’re assigning it to the <code>path</code> variable. </p>
<p>You might have noticed a pattern that the variable name we use matches the module name. It doesn’t have to be that way. We could assign the <code>path</code> module to the <code>foobar</code> variable. But it makes more sense to name it what it is.</p>
<p>What does the <code>path</code> module do? It is a module (just a bundle of code) that lets us work with file and folder paths. This way we can use a JavaScript syntax that Node understands in order to access files/folders on our server. That will come in handy when we want to reference where index.html is located in our project.</p>
<p>Alright, moving on…</p>
<pre><code><span class="hljs-keyword">const</span> app = express();
</code></pre><p>Express, again? That’s right. The first time we were just importing the module, now we are using it. </p>
<p>To use the Express framework we have to instantiate it, meaning we have to run this express function, <code>express()</code>, and now we’re assigning it to the <code>app</code> variable.</p>
<p>“Whoa, but Luke, why not have this line of code right after the earlier <code>express</code> line of code?”</p>
<p>Good question. It is a common pattern to import (or use <code>require()</code>) all the modules you’ll need at the top of your code, and then use them after you’re done importing.</p>
<p>Now for the big banana…</p>
<pre><code>app.use(express.static(path.join(__dirname, <span class="hljs-string">"public"</span>)));
</code></pre><p>Whoa there! Let’s break this apart a little.</p>
<p>First, we are calling a function, the <code>app.use()</code> function. This is telling our Express application that we want to USE another function in our app. Makes sense. </p>
<p>The function we are telling Express that we want run is the <code>express.static()</code> function being called inside of the <code>.use()</code> function parameter. So, <code>app.use()</code> is telling Express we want to use some code in the app, and specifically here we are wanting to USE <code>express.static(path.join(__dirname, “public”))</code>.</p>
<p>Now, <code>express.static()</code> is an Express function which we can use since it’s part of the module that we imported and assigned to the <code>express</code> variable.</p>
<p>The <code>.static()</code> function handles serving static files. I hope your ears perked up and eyes opened wide! I’ll say it again. The <code>.static()</code> function handles SERVING static files. Serving! </p>
<p>Remember, in this deployment approach we are handling a bit more of the configuration to SERVE our project. Here is the Express function that we are using to say, “I want to serve some static files”. Static files means our index.html file.</p>
<p>So, <code>app.use()</code> was saying, “Hey, I want to run some code for this app inside my function parameter”. Specifically we want to run <code>express.static()</code> which says, “I want to deliver static files, like an index.html”, and then its function parameter tells us where to find those static files. </p>
<p>So let’s look at <code>path.join(__dirname, “public”)</code> to understand how it’s telling us where our static files are.</p>
<p>Earlier we imported the <code>path</code> module to be able to access files in our server/computer. </p>
<p>Well, we want to access the index.html file, which is the public folder. We use the <code>path</code> function <code>.join()</code> to say, “hey, from our current directory (or, folder), go to the public folder to find the files I want”. That will return the index.html, main.js, and styles.css to the <code>express.static()</code>, which will return to the <code>app.use()</code> function that is handling what files to serve (SERVE!) our visitors.</p>
<p>All together now!</p>
<p><code>app.use()</code> = “when the browser requests the app, I want to run <code>express.static()</code>"</p>
<p><code>express.static()</code> = “I am going to deliver some static files and they are located here…"</p>
<p><code>path.join(__dirname, “public”)</code> = “grab the files in the public folder”</p>
<p>Yay! We did it! We configured out Express app running in the Node runtime to deliver the index.html, main.js, and styles.css files when someone visits our site.</p>
<p>But wait…there’s more:</p>
<pre><code><span class="hljs-keyword">const</span> port = <span class="hljs-number">8080</span>;
app.listen(port);
</code></pre><p>Remember that the <code>app</code> variable is our instance of the Express framework. The <code>.listen()</code> function is the app telling the computer, “hey, any requests made on port 8080, bring them my way!” </p>
<p>Ports and sockets are a more advanced topic, so we won’t get into it now. But just know that a computer/server has many ports, which are like access points, and we are configuring Node/Express app to only listen to one access point, 8080.</p>
<p>Lastly…</p>
<pre><code><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"listening on port: "</span> + port);
</code></pre><p>This is standard practice for Express apps, where we are just logging out on the computer what port we’re running. It gives us some verification that the Express application works.</p>
<p>Great job! That was a lot of code to walk through. </p>
<p>Now, if you’re familiar enough with how to navigate via a terminal, we can test this Express app locally on our computer before moving on to AWS. Ensuring it works locally before we go to AWS will help us in case we have errors in AWS because we’ll know the errors can’t be related to our Express app not working.</p>
<h3 id="heading-how-to-test-our-nodeexpressjs-app">How to Test Our Node/Express.js App</h3>
<p>If you open the package.json file, you’ll see a “scripts” section. This package.json file is metadata for our Node application, and it also can contain configurable commands to run in the “scripts” section. </p>
<p>I’ve included the “start” script, which runs the app.js file in the Node environment, which in turn runs our Express.js code we just discussed. This “start” script is how we run our project.</p>
<p>To test this out, in your terminal navigate to our <code>fcc-to-aws-part2</code> folder, enter <code>npm start</code> to start the app.</p>
<p><img src="https://i2.wp.com/lukemiller.dev/wp-content/uploads/2021/04/term.png?resize=640%2C28&amp;ssl=1" alt="Image" width="600" height="400" loading="lazy">
<em>Terminal command to run our app</em></p>
<p>After running that command, you should immediately see our <code>console.log()</code> message informing us that the app is running on port 8080.</p>
<p><img src="https://i2.wp.com/lukemiller.dev/wp-content/uploads/2021/04/term-1.png?resize=640%2C68&amp;ssl=1" alt="Image" width="600" height="400" loading="lazy">
<em>You should see the “listening on port: 8080” message to know it’s running</em></p>
<p>Now, to view your project, open a web browser and enter localhost:8080 in your address bar and click Enter. You should now see your project running! </p>
<p>If you don’t, make sure you are using the correct index.html, main.js, and styles.css files, and that you didn’t alter any of the code from <code>fcc-to-aws-part2</code>.</p>
<h3 id="heading-woohoo">Woohoo!</h3>
<p>Alright, now we have configured Node and Express to serve our project. Our local computer is acting as the server at the moment, but we want anyone in the world to be able to view our project and they can’t do that via our localhost:8080 address. </p>
<p>So, we are going to use AWS to host a server for us, put our app on it, and then let AWS manage the configuration necessary to generate the URL endpoint for the world to access. That’s where the AWS service called Elastic Beanstalk comes into play. Let’s get to it!</p>
<h2 id="heading-go-to-aws-elastic-beanstalk">Go to AWS Elastic Beanstalk</h2>
<p>After logging into your AWS account, search for the Elastic Beanstalk service in the top search bar. Once there, click the <strong>Create Application</strong> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-22-at-7.59.43-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>The Main Console page for AWS Elastic Beanstalk</em></p>
<p>Next we’ll add the name for our Elastic Beanstalk application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-22-at-8.00.03-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Enter your desired application name</em></p>
<p>Skip the <strong>Tags</strong> section, and let’s enter information for the <strong>Platform and Application Code</strong> sections.</p>
<p>Our <strong>Platform</strong> is Node.js, the <strong>Platform Branch</strong> is Node.js 14 running on 64bit Amazon Linux 2 (that’s telling us what kind of server is running Node), and <strong>Platform Version</strong> is whatever AWS recommends. Then for <strong>Application code</strong>, we are going to upload our code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-22-at-8.00.34-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Our settings for the Elastic Beanstalk app</em></p>
<p>Click the <strong>Create Application</strong> button.</p>
<h3 id="heading-upload-and-deploy">Upload And Deploy</h3>
<p>Before we click the <strong>Upload and Deploy</strong> button, we need to zip our project. Open the <code>fcc-to-aws-part2</code> folder, and select all of the files in it and zip them up. DO NOT zip the <code>fcc-to-aws-part2</code> folder itself – that won’t work.</p>
<p><img src="https://i0.wp.com/lukemiller.dev/wp-content/uploads/2021/04/Capture-1.png?resize=611%2C217&amp;ssl=1" alt="Image" width="600" height="400" loading="lazy">
<em>The contents of fcc-to-aws-part2 zipped together, not fcc-to-aws-part2 zipped</em></p>
<p>Now, with your files zipped, click the <strong>Upload and Deploy</strong> button in your Elastic Beanstalk environment. Select your zipped file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-22-at-8.01.17-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Upload your zipped project</em></p>
<p>After uploading your code, a black box will appear giving us a series of logs which are the steps being taken to launch the Elastic Beanstalk application. Revel in the fact that with each log entry, that is a configuration we did not have to do.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-22-at-8.01.51-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>The start of our logs</em></p>
<p>Once it finishes, you’ll be navigated to the main view of your app. You’ll see your environment has a Health status that is probably red at the start. </p>
<p>Do not be alarmed by this. AWS is now going through the configuration process that we don’t want to do, namely the launching of the server, the creation of the URL endpoint, and then running our app. It takes a few minutes for that to finish.</p>
<p>This is when Elastic Beanstalk is doing all of that configuration for us that makes cloud computing platforms so handy!</p>
<p>Notice at the top that you already have a URL endpoint given to you. Once the Health improves you should be able to open that link to see your project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-03-22-at-9.06.33-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Your url can be found here (under Randomquotemachine-env-1)</em></p>
<p>If your health status never changes to orange and then green, then something is wrong. The best thing to do is re-download the <code>fcc-to-aws-part2</code>, paste your code in, and then re-zip the contents of <code>fcc-to-aws-part2</code>, not the <code>fcc-to-aws-part2</code> folder itself, and upload it again to Elastic Beanstalk.</p>
<p>Once the health status improves, you should be able to open the link and see your project. More importantly, anyone in the world can now see your project with that link!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-02-at-11.33.49-AM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>A healthy app, time to go check it out!</em></p>
<h2 id="heading-you-did-it-1">You Did It!</h2>
<p>Congratulations!</p>
<p>I know this was a longer and more detailed process than the S3 deployment, but you did it. </p>
<p>We took on doing some of the configuration ourselves this time by creating a Node/Express app to serve our static files, but we still let AWS handle creating and configuring the server, its environment, and the endpoint for us to run our project on and view.</p>
<p>Even with our time spent going through the Express code, the amount time it took to deploy this project is minimal since AWS is automating so much for us. </p>
<p>We didn’t have to take the time to buy a server, install programs onto it, set up a URL endpoint for it, or install our project onto it. That’s the benefit of cloud computing platforms like AWS.</p>
<p><strong>NOTICE:</strong> Be sure to stop or terminate your EC2 Instance, or delete your Elastic Beanstalk Application when you’re done with it. If you do not, you’l be charged for however long your application runs.</p>
<h2 id="heading-the-underlying-services-of-elastic-beanstalk">The Underlying Services of Elastic Beanstalk</h2>
<p>I mentioned at the start of this section that the beauty of Elastic Beanstalk is that by using this service we actually get an introduction to many services: EC2, Load Balancers, Auto Scaling Groups, and even Cloud Watch. </p>
<p>Below is a brief explanation of those to further your learning about AWS and its services.</p>
<p>To get a better visual to see some of the depth of configuration Elastic Beanstalk did for us, navigate to the <strong>Configurations</strong> link and scroll through the page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-02-at-11.34.14-AM--2-.png" alt="Image" width="600" height="400" loading="lazy">
<em>An overview of the configurations Elastic Beanstalk does for us</em></p>
<h3 id="heading-ec2">EC2</h3>
<p>In the <strong>Configuration Overview</strong> there is a category called <strong>Instances.</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-02-at-9.21.24-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>The Instances configuration category</em></p>
<p>Instances is another word used for server, or computer. Remember that we’re deploying our code to a server, well AWS calls those servers Instances, and more specifically they’re called EC2 Instances. </p>
<p>EC2, short for Elastic Computer Container, is a service by AWS that lets you launch EC2 Instances very quickly. We can “spin up” a server quickly using EC2 and put whatever we want on it. </p>
<p>In our case, Elastic Beanstalk ran the EC2 service for us and started an EC2 Instance for our application to be hosted on.</p>
<p>If you still have your Elastic Beanstalk app running we can go look at your EC2 Instance. At the top of your screen, in the AWS search bar, enter EC2 and click enter.</p>
<p>You should see that you have one instance running, like this…</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-02-at-9.29.37-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Your EC2 Management Console</em></p>
<p>If you click on the <strong>Instances (running)</strong> link you’ll get to look at the specifics of the EC2 instance. This EC2, again, is just a computer running in some AWS site that has your code on it. The EC2 was launched by Elastic Beanstalk for us.</p>
<h3 id="heading-load-balancer">Load Balancer</h3>
<p>Not only did the Elastic Beanstalk create an EC2 Instance for us, it also created a Load Balancer for us. In the EC2 Management Console, on the left navigation panel scroll down to <strong>Load Balancers</strong> and click the link to see the one made for you.</p>
<p>A load balancer’s purpose is to distribute incoming traffic across multiple targets. That target in our case is an EC2, but we only have one running, so a load balancer is not all that useful at the moment. </p>
<p>Let’s pretend for a moment that our app went viral and we had tens of thousands of people trying to access the endpoint Elastic Beanstalk gave us. That single EC2 Instance would be overwhelmed with traffic. Requests would timeout, visitors would get frustrated, and our site would suffer since we don’t have enough EC2s to handle the load.</p>
<p>But! If we DID have more EC2 Instances running, each with our project deployed on it, we’d be able to handle going viral. </p>
<p>Though there is a problem that arises from having mulitple EC2s. We need each EC2 instance to be reachable by that same Elastic Beanstalk endpoint, and that’s tricky networking. </p>
<p>That’s where the Load Balancer comes in. It provides a single access point for our Elastic Beanstalk to target, and the Load Balancer then handles keeping traffic organized between the different EC2s and the Beanstalk.</p>
<p>If you look at your Load Balancers <strong>Basic Configuration</strong>, you’ll see a <strong>DNS Name</strong>, which looks a lot like the Elastic Beanstalk endpoint. If you open it, your app should run. That’s because the Elastic Beanstalk endpoint actually points to this endpoint which is able to balance traffic among multiple endpoints.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-02-at-9.36.01-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Load Balancer view in the EC2 Management Console</em></p>
<p>Now, you might be wondering “Luke, how do I get more EC2s to launch so that I can capitalize on this sweet load balancing action?” Glad you asked! The answer is Auto Scaling Groups!</p>
<h3 id="heading-auto-scaling-group">Auto Scaling Group</h3>
<p>As the name suggests, this AWS service automatically scales a group of EC2 Instances based on a set of criteria. We currently have only one EC2 Instance running that our Load Balancer targets, but an Auto Scaling Group has configurable thresholds to determine when to add or remove instances.</p>
<p>To see your Auto Scaling Group, in your EC2 Mangement Console’s left-side navigation scroll down towards the bottom until you find Auto Scaling Group and click on the checkbox next to the single Auto Scaling Group listed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-05-at-9.05.07-AM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Auto Scaling Groups list in your EC2 Management Console</em></p>
<p>Feel free to peruse the various details found in the tabs, but I want to point out a few details.</p>
<p>First, click on the Details tab, and look at the Desired, Minimum, Maximum Capacity settings. It should default to 1, 1, and 4 respectively. These values are configurable and they're our way of telling AWS how many EC2 Instances we want running. </p>
<p>Since EC2 Instances cost money to run, companies want to fine tune how often new ones are added or removed. Ours says that we only want one running, minimum of one instance running, and four at maximum. If you edit the desired value to 2 you’ll see that a new instance will launch.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-05-at-9.06.15-AM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Our Auto Scaling Group capacity settings</em></p>
<p>But how does Auto Scaling Group know when to add/remove instances? Click on the <strong>Automatic Scaling</strong> tab to see our current configuration for determining when to add/remove instances.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-05-at-9.07.26-AM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Threshold settings for our Auto Scaling Group</em></p>
<p>Here we have two policies: one for scaling in and one for scaling out. I’ve highlighted the current threshold that needs to be hit in order for either to be triggered. </p>
<p>What this says is “if there is a network issue on an EC2 Instance for more than one five minute period, add an EC2 if we haven’t hit our maximum capacity” and “if there are no network issues for five minutes, remove an EC2 if we haven’t already hit our desired capacity.”</p>
<p>And just like that, our application can grow/reduce based on our configurations of the Auto Scaling Group. Our application can now handle the additional load of going viral!</p>
<p>But, one last question: how does the Auto Scaling Group know about our EC2 network errors? Enter, CloudWatch.</p>
<h3 id="heading-cloudwatch">CloudWatch</h3>
<p>If you click on the <strong>Monitoring</strong> tab in your Auto Scaling Group, you’ll see links that will take you to CloudWatch. Once in the CloudWatch Management Console, you’ll see a variety of AWS services and resources listed and their associated metrics. </p>
<p>Browsing through the list you’ll find EC2 and Auto Scaling Group, and monitoring details for each. Where did all of these metrics come from? AWS provides them for your automatically, along with this helpful dashboard, allowing us to do some pretty cool dynamic networking and programming based on individual metrics or a composite, such as Auto Scaling.</p>
<p>The Auto Scaling Group we have has access to these metrics, and watches the NetworkOut metric of our EC2 Instances to determine if a scaling in/out action should occur.</p>
<p>Just another example of the leverage we gain from a cloud computing platform.</p>
<h2 id="heading-elasticbeanstalk-deployment-recap">ElasticBeanstalk Deployment Recap</h2>
<p>We took a look at a second method for deploying an application: Elastic Beanstalk. Compared to our S3 deployment, this route required us to add more code for configuring the serving of our index.html. </p>
<p>We used Express.js, a Node.js web application framework, to serve our front end, and then uploaded our newly updated project to Elastic Beanstalk. This in turn launched a myriad of AWS resources and services for us. </p>
<p>We learned about EC2, Load Balancer, Auto Scaling Group, and CloudWatch, and how they work together to deliver our project by a globally accessible url endpoint. </p>
<p>There are even more settings, services, and resources Elastic Beanstalk configuring and provisioned for us that we did not discuss, but for now you’ve gained a good first step into the benefits of a cloud hosting provider and a few of the key AWS services.</p>
<h1 id="heading-how-to-deploy-your-freecodecamp-project-with-aws-lambda">How to Deploy Your freeCodeCamp Project with AWS Lambda</h1>
<p>For our final part of this post, we are going to deploy our code to a serverless environment. Serverless infrastructures are becoming increasingly popular and preferred over a dedicated on-premise server or even a hosted server (like EC2). </p>
<p>Not only is the serverless route more cost-effective, it lends itself to a different software architectural approach: microservices. </p>
<p>To gain an introduction into the serverless world, we are going to deploy our code via AWS’s serverless service called Lambda and another AWS service, API Gateway. Let’s get started!</p>
<h2 id="heading-what-is-serverless">What is Serverless?!</h2>
<p><em>Say, what?!</em> This whole time we’ve been talking about servers delivering our code when the web browser requests it, so what’s <em>serverless</em> mean?</p>
<p>To start off, it doesn’t mean there is no server. There has to be one, since a server is a computer and we need our program running on a computer. </p>
<p>So, serverless doesn’t mean no-server. It means you and I, the code deployers, do not ever see the server and have no configurations to set for that server. The server belongs to AWS, and it just runs our code without us doing anything else. </p>
<p>Serverless means that for you and me in all practicality there is no server, but in reality there is.</p>
<p>Sounds pretty simple, right? It is! We have no server to configure, AWS has the server and handles everything. We just hand over the code.</p>
<p>You might be asking yourself, how is that different from EC2? Good question.</p>
<h3 id="heading-theres-less-configuration">There's Less Configuration</h3>
<p>Do you remember all the configuration options that we could mess with on Elastic Beanstalk, and in the EC2 management console, and the Auto Scaling Group, and the Load Balancer?</p>
<p><img src="https://i1.wp.com/lukemiller.dev/wp-content/uploads/2021/04/Screen-Shot-2021-04-02-at-11.34.14-AM-1.png?resize=640%2C306&amp;ssl=1" alt="Image" width="600" height="400" loading="lazy">
<em>Some of the Elastic Beanstalk configurations available to us</em></p>
<p>Then, Elastic Beanstalk created more resources all with their own configurations, like the Load Balancer and Auto Scaling Group.</p>
<p>Well, as nice as it is that we <em>can</em> configure those, it can also be a pain to setup. More importantly, it takes more time to setup, which takes us away from time spent developing our actual application. </p>
<p>With a Lambda (an AWS serverless service), we just say, “<em>hey, we want a Node.js environment to run our code</em>“, and then after that there is no more configurations to make about the server itself. We can get back to writing more code.</p>
<h3 id="heading-its-cheaper">It's Cheaper</h3>
<p>Also, that EC2 is costing us money for as long as it runs. Now it’s simple to terminate or temporarily stop to save us money, and is usually cheaper than purchasing your own physical server – but it still costs money at all times of the day that it’s running, even if no one is trying to reach your website. </p>
<p>So, what if there was an option that only charged us a fee for the time the code ran? That’s serverless!</p>
<p>With AWS Lambda, we have our code able to be run at any time, it’s sitting on a server. But it only runs our program when asked for and we are only paying for those times it runs. The cost savings are huge.</p>
<h3 id="heading-microservice-architecture">Microservice Architecture</h3>
<p>Because most programs have been written and deployed onto a server to run, all the code for that application has been bundled together so that that server could access and run the entire application. That makes sense! </p>
<p>But, if you have a way to run code with a serverless approach, which only runs that code asked for, you could break up that one application into many applications. That breaking apart an application into smaller sub-applications is the idea of microservices.</p>
<p>One of the main benefits of a microservice approach is the update process. If we have a monolithic application (one that is all bundled together on an EC2), and need to update one small piece of code, then we have to redeploy the entire application, which may mean service interruptions. </p>
<p>Alternatively, a microservice approach means we only update the minor-application of the whole that deals with that small piece of code. This is a less abrupt approach to deployment, and one of the primary benefits of a microservice approach that AWS Lambda allows us to do.</p>
<p>I should note that serverless has its own drawbacks as well. For starters, if you have a microservice architecture via serverless, then the integration of those microservices to talk to each other requires extra work that a monolithic application doesn’t have to deal with. </p>
<p>That being said, it’s an approach that is massively popular and growing in use, so it’s worth your knowing.</p>
<p><em>Disclaimer: Now, technically Part 1’s use of S3 was a serverless deployment, but typically when people discuss serverless and AWS they’re talking about Lambda.</em></p>
<h2 id="heading-how-to-get-started-with-aws-lambda">How to Get Started with AWS Lambda</h2>
<p>So, Lambdas allow us to execute code without worrying about server configuration or cost. That’s it. They run on a server owned by AWS, configured by AWS, managed by AWS, and never seen by us. </p>
<p>For the most part, all we control and care about is the function. This is what’s referred to as <em>function as a service</em> in cloud computing. It lets us focus on code without needing to think about the complexities of infrastructure.</p>
<p>So now that we know that Lambdas are just functions running on an AWS server that we don’t have to configure, let’s get started with ours.</p>
<h3 id="heading-how-to-create-our-lambda">How to Create Our Lambda</h3>
<p>Head over to the AWS Lambda Management Console. Click on the <strong>Create Function</strong> button. On the next screen, we are going to keep the <strong>Author from scratch</strong> selection, enter our function’s name (aka, the name for our Lambda), and then select the Node.js 14.x runtime. </p>
<p>That’s all we need to do for now, go ahead and click the <strong>Create Function</strong> button in the bottom right.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-14-at-9.51.24-AM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Our screen when creating our Lambda function</em></p>
<p>The next screen that loads will be our console for managing a Lambda. If you scroll down a bit you’ll find an embedded code editor and file system. A directory (aka folder) with the name of your Lambda is already there along with an index.js file. Click <strong>File</strong> and create a new file called <code>index.html</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-04-15-at-3.51.23-PM--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>You should have an index.js and index.html files</em></p>
<h3 id="heading-adjust-your-projects-code">Adjust Your Project’s Code</h3>
<p>Due to the nature of this deployment, we need to adjust our code a little bit. Instead of using the <code>&lt;link href="styles.css" /&gt;</code> tag and the <code>&lt;script src="main.js"&gt;&lt;/script&gt;</code> tag in our freeCodeCamp project’s index.htm<em>l</em> that links our .css and .js code to our HTML code, we are going to add them directly into the HTML file. </p>
<p>If we don’t do this, then when we try to open our app, it will look for those files at a different URL route than we want.</p>
<p>To change the code to work for us in Lambda, do the following:</p>
<ol>
<li>Replace the <code>&lt;link href="styles.css"/&gt;</code> line in your index.htm<em>l</em> file with <code>&lt;style&gt;&lt;/style&gt;</code> (this should be right above your <code>&lt;body&gt;</code> opening tag)</li>
<li>Open your <code>styles.css</code> file, copy all of the CSS, and paste it between the two <code>&lt;style&gt;</code> tags (like <code>&lt;style&gt; _your pasted code_</code> ).</li>
<li>Do the same thing with our <code>main.js</code> – remove the <code>src="main.js"</code> inside the opening <code>&lt;script&gt;</code> tag.</li>
<li>Open your main.js file, copy all of your JS code, and paste it between the two <code>&lt;script&gt;</code> tags at the bottom of our index.html file (ie: <code>&lt;script&gt; _your main.js code_</code> </li></ol> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Receive Emails from Your Site's "Contact Us" form Using AWS SES, Lambda, & API Gateway ]]>
                </title>
                <description>
                    <![CDATA[ By Adham El Banhawy I was recently building a simple landing page website for a client who wanted to receive emails through their website without sharing their email.  Honestly, I had never tried to implement that functionality myself before. I was a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-receive-emails-via-your-sites-contact-us-form-with-aws-ses-lambda-api-gateway/</link>
                <guid isPermaLink="false">66d45d5fbd438296f45cd383</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ email ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 24 Mar 2021 19:26:52 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/605b0cfb687d62084bf6bd50.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Adham El Banhawy</p>
<p>I was recently building a simple landing page website for a client who wanted to receive emails through their website without sharing their email. </p>
<p>Honestly, I had never tried to implement that functionality myself before. I was always used to having a simple "Contact Us" button with an anchor tag and a <code>mailto</code> in the <em>href</em> attribute like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"mailto:myemail@example.com"</span>&gt;</span>Contact Me<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>But this approach has two inconveniences:</p>
<ol>
<li>It forces both parties, the user who wants to send the message and the site owner who receives it, to share their emails with one another. While this is OK for some, it is not ideal for privacy-minded individuals.</li>
<li>For the site visitor, clicking the link forces them to open their default mail program on their device, and that can be frustrating. What if they're using a public computer? What if they're not logged in? What if they simply don't want to use their mail program?<br>Yes, technically they can just grab the recipient's email address and send the message via their browser or wherever they're logged in. But those are all extra steps and hurdles that can discourage users from sending their messages and the business might lose potential feedback or opportunities.</li>
</ol>
<p>For this reason, we chose to go with an email form from which the user can simply write in their message and click submit, sending an email to the site's owner without ever leaving the website.</p>
<p>A quick Google search shows that there are third party tools/widgets that you could embed in a website, but most of them are branded and require paid subscription for full customization. </p>
<p>And unless you are using a CMS like WordPress that has a built-in plugin that can do that, that's an inconvenient recurring cost. </p>
<p>I instead chose to code that feature myself so I would have full control.</p>
<p>For the purposes of this guide I will recreate the steps I took to implement that functionality using HTML and AWS services.</p>
<h2 id="heading-the-html-form">The HTML Form</h2>
<p>I'll keep it super simple here and go with a basic HTML form with no CSS, just to test our desired functionality.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Contact Us<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>/&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>/&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Message:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>/&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"result-text"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-61.png" alt="Image" width="600" height="400" loading="lazy">
<em>Nothing fancy to see here...</em></p>
<p>Now we want to handle the submit functionality with JavaScript.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> form = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'form'</span>)
form.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function"><span class="hljs-params">event</span> =&gt;</span> {
  <span class="hljs-comment">// prevent the form submit from refreshing the page</span>
  event.preventDefault()

  <span class="hljs-keyword">const</span> { name, email, message } = event.target
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Name: '</span>, name.value)
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'email: '</span>, email.value)
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message: '</span>, message.value)

})
</code></pre>
<p>At this point, we have a form that gets input from the user and JavaScript code that just displays the results to the console. </p>
<p>We can leave it at that for now and start working on the backend services that will receive the form data and send an email with that data.</p>
<h2 id="heading-the-backend-overview">The Backend Overview</h2>
<p>Let's dive into AWS and what services we are going to use and how. </p>
<p>As mentioned in the title, we will use <strong>AWS Lambda</strong> and <strong>Simple Email Service</strong> (SES). SES is a serverless messaging service that allows you to send email messages when invoked. AWS Lambda allows you to write server-side code to execute in response to events. </p>
<p>We will also use <strong>API Gateway</strong> which enables us to invoke Lambda functions via HTTP.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-62.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In this case, when our form is submitted, the following workflow will happen:</p>
<ol>
<li>Our browser (JavaScript) will make a post request, with the form data in the request body, to an endpoint URL specified by AWS API Gateway</li>
<li>The API Gateway will validate this request. Then it will trigger the Lambda function which accepts an event parameter. API Gateway will put the form data in the body property of the event parameter.</li>
<li>Our Lambda function will extract the data from the event body and we will then use this data to build the body of the email we want to send as well as its recipients. Our function will then use the AWS SDK to invoke SES with the email data.</li>
<li>Once SES gets the <em>sendMail</em> request, it turns the email data into an actual text email and sends it to the recipient via AWS's own mail servers.</li>
</ol>
<p>Once the email is sent, our browser will receive a response with status code 200 and a success message. If any step in the AWS cloud fails, the response will have a 500 status code.</p>
<h2 id="heading-step-1-how-to-set-up-ses">Step 1: How to Set Up SES</h2>
<p>We're actually going to set up each one of these steps in the reverse order, beginning with SES, which is going to be easier.</p>
<p>First in your AWS console, go to the SES service —&gt; then click on Email Addresses in the side menu —&gt; then click on the "Verify a New Email Address" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-63.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the dialogue that opens up, enter the email address that you want the SES service to put as the <em>sender</em> when it sends the email.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-64.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This will send an email to the email address you put with a link to click to verify. This is how AWS knows that the owner of the email consents to having their email address used as the sender address.</p>
<p>Until you verify the email, the SES email dashboard will keep the verification status as pending.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-65.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once the email owner opens the email they received from AWS and clicks the verification link in it, the verification status should change to verified (refresh the page to see the change).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-66.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And that's all you have to do for SES. You can optionally test the service by selecting your verified email in the list and clicking the "Send a Test Email" button. This will let you put in a recipient's email address, a subject, and a message and send it.</p>
<p>The email sent is going to be signed by AWS servers and your verified address should be the sender. It should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-67.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-step-2-how-to-set-up-lambda">Step 2: How to Set Up Lambda</h2>
<p>Now this is the most fun part. We are going to create a function that is going to receive the form data and call SES. </p>
<p>The beauty of Lambda functions is that you don't have to worry about running your backend code on a server 24/7 and maintaining that server. It's <em>serverless</em>. </p>
<p>But that doesn't mean there are no servers involved. AWS is going to take care of that under the hood so you can only focus on writing code, not maintaining servers. Additionally, you only get billed for the number of times your function gets called and the amount of time it takes to execute, and it's <a target="_blank" href="https://aws.amazon.com/lambda/pricing/">incredibly cheap</a>!</p>
<h3 id="heading-create-an-iam-role-and-configure-it">Create an IAM Role and Configure it</h3>
<p>Before we start writing our lambda function, we need to create an IAM <em>role</em> to attach it to the function and grant it permissions (referred to as policies in AWS) to invoke the SES service.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-68.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From your AWS console, go to the IAM service —&gt; click on Policies in the side menu —&gt; then click on the "Create Policy" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-69.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the policy creation page, go to the JSON tab and paste the following permissions, then click Next.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"VisualEditor0"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"ses:SendEmail"</span>,
                <span class="hljs-string">"ses:SendRawEmail"</span>
            ],
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"*"</span>
        }
    ]
}
</code></pre>
<p>In the third screen, name the policy and click the "Create Policy" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-70.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now we create an IAM <em>role</em> which will be attached to the lambda and link it to the permissions policy which we just created.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-71.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From the IAM side menu, click Roles then click the "Create role" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-72.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the role creation screen, make sure the type selected is "AWS service" and select the Lambda case then click on the "Next:Permissions" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-73.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On the next screen, search for the policy we created earlier by its name and select it, then click next.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-74.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On the review screen, give the role a name you can remember then click on "Create role".</p>
<p>Now we can create a new lambda function. Go to the Lambda service dashboard and click the "Create Function" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-75.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the function creation screen, name your function, select the "Author from scratch" option, and choose Node.js as the runtime. </p>
<p>Under "Change default execution role" choose the "Use an existing role" option then choose the name of the role you created in the previous step from the "Existing role" drop down. </p>
<p>Finally, click the "Create function" button to create the function.</p>
<h3 id="heading-write-the-code-and-test-it">Write the Code and Test it</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-76.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the editor, open the index.js file (this is the file that will be executed when your lambda is called), and replace its content with the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> aws = <span class="hljs-built_in">require</span>(<span class="hljs-string">"aws-sdk"</span>);
<span class="hljs-keyword">const</span> ses = <span class="hljs-keyword">new</span> aws.SES({ <span class="hljs-attr">region</span>: <span class="hljs-string">"us-east-1"</span> });
<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'EVENT: '</span>, event)
  <span class="hljs-keyword">const</span> params = {
    <span class="hljs-attr">Destination</span>: {
      <span class="hljs-attr">ToAddresses</span>: [<span class="hljs-string">"your@email.com"</span>],
    },
    <span class="hljs-attr">Message</span>: {
      <span class="hljs-attr">Body</span>: {
        <span class="hljs-attr">Text</span>: { 
            <span class="hljs-attr">Data</span>: <span class="hljs-string">`Hello from Lambda!`</span> 
        },
      },
      <span class="hljs-attr">Subject</span>: { <span class="hljs-attr">Data</span>: <span class="hljs-string">`Message from AWS Lambda`</span> },
    },
    <span class="hljs-attr">Source</span>: <span class="hljs-string">"your@email.com"</span>,
  };

  <span class="hljs-keyword">return</span> ses.sendEmail(params).promise()
};
</code></pre>
<p>Notice that on line 2 we are using the AWS SDK and creating an SES instance. The reason I chose <strong>us-east-1</strong> as the region is because that's <em>where I registered &amp; verified my email</em>. Be sure to replace the email and use the AWS region where you registered your email.</p>
<p>Now to test this function, click on the "Deploy" button. Then click on the Test button —&gt; Configure test event which should open up a test configuration dialogue where you can create a new test event.  </p>
<p>In the test event body editor, enter the following JSON which mimics what will eventually come from our browser request. Then click create.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"body"</span>: {
        <span class="hljs-attr">"senderName"</span>: <span class="hljs-string">"Namo"</span>,
        <span class="hljs-attr">"senderEmail"</span>: <span class="hljs-string">"namo@trains.com"</span>,
        <span class="hljs-attr">"message"</span>: <span class="hljs-string">"I love trains!"</span>
    }
}
</code></pre>
<p>Now clicking the test button will run the test we just created. It should open a new tab in the editor to show us the logs created from running the function, which should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-77.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Notice the event object we logged out shows here under Function logs with the body data we used in the test event.</p>
<p>This test should have sent an email to my inbox as well – let's see if that happened.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-78.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Yep, just as expected. And that happened almost immediately after running the test.</p>
<p>Now let's modify our function code to get a more meaningful message from the test data.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> aws = <span class="hljs-built_in">require</span>(<span class="hljs-string">"aws-sdk"</span>);
<span class="hljs-keyword">const</span> ses = <span class="hljs-keyword">new</span> aws.SES({ <span class="hljs-attr">region</span>: <span class="hljs-string">"us-east-1"</span> });
<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'EVENT: '</span>, event)
    <span class="hljs-comment">// Extract the properties from the event body</span>
  <span class="hljs-keyword">const</span> { senderEmail, senderName, message } = <span class="hljs-built_in">JSON</span>.parse(event.body)
  <span class="hljs-keyword">const</span> params = {
    <span class="hljs-attr">Destination</span>: {
      <span class="hljs-attr">ToAddresses</span>: [<span class="hljs-string">"the.benhawy@gmail.com"</span>],
    },
        <span class="hljs-comment">// Interpolate the data in the strings to send</span>
    <span class="hljs-attr">Message</span>: {
      <span class="hljs-attr">Body</span>: {
        <span class="hljs-attr">Text</span>: { 
            <span class="hljs-attr">Data</span>: <span class="hljs-string">`You just got a message from <span class="hljs-subst">${senderName}</span> - <span class="hljs-subst">${senderEmail}</span>:
            <span class="hljs-subst">${message}</span>`</span> 
        },
      },
      <span class="hljs-attr">Subject</span>: { <span class="hljs-attr">Data</span>: <span class="hljs-string">`Message from <span class="hljs-subst">${senderName}</span>`</span> },
    },
    <span class="hljs-attr">Source</span>: <span class="hljs-string">"the.benhawy@gmail.com"</span>,
  };

  <span class="hljs-keyword">return</span> ses.sendEmail(params).promise();
};
</code></pre>
<p>It's important to note that when API Gateway calls our function it will pass a string to the event body. This is why I use <code>JSON.parse</code> on event.body, to turn it into JSON and extract our sender's email, name, and message. Then I use those variables in the email body text and subject using string interpolation.</p>
<p>If you try the test it, the code will return an error. This is because the test is passing a JSON object to event.body and we are using JSON.parse on JSON, which causes an error in JavaScript. </p>
<p>Sadly, the test editor doesn't allow us to pass strings to the event, so we'll have to test that later from somewhere else.</p>
<h2 id="heading-step-3-how-to-set-up-api-gateway">Step 3: How to Set Up API Gateway</h2>
<p>Next, the last AWS service we are going to use is API Gateway, which will enable our browser to send HTTP requests to the Lambda function we created.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-79.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Without leaving your lambda function page, expand the "Function overview" section and click on "Add trigger".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-80.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, choose API Gateway from the dropdown, HTTP API as the API type, "Open" as the security mechanism, and check the CORS checkbox option. Then click "Add".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-81.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You should be redirected to the "Configuration" tab of your function, showing you the new API Gateway trigger you just created. From there, note the <strong>API endpoint</strong>. This is the URL we are going to be calling from our browser with the form data.</p>
<h2 id="heading-back-to-the-html">Back to the HTML</h2>
<p>We can finally test the form to see if it sends emails or not.</p>
<p>Let's modify our JavaScript to handle sending the request when the form is submitted.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> form = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"form"</span>);
form.addEventListener(<span class="hljs-string">"submit"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-comment">// prevent the form submit from refreshing the page</span>
  event.preventDefault();

  <span class="hljs-keyword">const</span> { name, email, message } = event.target;

    <span class="hljs-comment">// Use your API endpoint URL you copied from the previous step</span>
  <span class="hljs-keyword">const</span> endpoint =
    <span class="hljs-string">"&lt;https://5ntvcwwmec.execute-api.us-east-1.amazonaws.com/default/sendContactEmail&gt;"</span>;
  <span class="hljs-comment">// We use JSON.stringify here so the data can be sent as a string via HTTP</span>
    <span class="hljs-keyword">const</span> body = <span class="hljs-built_in">JSON</span>.stringify({
    <span class="hljs-attr">senderName</span>: name.value,
    <span class="hljs-attr">senderEmail</span>: email.value,
    <span class="hljs-attr">message</span>: message.value
  });
  <span class="hljs-keyword">const</span> requestOptions = {
    <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
    body
  };

  fetch(endpoint, requestOptions)
    .then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (!response.ok) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Error in fetch"</span>);
      <span class="hljs-keyword">return</span> response.json();
    })
    .then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> {
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"result-text"</span>).innerText =
        <span class="hljs-string">"Email sent successfully!"</span>;
    })
    .catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
      <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"result-text"</span>).innerText =
        <span class="hljs-string">"An unkown error occured."</span>;
    });
});
</code></pre>
<p>Now, the moment of truth: fill in the form and click submit. If you see the success message, that means the email was sent.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-82.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Since I own the email the message was sent to, I take a quick look at my inbox to see that I received an email from myself with the details I used in the form!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-83.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you've followed along, you now have a functioning "Contact Us" form that you can plug into any website. And you'll only get billed for when it is actually used. </p>
<p>I don't know about you, but I think this is pretty awesome and almost magical! And it's a nice, practical way to use cloud computing/services in your workflow. </p>
<p>Of course you can customize this flow in terms of using a framework on the frontend like React or Vue or a different programming language for the Lambda like Python or Go.</p>
<h2 id="heading-before-you-go">Before you go...</h2>
<p>Thank you for reading this far! I write posts about JavaScript, cloud development, and my personal educational &amp; professional experiences as a self-taught developer. So feel free to follow me on twitter <a target="_blank" href="https://twitter.com/adham_benhawy">@adham_benhawy</a> where I tweet about them too!</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><a target="_blank" href="https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/">https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/</a></li>
<li><a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html">https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html</a></li>
<li><a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html?icmpid=docs_lambda_console">https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html?icmpid=docs_lambda_console</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Screenshot Capture API Using Terraform, AWS API Gateway, and AWS Lambda ]]>
                </title>
                <description>
                    <![CDATA[ By Aaron Katz Recently, I really wanted to find a way to build an API that would take in a URL and save a screenshot.  My initial use case was simple: if I was analyzing phishing emails, I wanted an easy way to get a screenshot of the URL ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-screenshot-capture-api-using-terraform-aws-api-gateway-and-aws-lambda/</link>
                <guid isPermaLink="false">66d45d593a8352b6c5a2a9e7</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Google Chrome ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Terraform ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 23 Nov 2020 15:49:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c95b1740569d1a4ca0e07.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Aaron Katz</p>
<p>Recently, I really wanted to find a way to build an API that would take in a URL and save a screenshot. </p>
<p>My initial use case was simple: if I was analyzing phishing emails, I wanted an easy way to get a screenshot of the URL that the email was trying to direct their targets to.</p>
<p>To build this, I used <a target="_blank" href="https://www.terraform.io/">Terraform</a> to create all of the infrastructure necessary to set it up in AWS, using Selenium, chromedriver, and headless Chrome to obtain the screenshots.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot-Service.png" alt="High level diagram illustrating what will be built in AWS" width="600" height="400" loading="lazy">
<em>High level diagram illustrating what will be built in AWS by Terraform</em></p>
<p><em>Note: All code samples are from PowerShell, so please excuse the ".\" notation.</em></p>
<h3 id="heading-requirements">Requirements</h3>
<ul>
<li>An AWS account</li>
<li>Terraform binary</li>
<li>Existing S3 bucket to store Terraform state (<a target="_blank" href="https://www.terraform.io/docs/backends/types/s3.html">https://www.terraform.io/docs/backends/types/s3.html</a>)</li>
<li>AWS IAM user and access key created with appropriate permissions (programmatic access, administrative group) for Terraform usage</li>
</ul>
<h2 id="heading-how-to-set-up-the-project">How to Set Up the Project</h2>
<p>Create your new directory and initialize Terraform like this:</p>
<pre><code class="lang-powershell">mkdir .\screenshot<span class="hljs-literal">-service</span>
<span class="hljs-built_in">cd</span> .\screenshot<span class="hljs-literal">-service</span>
.\terraform init
</code></pre>
<h2 id="heading-configure-the-aws-provider">Configure the AWS Provider</h2>
<p>Create a file called <code>provider.tf</code> in the root of your project directory. Then configure with appropriate values for the AWS access key and secret key, as well as the name of an existing S3 bucket that will be used to store the Terraform state file.</p>
<pre><code class="lang-terraform">provider "aws" {
  region = "us-east-1"

  access_key = "ACCESSKEY"
  secret_key = "SECRETKEY"
}

terraform {
  backend "s3" {
    bucket = "EXISTING_BUCKET"
    region = "us-east-1"
    key = "KEYFORSTATE"
    access_key = "ACCESSKEY"
    secret_key = "SECRETKEY"
    encrypt = "true"
  }
}
</code></pre>
<h2 id="heading-configure-the-s3-bucket">Configure the S3 Bucket</h2>
<p>We will be using an S3 bucket to store all of our screenshots. To configure the S3 service, create a new file in the root of your project called <code>s3.tf</code> and add the following:</p>
<pre><code class="lang-terraform">resource "aws_s3_bucket" "screenshot_bucket" {
  bucket        = "STORAGE_BUCKET_NAME"
  force_destroy = true
  acl = "public-read"

  versioning {
    enabled = false
  }
}
</code></pre>
<h2 id="heading-create-the-lambda-layer">Create the Lambda Layer</h2>
<p>Let's start by creating the lambda layer which will contain the necessary binaries. First, from the root of the project, create a folder called chromedriver_layer: <code>mkdir .\chromedriver_layer</code>.</p>
<p>Next, download the chromedriver and chromium binaries:</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">cd</span> .\chromedriver_layer
<span class="hljs-built_in">wget</span> https://chromedriver.storage.googleapis.com/<span class="hljs-number">2.41</span>/chromedriver_linux64.zip <span class="hljs-literal">-OutFile</span> .\chromedriver.zip
<span class="hljs-built_in">wget</span> https://github.com/adieuadieu/serverless<span class="hljs-literal">-chrome</span>/releases/download/v1.<span class="hljs-number">0.0</span><span class="hljs-literal">-54</span>/stable<span class="hljs-literal">-headless</span><span class="hljs-literal">-chromium</span><span class="hljs-literal">-amazonlinux</span><span class="hljs-literal">-2017</span><span class="hljs-literal">-03</span>.zip <span class="hljs-literal">-OutFile</span> .\headless<span class="hljs-literal">-chromium</span>.zip
<span class="hljs-built_in">Expand-Archive</span> .\headless<span class="hljs-literal">-chromium</span>.zip
<span class="hljs-built_in">rm</span> *.zip
</code></pre>
<p>Last, we need to zip this up nicely for Terraform:</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">cd</span> ..\
<span class="hljs-built_in">Compress-Archive</span> .\chromedriver_layer <span class="hljs-literal">-DestinationPath</span> \chromedriver_layer.zip
</code></pre>
<h2 id="heading-how-to-configure-lambda">How to Configure Lambda</h2>
<h3 id="heading-lambda-infrastructure">Lambda Infrastructure</h3>
<p>Create a file called <code>lambda.tf</code> in the root of your project directory. First, we will create the execution role required for our function:</p>
<pre><code class="lang-terraform">resource "aws_iam_role" "lambda_exec_role" {
  name        = "lambda_exec_role"
  description = "Execution role for Lambda functions"

  assume_role_policy = &lt;&lt;EOF
{
        "Version"  : "2012-10-17",
        "Statement": [
            {
                "Action"   : "sts:AssumeRole",
                "Principal": {  
                    "Service": "lambda.amazonaws.com"
                },
                "Effect": "Allow",
                "Sid"   : ""
            }
        ]
}
EOF
}
</code></pre>
<p>Next, we will add a few policies to the execution role we have created that will enable our function to access required services:</p>
<pre><code class="lang-terraform">resource "aws_iam_role_policy" "lambda_logging" {
  name = "lambda_logging"

  role = aws_iam_role.lambda_exec_role.id

  policy = &lt;&lt;EOF
{
    "Version"  : "2012-10-17",
    "Statement": [
        {
            "Effect"  : "Allow",
            "Resource": "*",
            "Action"  : [
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "logs:CreateLogGroup"
            ]
        }
    ]
}
EOF
}

resource "aws_iam_role_policy" "lambda_s3_access" {
  name = "lambda_s3_access"

  role = aws_iam_role.lambda_exec_role.id

  # TODO: Change resource to be more restrictive
  policy = &lt;&lt;EOF
{
  "Version"  : "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBuckets",
        "s3:PutObject",
        "s3:PutObjectAcl",
        "s3:GetObjectAcl"
      ],
      "Resource": ["*"]
    }
  ]
}
EOF
}
</code></pre>
<p>There, now our function will be able to access S3 and log to CloudWatch. Let's define our function:</p>
<pre><code class="lang-terraform">resource "aws_lambda_function" "take_screenshot" {
  filename      = "./screenshot-service.zip"
  function_name = "take_screenshot"
  role          = aws_iam_role.lambda_exec_role.arn
  handler       = "screenshot-service.handler"
  runtime       = "python3.7"

  source_code_hash = filebase64sha256("./screenshot-service.zip")
  timeout          = 600
  memory_size      = 512 
  layers = ["${aws_lambda_layer_version.chromedriver_layer.arn}"]

  environment {
    variables = {
      s3_bucket = "${aws_s3_bucket.screenshot_bucket.bucket}"
    }
  }
}
</code></pre>
<p>The above code specifies that we are uploading a lambda function package using a Python 3.7 runtime, and that the function that will be called is named "handler".  </p>
<p>I have set the timeout to 600 seconds, but feel free to change that as you will. Also, feel free to play with the memory_size – for me, this has led to super quick screenshots.</p>
<p>We also set an environment variable called <strong>s3_bucket</strong> which will be passed to the function, containing the name of the bucket used to store the screenshot.</p>
<h3 id="heading-the-lambda-function-itself">The Lambda function itself</h3>
<p>Create a folder called <code>lambda</code> in the root of the project directory and create a file called <code>screenshot-service.py</code> in that folder.</p>
<p>Add the following imports and logging configuration to the file:</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/env python</span>
<span class="hljs-comment"># -*- coding utf-8 -*-</span>

<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">from</span> urllib.parse <span class="hljs-keyword">import</span> urlparse, unquote <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> Can I use urllib3?</span>
<span class="hljs-keyword">from</span> selenium <span class="hljs-keyword">import</span> webdriver
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> shutil <span class="hljs-keyword">import</span> copyfile
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> stat
<span class="hljs-keyword">import</span> urllib.request
<span class="hljs-keyword">import</span> tldextract

<span class="hljs-comment"># Configure logging</span>
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
</code></pre>
<p>Next, we will create a function that will copy the binaries from our lambda layer and make them executable:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">configure_binaries</span>():</span>
    <span class="hljs-string">"""Copy the binary files from the lambda layer to /tmp and make them executable"""</span>
    copyfile(<span class="hljs-string">"/opt/chromedriver"</span>, <span class="hljs-string">"/tmp/chromedriver"</span>)
    copyfile(<span class="hljs-string">"/opt/headless-chromium"</span>, <span class="hljs-string">"/tmp/headless-chromium"</span>)

    os.chmod(<span class="hljs-string">"/tmp/chromedriver"</span>, <span class="hljs-number">755</span>)
    os.chmod(<span class="hljs-string">"/tmp/headless-chromium"</span>, <span class="hljs-number">755</span>)
</code></pre>
<p>Next, we will create the function that will take the screenshot of the provided domain. We will be passing in the URL and the S3 bucket name.  </p>
<p>We will add an optional parameter allowing for the title of the image to be set by the user. The screenshot is taken by Selenium automating the headless Chrome browser we downloaded.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_screenshot</span>(<span class="hljs-params">url, s3_bucket, screenshot_title = None</span>):</span>     
    configure_binaries()

    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument(<span class="hljs-string">'--headless'</span>)
    chrome_options.add_argument(<span class="hljs-string">"disable-infobars"</span>)
    chrome_options.add_argument(<span class="hljs-string">"enable-automation"</span>)

    chrome_options.add_argument(<span class="hljs-string">'--no-sandbox'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--disable-gpu'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--window-size=1280x1696'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--user-data-dir=/tmp/user-data'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--hide-scrollbars'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--enable-logging'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--log-level=0'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--disable-dev-shm-usage'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--v=99'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--single-process'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--data-path=/tmp/data-path'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--ignore-certificate-errors'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--homedir=/tmp'</span>)
    chrome_options.add_argument(<span class="hljs-string">'--disk-cache-dir=/tmp/cache-dir'</span>)
    chrome_options.add_argument(
        <span class="hljs-string">'user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'</span>)
    chrome_options.binary_location = <span class="hljs-string">"/tmp/headless-chromium"</span>

    <span class="hljs-keyword">if</span> screenshot_title <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>: 
        ext = tldextract.extract(url)
        domain = <span class="hljs-string">f"<span class="hljs-subst">{<span class="hljs-string">''</span>.join(ext[:<span class="hljs-number">2</span>])}</span>:<span class="hljs-subst">{urlparse(url).port}</span>.<span class="hljs-subst">{ext[<span class="hljs-number">2</span>]}</span>"</span>
        screenshot_title = <span class="hljs-string">f"<span class="hljs-subst">{domain}</span>_<span class="hljs-subst">{datetime.utcnow().strftime(<span class="hljs-string">'%Y%m%d_%H%M%S'</span>)}</span>"</span>
    logger.debug(<span class="hljs-string">f"Screenshot title: <span class="hljs-subst">{screenshot_title}</span>"</span>)

    <span class="hljs-keyword">with</span> webdriver.Chrome(chrome_options=chrome_options, executable_path=<span class="hljs-string">"/tmp/chromedriver"</span>, service_log_path=<span class="hljs-string">"/tmp/selenium.log"</span>) <span class="hljs-keyword">as</span> driver: 
        driver.set_window_size(<span class="hljs-number">1024</span>, <span class="hljs-number">768</span>)

        logger.info(<span class="hljs-string">f"Obtaining screenshot for <span class="hljs-subst">{url}</span>"</span>)
        driver.get(url)     

        driver.save_screenshot(<span class="hljs-string">f"/tmp/<span class="hljs-subst">{screenshot_title}</span>.png"</span>) <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> Delete the screenshot after</span>
        logger.info(<span class="hljs-string">f"Uploading /tmp/<span class="hljs-subst">{screenshot_title}</span>.png to S3 bucket <span class="hljs-subst">{s3_bucket}</span>/<span class="hljs-subst">{screenshot_title}</span>.png"</span>)
        s3 = boto3.client(<span class="hljs-string">"s3"</span>)
        s3.upload_file(<span class="hljs-string">f"/tmp/<span class="hljs-subst">{screenshot_title}</span>.png"</span>, s3_bucket, <span class="hljs-string">f"<span class="hljs-subst">{screenshot_title}</span>.png"</span>, ExtraArgs={<span class="hljs-string">'ContentType'</span>: <span class="hljs-string">'image/png'</span>, <span class="hljs-string">'ACL'</span>: <span class="hljs-string">'public-read'</span>})
    <span class="hljs-keyword">return</span> <span class="hljs-string">f"https://<span class="hljs-subst">{s3_bucket}</span>.s3.amazonaws.com/<span class="hljs-subst">{screenshot_title}</span>.png"</span>
</code></pre>
<p>Last, let's create our handler, which will be invoked when the API Gateway receives a legitimate request:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handler</span>(<span class="hljs-params">event, context</span>):</span> 
    logger.debug(<span class="hljs-string">"## ENVIRONMENT VARIABLES ##"</span>)
    logger.debug(os.environ)
    logger.debug(<span class="hljs-string">"## EVENT ##"</span>)
    logger.debug(event)

    bucket_name = os.environ[<span class="hljs-string">"s3_bucket"</span>]
    logger.debug(<span class="hljs-string">f"bucket_name: <span class="hljs-subst">{bucket_name}</span>"</span>)

    logger.info(<span class="hljs-string">"Validating url"</span>)  

    <span class="hljs-keyword">if</span> event[<span class="hljs-string">"httpMethod"</span>] == <span class="hljs-string">"GET"</span>:
        <span class="hljs-keyword">if</span> event[<span class="hljs-string">"queryStringParameters"</span>]:
            <span class="hljs-keyword">try</span>:
                url = event[<span class="hljs-string">"queryStringParameters"</span>][<span class="hljs-string">"url"</span>]
            <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
                logger.error(e)
                <span class="hljs-keyword">raise</span> e
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> {
                <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">400</span>,
                <span class="hljs-string">"body"</span>: json.dumps(<span class="hljs-string">"No URL provided..."</span>)
            }
    <span class="hljs-keyword">elif</span> event[<span class="hljs-string">"httpMethod"</span>] == <span class="hljs-string">"POST"</span>:
        <span class="hljs-keyword">if</span> event[<span class="hljs-string">"body"</span>]:
            <span class="hljs-keyword">try</span>:
                body = json.loads(event[<span class="hljs-string">"body"</span>])
                url = body[<span class="hljs-string">"url"</span>]
            <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
                logger.error(e)
                <span class="hljs-keyword">raise</span> e
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> {
                <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">400</span>,
                <span class="hljs-string">"body"</span>: json.dumps(<span class="hljs-string">"No URL provided..."</span>)
            }
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">405</span>,
            <span class="hljs-string">"body"</span>: json.dumps(<span class="hljs-string">f"Invalid HTTP Method <span class="hljs-subst">{event[<span class="hljs-string">'httpMethod'</span>]}</span> supplied"</span>)
        }

    logger.info(<span class="hljs-string">f"Decoding <span class="hljs-subst">{url}</span>"</span>)
    url = unquote(url)

    logger.info(<span class="hljs-string">f"Parsing <span class="hljs-subst">{url}</span>"</span>)
    <span class="hljs-keyword">try</span>: 
        parsed_url = urlparse(url)
        <span class="hljs-keyword">if</span> parsed_url.scheme != <span class="hljs-string">"http"</span> <span class="hljs-keyword">and</span> parsed_url.scheme != <span class="hljs-string">"https"</span>:
            logger.info(<span class="hljs-string">"No valid scheme found, defaulting to http://"</span>)
            parsed_url = urlparse(<span class="hljs-string">f"http://<span class="hljs-subst">{url}</span>"</span>)
        <span class="hljs-keyword">if</span> parsed_url.port <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
            <span class="hljs-keyword">if</span> parsed_url.scheme == <span class="hljs-string">"http"</span>:
                parsed_url = urlparse(<span class="hljs-string">f"<span class="hljs-subst">{parsed_url.geturl()}</span>:80"</span>)
            <span class="hljs-keyword">elif</span> parsed_url.scheme == <span class="hljs-string">"https"</span>:
                parsed_url = urlparse(<span class="hljs-string">f"<span class="hljs-subst">{parsed_url.geturl()}</span>:443"</span>)

    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e: 
        logger.error(e)
        <span class="hljs-keyword">raise</span> e

    logger.info(<span class="hljs-string">"Getting screenshot"</span>)
    <span class="hljs-keyword">try</span>: 
        screenshot_url = get_screenshot(parsed_url.geturl(), bucket_name) <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> Variable!</span>
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:  
        logger.error(e)
        <span class="hljs-keyword">raise</span> e

    response_body = {
        <span class="hljs-string">"message"</span>: <span class="hljs-string">f"Successfully captured screenshot of <span class="hljs-subst">{parsed_url.geturl()}</span>"</span>,
        <span class="hljs-string">"screenshot_url"</span>: screenshot_url
    }

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">"body"</span>      : json.dumps(response_body)
    }
</code></pre>
<p>Next, we need to install all packages that the lambda function uses into the <code>lambda</code> directory since these packages are not installed by default in AWS. </p>
<p>Then we need to create the zip archive (once created, Terraform will continue to update it if you make changes to your code):</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">cd</span> .\lambda
pip install selenium tldextract <span class="hljs-literal">-t</span> .\
<span class="hljs-built_in">cd</span> ..\
<span class="hljs-built_in">Compress-Archive</span> .\lambda <span class="hljs-literal">-DestinationPath</span> .\screenshot<span class="hljs-literal">-service</span>.zip
</code></pre>
<h2 id="heading-how-to-configure-api-gateway">How to Configure API Gateway</h2>
<p>Create a file called <code>apigw.tf</code> in the root of your project directory. First, we will configure the REST API:</p>
<pre><code class="lang-terraform">resource "aws_api_gateway_rest_api" "screenshot_api" {
  name        = "screenshot_api"
  description = "Lambda-powered screenshot API"
  depends_on = [
    aws_lambda_function.take_screenshot
  ]
}
</code></pre>
<p>This API will be used to direct all requests that are made for the screenshot service. We use the <code>depends_on</code> feature to ensure that the gateway and its related components are only created <em>after</em> the lambda function is created.  </p>
<p>Next, let's create the API Gateway resource for the lambda function:</p>
<pre><code class="lang-terraform">resource "aws_api_gateway_resource" "screenshot_api_gateway" {
  path_part   = "screenshot"
  parent_id   = aws_api_gateway_rest_api.screenshot_api.root_resource_id
  rest_api_id = aws_api_gateway_rest_api.screenshot_api.id
}
</code></pre>
<p>We have now defined a resource that will respond at the <code>/screenshot</code> endpoint for the API service.</p>
<p>Next up we will create a stage for the API. A stage is a fancy way of naming our deployment of the API. You can configure caching, logging, request throttling, and more using a stage.</p>
<pre><code class="lang-terraform">resource "aws_api_gateway_stage" "prod_stage" {
  stage_name = "prod"
  rest_api_id = aws_api_gateway_rest_api.screenshot_api.id
  deployment_id = aws_api_gateway_deployment.api_gateway_deployment_get.id
}
</code></pre>
<p>Next we will create an API key and usage plan tied to our stage, so that only users with knowledge of the key can use this service. (<em>Note:</em> If you want this publicly accessible, skip this step.)</p>
<pre><code class="lang-terraform">resource "aws_api_gateway_usage_plan" "apigw_usage_plan" {
  name = "apigw_usage_plan"

  api_stages {
    api_id = aws_api_gateway_rest_api.screenshot_api.id
    stage = aws_api_gateway_stage.prod_stage.stage_name
  }
}

resource "aws_api_gateway_usage_plan_key" "apigw_usage_plan_key" {
  key_id = aws_api_gateway_api_key.apigw_prod_key.id
  key_type = "API_KEY"
  usage_plan_id = aws_api_gateway_usage_plan.apigw_usage_plan.id
}

resource "aws_api_gateway_api_key" "apigw_prod_key" {
  name = "prod_key"
}
</code></pre>
<p>Let's now configure the API to respond to either a <strong>GET</strong> or <strong>POST</strong> request if a valid API Gateway Key is provided (set the value to false if you want the method open to the public):</p>
<pre><code class="lang-terraform">resource "aws_api_gateway_method" "take_screenshot_get" {
  rest_api_id   = aws_api_gateway_rest_api.screenshot_api.id
  resource_id   = aws_api_gateway_resource.screenshot_api_gateway.id
  http_method   = "GET"
  authorization = "NONE"
  api_key_required = true
}

resource "aws_api_gateway_method" "take_screenshot_post" {
  rest_api_id   = aws_api_gateway_rest_api.screenshot_api.id
  resource_id   = aws_api_gateway_resource.screenshot_api_gateway.id
  http_method   = "POST"
  authorization = "NONE"
  api_key_required = true
}
</code></pre>
<p>We now need to give the API Gateway permission to invoke the lambda function we created:</p>
<pre><code class="lang-terraform">resource "aws_lambda_permission" "apigw" {
  statement_id  = "AllowAPIGatewayInvoke"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.take_screenshot.arn
  principal     = "apigateway.amazonaws.com"

  source_arn = "${aws_api_gateway_rest_api.screenshot_api.execution_arn}/*/*/*"
}
</code></pre>
<p>Great, we now have the appropriate permissions. Let's set up our integration with the lambda function:</p>
<pre><code class="lang-terraform">resource "aws_api_gateway_integration" "lambda_integration_get" {
  depends_on = [
    aws_lambda_permission.apigw
  ]
  rest_api_id = aws_api_gateway_rest_api.screenshot_api.id
  resource_id = aws_api_gateway_method.take_screenshot_get.resource_id
  http_method = aws_api_gateway_method.take_screenshot_get.http_method

  integration_http_method = "POST" # https://github.com/hashicorp/terraform/issues/9271 Lambda requires POST as the integration type
  type                    = "AWS_PROXY"
  uri                     = aws_lambda_function.take_screenshot.invoke_arn
}

resource "aws_api_gateway_integration" "lambda_integration_post" {
  depends_on = [
    aws_lambda_permission.apigw
  ]
  rest_api_id = aws_api_gateway_rest_api.screenshot_api.id
  resource_id = aws_api_gateway_method.take_screenshot_post.resource_id
  http_method = aws_api_gateway_method.take_screenshot_post.http_method

  integration_http_method = "POST" # https://github.com/hashicorp/terraform/issues/9271 Lambda requires POST as the integration type
  type                    = "AWS_PROXY"
  uri                     = aws_lambda_function.take_screenshot.invoke_arn
}
</code></pre>
<p>This integration tells the API Gateway what lambda function to invoke when it receives a request at the specified endpoint and HTTP method.</p>
<p>Almost done with the gateway, I promise. As a last step, let's make sure that our API can send logs to CloudWatch:</p>
<pre><code class="lang-terraform">resource "aws_api_gateway_account" "apigw_account" {
  cloudwatch_role_arn = aws_iam_role.apigw_cloudwatch.arn
}

resource "aws_iam_role" "apigw_cloudwatch" {
  # https://gist.github.com/edonosotti/6e826a70c2712d024b730f61d8b8edfc
  name = "api_gateway_cloudwatch_global"

  assume_role_policy = &lt;&lt;EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "apigw_cloudwatch" {
  name = "default"
  role = aws_iam_role.apigw_cloudwatch.id

  policy = &lt;&lt;EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "logs:PutLogEvents",
                "logs:GetLogEvents",
                "logs:FilterLogEvents"
            ],
            "Resource": "*"
        }
    ]
}
EOF
}
</code></pre>
<p>We have now given API Gateway the requisite permissions in order to write logs to CloudWatch.  </p>
<p>Last but not least, we deploy our API. We use <code>depends_on</code> to ensure that the deployment occurs after all dependencies are created.</p>
<pre><code class="lang-terraform">resource "aws_api_gateway_deployment" "api_gateway_deployment_get" {
  depends_on = [aws_api_gateway_integration.lambda_integration_get,  aws_api_gateway_method.take_screenshot_get, aws_api_gateway_integration.lambda_integration_post, aws_api_gateway_method.take_screenshot_post]

  rest_api_id = aws_api_gateway_rest_api.screenshot_api.id
}
</code></pre>
<h2 id="heading-lambda-packaging">Lambda packaging</h2>
<p>In <code>main.tf</code>, add the following:</p>
<pre><code class="lang-terraform">data "archive_file" "screenshot_service_zip" {
  type        = "zip"
  source_dir  = "./lambda"
  output_path = "./screenshot-service.zip"
}

data "archive_file" "screenshot_service_layer_zip" {
  type = "zip"
  source_dir = "./chromedriver_layer"
  output_path = "./chromedriver_lambda_layer.zip"
}
</code></pre>
<h2 id="heading-outputs">Outputs</h2>
<p>Create a file called <code>output.tf</code> in the root of your project directory and add the following:</p>
<pre><code class="lang-terraform">output "api_gateway_url" {
  value = "${aws_api_gateway_stage.prod_stage.invoke_url}/${aws_api_gateway_resource.screenshot_api_gateway.path_part}"
}

output "api_key" {
  value = aws_api_gateway_api_key.apigw_prod_key.value
}
</code></pre>
<p>Now once you run <code>.\terraform apply</code> you will get output with the URL of the API and the associated API key.</p>
<p>Congrats! You now have a working screenshot service. To view the code I've used, feel free to check out my <a target="_blank" href="https://github.com/Caliburn-Security/screenshot-service">Github</a> repository.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Reuse Node.js Packages with AWS Lambda Functions Using Amplify and Lambda Layers ]]>
                </title>
                <description>
                    <![CDATA[ By Erick Wendel In this article, you'll learn how to inject custom packages on AWS Lambda Functions' Runtime by using AWS Lambda Layers. You'll also use Amplify to develop, deploy, and distribute your applications. Serverless applications are great f... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-reuse-packages-with-aws-lambda-functions-using-amplify/</link>
                <guid isPermaLink="false">66d45e3e8812486a37369c95</guid>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 20 Jul 2020 19:33:48 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c99a3740569d1a4ca20e2.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Erick Wendel</p>
<p>In this article, you'll learn how to inject custom packages on AWS Lambda Functions' Runtime by using AWS Lambda Layers. You'll also use Amplify to develop, deploy, and distribute your applications.</p>
<p>Serverless applications are great for those who don't want to pay for idle machines or even manage cloud infrastructures. This post will focus on a gentle introduction to the <strong>Amplify Framework</strong> using <strong>Node.js</strong>. You'll install a custom Node.js package once and inject it on all your functions using <strong>AWS Lambda Layers</strong>.</p>
<p>By the end of this post you will have learned how to deploy serverless functions on AWS by:</p>
<ul>
<li>Creating a Node.js Web API using Express.js as a serverless function using the <strong>Amplify Framework</strong></li>
<li>Injecting a custom package on AWS Lambda functions' runtime using AWS Lambda Layers to monitor and extend HTTP requests.</li>
</ul>
<h2 id="heading-requirements"><strong>Requirements</strong></h2>
<p>In the next steps, you'll create a real application and publish it on the AWS Infrastructure. Before starting to code make sure you have the following requirements set up on your environment:</p>
<ul>
<li>An AWS Active Account</li>
<li><a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html">AWS CLI</a></li>
<li><a target="_blank" href="https://nodejs.org/en/download/current/">Node.js v14</a></li>
</ul>
<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>One of my favorites cloud platforms that provides serverless infrastructure is Amazon Web Services. They have long been developing and shipping platforms that empower companies to release software faster than if they had to configure and handle repetitive configurations. </p>
<p>If you're familiar with Serverless applications, you might have heard about the <a target="_blank" href="https://www.serverless.com/">Serverless Framework</a>. It's a multi-cloud framework for managing serverless architectures by using configuration files, deploying and running apps using a single command. </p>
<p>Even so, developers still need install plugins and manage configuration files by themselves and it could take a while to build a complex workflow.</p>
<p>So, what kind of CLI do you like most? A CLI that would ask you about what you want such as external connections (Database, Storage, Queue, etc), authentication flow, external permissions, and so on? Yeah my friend, welcome to <strong>AWS Amplify.</strong></p>
<h2 id="heading-aws-amplify">AWS Amplify</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/Screen-Shot-2020-07-18-at-00.06.42.png" alt="Image" width="600" height="400" loading="lazy">
<em>Amplify Framework website</em></p>
<p>AWS Amplify is an ecosystem that helps back end, front end, and integration developers. If you take a look at its <a target="_blank" href="https://aws.amazon.com/amplify/framework/">official docs</a>, you'll see the huge list of possible libraries and examples to work on both back end and front and applications.</p>
<p>When you have your AWS CLI configured and your environment set, run the following command to install AWS Amplify globally on your machine:</p>
<p><code>npm install -g amplify-cli</code></p>
<p>Next, let's initialize a work directory by creating a folder:</p>
<p><code>mkdir app &amp;&amp; cd app</code></p>
<p>Now, you'll initialize an amplify project by running the command below. To do so, you'll need to check a few options on the CLI wizard. Notice that when prompted you can press <strong>Enter</strong> on your keyboard to choose default values.</p>
<p><code>amplify init</code></p>
<p>Choose the options according to the <strong>bold</strong> text below:</p>
<ul>
<li>Enter a name for the project <strong>app</strong></li>
<li>Enter a name for the environment <strong>dev</strong></li>
<li>Choose your default editor: <strong>Visual Studio Code</strong></li>
<li>Choose the type of app that you're building <strong>javascript</strong></li>
</ul>
<p>Please tell us about your project</p>
<ul>
<li>What javascript framework are you using <strong>none</strong></li>
<li>Source Directory Path:  <strong>src</strong></li>
<li>Distribution Directory Path: <strong>dist</strong></li>
<li>Build Command:  <strong>npm run-script build</strong></li>
<li>Start Command: <strong>npm run-script start</strong></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/01-init.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-initialising-the-shared-project">Initialising the shared project</h2>
<p>Through the next steps, you'll create a function. This function will be used to store dependencies which will be injected later into functions around the AWS Lambda Functions ecosystem. </p>
<p>Running the command below will guide you towards steps to create your Lambda Layer:</p>
<p><code>amplify function add</code></p>
<p>Choose the options according to the <strong>bold</strong> text options below. Notice that for the compatible runtimes, you'll need to hit the <strong>space</strong> key on your keyboard to select the runtime.</p>
<ul>
<li>Select which capability you want to add: Lambda layer (shared code &amp; resource used across functions)</li>
<li>Provide a name for your Lambda layer: <strong>apmAgentLayer</strong></li>
<li>Select up to 2 compatible runtimes: <strong>NodeJS</strong></li>
<li>The current AWS account will always have access to this layer.</li>
<li>Optionally, configure who else can access this layer. (Hit to skip) <strong>Public</strong></li>
</ul>
<p>✅ Lambda layer folders and files created: amplify/backend/function/apmAgentLayer</p>
<h3 id="heading-installing-custom-modules">Installing custom modules</h3>
<p>Going to the Layer's path <em>amplify/backend/function/apmAgentLayer</em> you may have seen a few folders created by Amplify. As we're working on a Node.js project, all node modules must be installed on <em>lib/nodejs</em>.</p>
<p>I built an example of an Application Performance Monitor to show how to use the Node.js <em>performance hooks</em> feature to measure duration between requests and change HTTP response readers. It will help show you other possibilities to implement shared code and extend Node.js behaviour.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/Screen-Shot-2020-07-18-at-00.56.56.png" alt="Image" width="600" height="400" loading="lazy">
<em>NPM package to measure duration between requests</em></p>
<p>The first step here is to install the shared dependencies and upload them to the AWS. Go to the path generated by the CLI <em>amplify/backend/function/apmAgentLayer/lib/nodejs</em> and then install the package using the following commands:</p>
<pre><code class="lang-sh"><span class="hljs-built_in">cd</span> amplify/backend/<span class="hljs-keyword">function</span>/apmAgentLayer/lib/nodejs
npm i @erickwendel/ew-agent
</code></pre>
<h3 id="heading-deploying">Deploying</h3>
<p>Once you've installed your package you can just deploy it and inspect it later via the AWS Console. Notice that we haven't added any code yet. The goal at this point is just to prepare this library for further use. </p>
<p>Run the following command to upload your Lambda Layer:</p>
<p><code>amplify push</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/02-amplify-push.gif" alt="Image" width="600" height="400" loading="lazy">
<em>running amplify push command and seeing output</em></p>
<h2 id="heading-creating-the-web-api-function">Creating the Web API function</h2>
<p>At this point, you already have a local Amplify infrastructure project ready to add API Routes, Routines, link with AWS services, and so on. </p>
<p>The command below will be useful for generating an ExpressJS-based project and an AWS Lambda function. It'll also link the function on your nearly created AWS Lambda Layer and expose it on the AWS API Gateway.</p>
<p><code>amplify api add</code></p>
<p>Choose the options according to the <strong>bold</strong> text below:</p>
<ul>
<li>Please select from one of the below mentioned services: <strong>REST</strong></li>
<li>Provide a friendly name for your resource to be used as a label for this category in the project: <strong>myApi</strong></li>
<li>Provide a path (e.g., /book/{isbn}): <strong>/hi</strong></li>
<li>Choose a Lambda source <strong>Create a new Lambda function</strong></li>
<li>Provide a friendly name for your resource to be used as a label for this category in the project: <strong>myApi</strong></li>
<li>Provide the AWS Lambda function name: <strong>myApi</strong></li>
<li>Choose the runtime that you want to use: <strong>NodeJS</strong></li>
<li>Choose the function template that you want to use: <strong>Serverless ExpressJS function (Integration with API Gateway)</strong></li>
<li>Do you want to access other resources in this project from your Lambda function? <strong>No</strong></li>
<li>Do you want to invoke this function on a recurring schedule? <strong>No</strong></li>
<li>Do you want to configure Lambda layers for this function? <strong>Yes</strong></li>
<li>Provide existing layers or select layers in this project to access from this function (pick<br>up to 5): <strong>apmAgentLayer</strong></li>
<li>Select a version for apmAgentLayer: <strong>1</strong></li>
<li>Do you want to edit the local lambda function now? <strong>Yes</strong></li>
</ul>
<p>As I'm using VSCode, the last wizard's answer will open the <code>app.js</code> file on my editor so I can edit. Now without adding other dependencies, let's import the <strong>Lambda Layer</strong> shared module on the first line of this file using the code below:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'@erickwendel/ew-agent'</span>).start()
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/Screen-Shot-2020-07-18-at-01.24.42.png" alt="Image" width="600" height="400" loading="lazy">
<em>code added on the file's head</em></p>
<p>After editing the file, go to the terminal and hit <strong>Enter</strong> and choose the answers showed in <strong>bold</strong> below:</p>
<ul>
<li>Restrict API access <strong>No</strong></li>
<li>Do you want to add another path? <strong>No</strong></li>
</ul>
<p>At the time of writing, we can't test Lambda Layers locally using AWS Amplify. But you're going deploy your project to AWS and test it <strong>in production</strong> by running the <code>amplify push</code> command again.</p>
<p>Notice that it will print out which resources need to be updated and which resources will be created on this deploy. It'd take a while to perform all operations and your output should look like the below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/03-deploy-fn-1.gif" alt="Image" width="600" height="400" loading="lazy">
<em>deploying API</em></p>
<p>As your terminal may have shown, your API has now a URL. My generated URL is <a target="_blank" href="https://nlq7x7onj0.execute-api.us-east-1.amazonaws.com/dev/hi">https://nlq7x7onj0.execute-api.us-east-1.amazonaws.com/dev</a> and the route will be <code>hi</code>, which we created together in the previous steps. </p>
<p>Let's trigger a request using the cURL (or even your browser) to see check what happens:</p>
<pre><code class="lang-sh">curl -i https://nlq7x7onj0.execute-api.us-east-1.amazonaws.com/dev/hi
</code></pre>
<p>After running it, the API should respond with a JSON response with the following content <code>{"success":"get call succeed!","url":"/hi"}</code>. The Lambda Layer was injected and it should have changed your response headers adding the <code>x-instrumented-by</code> and <code>x-request-id</code> keys like this:</p>
<pre><code class="lang-sh">x-instrumented-by: ErickWendel
x-request-id: 5ddf1343-e42e-4e33-b1e1-936c303c14c8
</code></pre>
<p>If you're curious about what Amplify has managed for you during this tutorial, run <code>amplify console</code> and browse on the dashboard. You can see mine below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/04-aws-console.gif" alt="Image" width="600" height="400" loading="lazy">
<em>dashboard and seeing function logs on AWS</em></p>
<h2 id="heading-cleaning-up">Cleaning up</h2>
<p>To remove all resources created by Amplify, run <code>amplify delete</code>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>There are a lot of ways to improve your experience on serverless applications. The Amplify Framework can help you build next-generation apps and avoid repetitive tasks. </p>
<p>Check the official docs to see other possibilities to build powerful APIs using cutting-edge technologies such as GraphQL and AWS AppSync. I'm sure it'll help you a lot!</p>
<h2 id="heading-thank-you-for-reading"><strong>Thank you for reading</strong></h2>
<p>I really appreciate the time we spent together. I hope this content will be more than just text. I hope it will have made you a better thinker and also a better programmer. Follow me on <a target="_blank" href="https://twitter.com/erickwendel_">Twitter</a> and check out my <a target="_blank" href="https://erickwendel.com/">personal blog</a> where I share all my valuable content.</p>
<p>See ya! ?</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cron Job AWS Lambda Functions Tutorial – How to Schedule Tasks ]]>
                </title>
                <description>
                    <![CDATA[ By Marcia Villalba Cron jobs are usually used to schedule commands at a specific time. You can use them for tasks like running backups, monitoring the status of the system, or running system maintenance tasks.  Cron jobs are a helpful utility for sys... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/using-lambda-functions-as-cronjobs/</link>
                <guid isPermaLink="false">66d460183bc3ab877dae2214</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 08 Jun 2020 22:25:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/aws-lambda---deno.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Marcia Villalba</p>
<p>Cron jobs are usually used to schedule commands at a specific time. You can use them for tasks like running backups, monitoring the status of the system, or running system maintenance tasks. </p>
<p>Cron jobs are a helpful utility for system administrators. And when you are administering a system in the cloud, cron jobs are still very useful – you still have to do a lot of administrative tasks on your systems. </p>
<p>One way of running cron jobs in the cloud is to use a function as a service (FaaS), like Lambda in the AWS ecosystem. </p>
<p>Functions execute when they are triggered to do so, and they run code in the cloud without the need to provision or maintain any infrastructure. Also functions can be configured to run at a certain time or with certain periodicity, like traditional cron jobs. </p>
<p>In this blog post, I will use the AWS ecosystem to show you a concrete example on how to create a cron job using a function in the cloud. </p>
<h2 id="heading-amazon-cloudwatch-events">Amazon CloudWatch events</h2>
<p>In order to use a Lambda function as a cron job, we need to understand Amazon CloudWatch events. </p>
<p>Amazon CloudWatch events are sent when there are changes in the AWS resources. These events can trigger an AWS Lambda function. When your AWS resources change state, they automatically send CloudWatch events to the event stream. </p>
<p>Therefore, you can create a rule that triggers a specific Lambda function when something happens. For example, you can automatically invoke a Lambda function when there is a change in an AutoScaling group. </p>
<p>In addition, CloudWatch events can invoke a Lambda function to execute on a regular schedule. And in this way you can have, for example, a Lambda function that turns off all your testing and development EC2 instances after 6pm and another one that turns them on after 8am. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/diagram2.png" alt="Image" width="600" height="400" loading="lazy">
<em>When there is a change in an autoscaling group the cloud watch event generated triggers a Lambda function</em></p>
<h2 id="heading-setting-up-the-demo">Setting up the demo</h2>
<p>I want to show you an example of a Lambda function that can perform actions on your EC2 instances. I will be using <a target="_blank" href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html">AWS SAM</a> to define my Lambda function as infrastructure as code. </p>
<p>If you want to try this demo out, you need to have an AWS account and one or more EC2 instances configured in your AWS account. These are the ones that we are going to manipulate from the Lambda functions. EC2 instances are the AWS version of virtual machines in the cloud.</p>
<p>You can try the demo on <a target="_blank" href="https://aws.amazon.com/cloud9/">AWS Cloud9 IDE</a> (a browser based IDE), as AWS SAM is already configured in that IDE. If you want to know how to use AWS Cloud9 IDE to operate Lambda functions you can check out this <a target="_blank" href="https://youtu.be/JmEMBxfYtf4">video</a>. </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/JmEMBxfYtf4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>In this example, we are going to start and stop EC2 instances using two different AWS Lambdas that get triggered at a given time. We are starting the instances at 8am everyday and turning them off at 6pm when the day is over. </p>
<p>For that we are going to use a CloudWatch event to trigger the Lambda at the right time and also the AWS SDK to perform the operations in the instances. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/digram1-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>At an specific time a Lambda function is triggered that will operate on a set of EC2 instances</em></p>
<p>The finalised code for this example is available in this <a target="_blank" href="https://github.com/mavi888/lambda-cronjobs">GitHub</a> repository. To get this code working in AWS Cloud9 IDE, you need to <a target="_blank" href="https://help.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account">configure your GitHub account</a> in the IDE to be able to clone the project and then clone it inside the IDE.</p>
<p>When you have that ready, just run this command inside the cloned directory:</p>
<pre><code>$ sam deploy --guided
</code></pre><p>When running that command you will get a set of questions that you need to answer in order to configure this project to run successfully.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/video1-1.gif" alt="Image" width="600" height="400" loading="lazy">
<em>How to deploy the project to the cloud using AWS SAM CLI</em></p>
<p>The first thing you need to define is a <strong>name</strong> for your project. Then you'll set the <strong>region</strong> where it is getting deployed - pick the same where your EC2 instances are. Next we need to give the deploy script a <strong>list of the instances</strong> that we want to manipulate. And then we are done – it will deploy the project to our AWS account.</p>
<h2 id="heading-defining-the-aws-lambda-function">Defining the AWS Lambda function</h2>
<p>The first thing I want to show you is how we define a AWS Lambda function that gets triggered in a specific time using AWS SAM. This definition will be in the file called "template.yml". </p>
<p><img src="https://lh5.googleusercontent.com/bnnu336j4GcilKLBDIoohJH18ba3KNJUW71fNnD06vaZReh4EpEAQIJRIufYovNDvU9ARKTiyZZmFO4wUrT7u9yXY9wVh8RCvLZ77xnwQ7Q4Yw30H5-Uh8mBi3SgDAhgaOdKq2uq" alt="Image" width="700" height="654" loading="lazy">
<em>AWS SAM of the StartInstance function</em></p>
<p>This is how a function looks. Let's look at the important lines: </p>
<p>The first line is the name of the function, in this case “<strong>StartInstanceFunction</strong>”.</p>
<p>Then we have the “<strong>Properties</strong>” definition. The first property is the “<strong>Handler</strong>”. Here we will specify the module (file) where the code that needs to execute is and then the method inside that module. </p>
<p>And then we have the “<strong>CodeUri</strong>”, which is the path that shows you where to find that file. In this case, our code will be inside a directory called “cron” in a file called “handler.js” and in a method called “startInstance”. </p>
<p>After that we have the “<strong>Runtime</strong>” definition. I will be using NodeJS version 12, but you can use Python, Java, Go, C#, or whatever makes you happy. <a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html">Lambda supports multiple runtimes</a> out of the box and <a target="_blank" href="https://youtu.be/MS5pzddwwqU">you can bring your own runtime</a> if you want to.</p>
<p>Then we have the “<strong>Environment</strong>” definition that we will use to define the one environmental variable. This variable will allow us to send to the code dynamically different instances ids, depending on the configuration when we deploy. </p>
<p>After that we have a section called “<strong>Policies</strong>” which is where we define the permissions that this particular Lambda function will have. </p>
<p>It is important to know that all Lambda functions are created without any permissions. That means that they cannot do anything on any other AWS resources. </p>
<p>In order for this Lambda function to start an EC2 instance, it needs permissions to do that particular action on that particular AWS resource. In this particular policy we are granting permissions to start ALL EC2 instances in this AWS account. ALL is represented with the “*” in the resources section. </p>
<p>If you have this piece of code running in production, I recommend that you limit the resources to exactly the ones that you want this Lambda to be able to start.</p>
<p>And finally, the last section is the “<strong>Events</strong>” section. Here we will define how this Lambda function will get triggered. This function will get triggered with a scheduled CloudWatch event that triggers the Lambda everyday at 8 in the morning. Basically at 8 every day it will turn on all the EC2 instances that you specify. </p>
<p>There are many rules to form this cron expression: for example, to say that you would like this to run only Monday through Friday, write cron(0 8 ? <em> MON-FRI </em>). You can find more info in the documentation site of CloudWatch events here: <a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html">https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html</a>.</p>
<h2 id="heading-coding-the-aws-lambda-function">Coding the AWS Lambda function</h2>
<p>Now that we have defined the Lambda function we need to provide some code to it. In the folder “cron”, in the file “handler.js”, we need to add the method called “<strong>startInstance</strong>” that looks like this:  </p>
<p><img src="https://lh6.googleusercontent.com/vZxQeuu9uphLodP3t-eEqtJ-fAxN4HMOsIcKgRq9Nmq5yCCyJCw_BE5U57pMiPLfG_uaoDhH4r0bboqo5MVfgDQ92td0dkvlNEBbhE2r5qUjoAvbWGDHQsOzBDWmA-DYJosJfL7T" alt="Image" width="838" height="948" loading="lazy">
<em>Code of the startInstance function</em></p>
<p>This method will get called when the function is triggered every day at 8 am. It will get the list of EC2 instances from an environmental variable that we passed all the instances ids during deployment time. Then it will create an array of them.</p>
<p>When it has that, it will call the AWS SDK and send the array of instances id as a parameter. And if there is any error it will log it and complete. Immediately after this Lambda finishes execution, you can go to your EC2 console and see how your instances turn on. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/start-instances.gif" alt="Image" width="600" height="400" loading="lazy">
<em>EC2 instances starting automatically when the Lambda function executes</em></p>
<p>The function to turn off the EC2 instances is very similar with a few differences. You can find the code for that function in this <a target="_blank" href="https://github.com/mavi888/lambda-cronjobs">link</a> and check it out.</p>
<h2 id="heading-running-the-cron-job">Running the cron job</h2>
<p>To run this cron job, there is not much left to do. After the two functions are deployed in your AWS account, in the same region as your instances, they will execute and do what they were programmed to do.</p>
<p><img src="https://lh3.googleusercontent.com/6gytkHs7wXgfKfc0-1IHBjl29miSOSm-8OeidooPaowAIJkHY_v11IIDfoEVJavCGANraYs8I_UvwjICWXQ3yHPfNJTporV8raxxDqZas7JyjCFEpjCMcAteUgitI2U1h7mKRf3E" alt="Image" width="1404" height="636" loading="lazy">
<em>AWS Lambda functions for starting and stopping instances deployed in my AWS account</em></p>
<p>Now you need to wait until 8am or 6pm to see if they work. Or if you want to test it out right now, change the event time in the Lambda definition to a time that works for you. Make sure that the instance is on if you are planning to turn them off or the other way around, so you can see the changes. </p>
<p>Now wait and see what happens in the EC2 console. Right after the time you set up, you will see the instance go off or on and then do the opposite at the other time you setup. This will go forever until you remove the Lambda functions.</p>
<h2 id="heading-cleaning-up-your-aws-account">Cleaning up your AWS account</h2>
<p>After completing this demo I recommend you that you turn off (or remove the instance you created to test) and remove the Lambda functions you just created. </p>
<p>Removing the lambda functions is as easy as going into your CloudFormation service in your AWS management console and removing the stack of resources that AWS SAM created. </p>
<p>Also don’t forget to terminate and remove the EC2 instances if you created them for this demo.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/delete-lambda.gif" alt="Image" width="600" height="400" loading="lazy">
<em>How to remove the AWS Lambda functions we created in this demo</em></p>
<h2 id="heading-to-conclude">To conclude</h2>
<p>AWS Lambda functions are a very useful tool to perform all kinds of tasks in your AWS account. You can basically get notifications of any changes in the AWS resources through CloudWatch events and then you can access almost all the services using the AWS SDK. So you can perform all kinds of maintenance tasks and automated tasks over your infrastructure.</p>
<p><strong>Thanks for reading.</strong></p>
<p>I’m Marcia Villalba, Developer Advocate for AWS and the host of a youtube channel called FooBar where I have over 250 video tutorials on Serverless, AWS and software engineer practices.</p>
<ul>
<li>Twitter: <a target="_blank" href="https://twitter.com/mavi888uy">https://twitter.com/mavi888uy</a></li>
<li>Youtube: <a target="_blank" href="https://youtube.com/foobar_codes">https://youtube.com/foobar_codes</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
