<?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[ serverless framework - 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[ serverless framework - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 26 Jun 2026 22:47:39 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/serverless-framework/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Serverless CRUD REST API with the Serverless Framework, Node.js, and GitHub Actions ]]>
                </title>
                <description>
                    <![CDATA[ Serverless computing emerged as a response to the challenges of traditional server-based architectures. With serverless, developers no longer need to manage or scale servers manually. Instead, cloud providers handle infrastructure management, allowin... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-serverless-crud-rest-api/</link>
                <guid isPermaLink="false">66c63e8f9aca8203eaa3e7e3</guid>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ifeanyi Otuonye ]]>
                </dc:creator>
                <pubDate>Wed, 21 Aug 2024 19:22:55 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724267592147/e9dc4429-6475-4d35-b0e8-81c116f769b8.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Serverless computing emerged as a response to the challenges of traditional server-based architectures. With serverless, developers no longer need to manage or scale servers manually. Instead, cloud providers handle infrastructure management, allowing teams to focus solely on writing and deploying code.</p>
<p>Serverless solutions automatically scale based on demand and offer a pay-as-you-go model. This means that you only pay for the resources your application actually uses. This approach significantly reduces operational overhead, increases flexibility and accelerates development cycles, making it an attractive option for modern application development.</p>
<p>By abstracting server management, Serverless platforms let you concentrate on business logic and application functionality. This leads to faster deployments and more innovation. Serverless architectures are also event-driven, which means they can automatically respond to real-time events and scale to meet user demands without manual intervention.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-important-concepts-to-understand">Important Concepts to Understand</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-application-programming-interface-api">Application Programming Interface (API)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-http-methods">HTTP Methods</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-amazon-api-gateway">Amazon API Gateway</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-amazon-dynamodb">Amazon DynamoDB</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-serverless-crud-application">Serverless CRUD Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-serverless-framework">The Serverless Framework</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-github-actions">GitHub Actions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-postman">Postman</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-our-use-case">Our Use Case</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tutorial-objectives">Tutorial Objectives</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-get-started-clone-the-git-repository">How to get Started:Clone the Git Repository</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-set-up-the-serverless-framework-environment">Step 1: Set up the Serverless Framework Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-define-the-api-in-the-serverless-yaml-file">Step 2: Define the API in the Serverless YAML File</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-develop-the-lambda-functions-for-crud-operations">Step 3: Develop the Lambda Functions for CRUD Operations</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-create-coffee-lambda-function">Create Coffee Lambda function</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-get-coffee-lambda-function">Get Coffee Lambda function</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-update-coffee-lambda-function">Update Coffee Lambda function</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-delete-coffee-lambda-function">Delete Coffee Lambda function</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-set-up-cicd-pipeline-multi-stage-deployments-for-dev-and-prod-environments">Step 4: Set Up CI/CD Pipeline Multi-stage Deployments for Dev and Prod Environments</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-test-the-dev-and-prod-pipelines">Step 5: Test the Dev and Prod Pipelines</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-test-and-validate-prod-and-dev-apis-using-postman">Step 6: Test and Validate Prod and Dev APIs using Postman</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<p>Before diving into the technical details, we'll go over some key background concepts.</p>
<h2 id="heading-important-concepts-to-understand">Important Concepts to Understand</h2>
<h3 id="heading-application-programming-interface-api">Application Programming Interface (API)</h3>
<p>An Application Programming Interface (API) allows different software applications to communicate and interact with each other. It defines the methods and data formats that applications can use to request and exchange information for integration and data sharing between diverse systems.</p>
<h3 id="heading-http-methods">HTTP Methods</h3>
<p>HTTP methods or request methods are a critical component of web services and APIs. They indicate the desired action to be performed on a resource in a given request URL.</p>
<p>The most commonly used methods in RESTful APIs are:</p>
<ul>
<li><p><strong>GET</strong>: used to retrieve data from a server</p>
</li>
<li><p><strong>POST</strong>: sends data, included in the body of the request, to create or update a resource</p>
</li>
<li><p><strong>PUT</strong>: updates or replaces an existing resource or creates a new resource if it doesn’t exist</p>
</li>
<li><p><strong>DELETE</strong>: deletes the specified data from the server.</p>
</li>
</ul>
<h3 id="heading-amazon-api-gateway">Amazon API Gateway</h3>
<p>Amazon API Gateway is a fully managed service that makes it easy for developers to create, publish, maintain, monitor and secure APIs at scale. It acts as an entry point for multiple APIs, managing and controlling the interactions between clients (such as web or mobile applications) and backend services.</p>
<p>It also provides various functions, including request routing, security, authentication, caching and rate limiting that help simplify the management and deployment of APIs.</p>
<h3 id="heading-amazon-dynamodb">Amazon DynamoDB</h3>
<p>DynamoDB is a fully managed NoSQL database service designed for high scalability, low latency, and replication of data across multiple regions.</p>
<p>DynamoDB stores data in a schema-less format, allowing for flexible and fast storage and retrieval of structured and semi-structured data. It is commonly used for building scalable and responsive applications in cloud-based environments.</p>
<h3 id="heading-serverless-crud-application">Serverless CRUD Application</h3>
<p>A serverless CRUD application refers to the ability to <strong>Create, Read, Update and Delete</strong> data. But the architecture and components involved differ from traditional server-based applications.</p>
<p><strong>Create</strong> involves adding new entries to a DynamoDB table. The <strong>Read</strong> operation retrieves data from a DynamoDB table. <strong>Update</strong> updates existing data in DynamoDB. And the <strong>Delete</strong> operation deletes data from DynamoDB.</p>
<h3 id="heading-the-serverless-framework">The Serverless Framework</h3>
<p>The Serverless Framework is an open-source tool that simplifies the deployment and management of serverless applications across multiple cloud providers, including AWS. It abstracts away the complexity of provisioning and managing infrastructure by allowing developers to define their infrastructure as code using a YAML file.</p>
<p>The framework handles the deployment, scaling and updating of serverless functions, APIs and other resources.</p>
<h3 id="heading-github-actions">GitHub Actions</h3>
<p>GitHub Actions is a powerful CI/CD automation tool that allows developers to automate their software workflows directly from their GitHub repository.</p>
<p>With GitHub Actions, you can create custom pipelines triggered by events such as code pushes, pull requests, or branch merges. These workflows are defined in YAML files within the repository and can perform tasks like testing, building and deploying applications to various environments.</p>
<h3 id="heading-postman">Postman</h3>
<p>Postman is a popular collaboration platform that simplifies the process of designing, testing, and documenting APIs. It offers a user-friendly interface for developers to create and send HTTP requests, test API endpoints, and automate testing workflows.</p>
<p>Alright, now that you're familiar with the tools and technologies we'll use here, let's dive in.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Node.js and npm installed</p>
</li>
<li><p>AWS CLI configured with access to your AWS account</p>
</li>
<li><p>A Serverlesss Framework account</p>
</li>
<li><p>Serverlesss Framework globally installed in your local CLI</p>
</li>
</ul>
<h2 id="heading-our-use-case">Our Use Case</h2>
<p>Meet Alyx, an entrepreneur who has recently been learning about serverless architecture. She's read about how it's a powerful and efficient way to build backends for web applications, offering a more modern approach to web application development.</p>
<p>She wants to apply what she's learned so far about of the fundamentals of AWS serverless  computing. She knows that serverless doesn’t mean there are no servers involved – rather, it just abstracts away the management and provisioning of servers. And now she wants to focus solely on writing code and implementing business logic.</p>
<p>Let’s check out how Alyx, the owner of a thriving coffee shop, begins to leverage serverless architecture for the backend of her web application.</p>
<p>Alyx’s Coffee Haven, an online coffee shop, offers an array of coffee blends and treats for sale. Initially, Alyx managed the shop’s orders and inventory with traditional web hosting services and operations, where she handled multiple servers and resources. But as her coffee shop grew in popularity, she started facing an increasing number of orders, especially during peak hours and seasonal promotions.</p>
<p>Managing the servers and ensuring the application could handle the surge in traffic became a challenge for Alyx. She found herself constantly worrying about server capacity, scalability, and the cost of maintaining the infrastructure.</p>
<p>She also wanted to introduce new features like personalized recommendations and loyalty programs, but this became a daunting task given the limitations of her traditional setup.</p>
<p>Then Alyx learned about the concept of serverless. She likened a serverless backend to a barista who automatically brews coffee in real-time, without her having to worry about the intricate details of the coffee-making process.</p>
<p>Excited by this idea, Alyx decided to migrate her coffee shop’s backend to a serverless platform using AWS Lambda, AWS API Gateway, and Amazon DynamoDB. This setup will let her focus more on crafting the perfect coffee blends and treats for her customers.</p>
<p>With serverless, each customer’s order becomes an event that triggers a series of serverless functions. Separate AWS Lambda functions processes the orders and handles all the business logic behind the scenes. For instance, it creates a customer’s order and is able to retrieve that order. It can also delete someone's order or update an order’s status.</p>
<p>Alyx no longer needs to worry about managing servers, as the serverless platform automatically scales up and down based on incoming order requests. Also, the cost-efficiency of serverless is huge for Alyx. With a pay-as-you-go model, she only pays for the actual compute time her functions consume, offering her a more a cost-effective solution for her growing business.</p>
<p>But she doesn’t stop there! She also wants to automate everything, from deploying infrastructure to updating her application whenever there’s a new change. By utilizing Infrastructure as Code (IaC) with the Serverless Framework, she can define all her infrastructure in code and manage it easily.</p>
<p>On top of that, she sets up GitHub Actions for continuous integration and delivery (CI/CD), so that every change she makes is automatically deployed through a pipeline, whether it’s a new feature in development or a hot fix for production.</p>
<h2 id="heading-tutorial-objectives">Tutorial Objectives</h2>
<ul>
<li><p>Set up the Serverless Framework environment</p>
</li>
<li><p>Define an API in the YAML file</p>
</li>
<li><p>Develop AWS Lambda functions to process CRUD operations</p>
</li>
<li><p>Set up multi-stage deployments for Dev and Prod</p>
</li>
<li><p>Test the Dev and Prod pipelines</p>
</li>
<li><p>Test and validate Dev and Prod APIs using Postman</p>
</li>
</ul>
<h2 id="heading-how-to-get-started-clone-the-git-repository">How to Get Started: Clone the Git Repository</h2>
<p>To enhance your understanding and so you can follow along with this tutorial more effectively, go ahead and clone the project’s repository from my GitHub. You can do that <a target="_blank" href="https://github.com/ifeanyiro9/coffee-shop-serverless-crud-api-nodejs">by going here</a>. As we move forward, feel free to edit the files as you feel necessary.</p>
<p>After cloning the repository, you will notice the presence of multiple files in your folder, as you can see in the image below. We’ll use all of these files to build our serverless coffee shop API.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353622612/2dd67caa-1a30-4511-afc5-babfaa0c5b82.png" alt="File structure" class="image--center mx-auto" width="379" height="503" loading="lazy"></p>
<h2 id="heading-step-1-set-up-the-serverless-framework-environment">Step 1: Set up the Serverless Framework Environment</h2>
<p>To set up the Serverless Framework environment for automated deployments, you'll need to authenticate your Serverless Framework account via the CLI.</p>
<p>This requires creating an access key that enables the CI/CD pipeline and utilizes the Serverless Framework to authenticate securely into your account without exposing your credentials. By signing into your Serverless account and generating an access key, the pipeline can deploy your serverless application automatically from the build configuration file.</p>
<p>To do this, head to your Serverless account and <a target="_blank" href="https://app.serverless.com/settings/accessKeys">navigate to the Access Keys section</a>. Click on “+add,” name it SERVERLESS_ACCESS_KEY, and then create the key.</p>
<p>Once you’ve created your access key, be sure to copy and store it securely. You'll use this key as a secret variable in your GitHub repository to authenticate and authorize your CI/CD pipeline.</p>
<p>It will provide access to your Serverless Framework account during the deployment process. You’ll add this key to your GitHub repository’s secrets later, so your pipeline can securely use it to deploy the serverless resources without exposing sensitive information in your codebase.</p>
<p>Now, let’s define the AWS resources as code in the <strong>severless.yaml</strong> file.</p>
<h2 id="heading-step-2-define-the-api-in-the-serverless-yaml-file">Step 2: Define the API in the Serverless YAML File</h2>
<p>In this file, you'll define the core infrastructure and functionality of the Coffee Shop API using the Serverless Framework’s YAML configuration.</p>
<p>This file defines the AWS services being utilized, including API Gateway, Lambda functions for CRUD operations, and DynamoDB for data storage.</p>
<p>You'll also configure an IAM role so the Lambda functions have the necessary permissions to interact with the DynamoDB service.</p>
<p>The API Gateway is set up with appropriate HTTP methods (<strong>POST</strong>, <strong>GET</strong>, <strong>PUT</strong>, and <strong>DELETE</strong>) to handle incoming requests and trigger the corresponding Lambda functions.</p>
<p>Let’s check out the code:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">service:</span> <span class="hljs-string">coffee-shop-api</span>
<span class="hljs-attr">frameworkVersion:</span> <span class="hljs-string">'4'</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">nodejs20.x</span>
  <span class="hljs-attr">region:</span> <span class="hljs-string">us-east-1</span>
  <span class="hljs-attr">stage:</span> <span class="hljs-string">${opt:stage}</span>
  <span class="hljs-attr">iam:</span>
    <span class="hljs-attr">role:</span>
      <span class="hljs-attr">statements:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">Effect:</span> <span class="hljs-string">Allow</span>
          <span class="hljs-attr">Action:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">dynamodb:PutItem</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">dynamodb:GetItem</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">dynamodb:Scan</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">dynamodb:UpdateItem</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">dynamodb:DeleteItem</span>
          <span class="hljs-attr">Resource:</span> <span class="hljs-string">arn:aws:dynamodb:${self:provider.region}:*:table/CoffeeOrders-${self:provider.stage}</span>

<span class="hljs-attr">functions:</span>
  <span class="hljs-attr">createCoffee:</span>
    <span class="hljs-attr">handler:</span> <span class="hljs-string">createCoffee.handler</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">COFFEE_ORDERS_TABLE:</span> <span class="hljs-string">CoffeeOrders-${self:provider.stage}</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">coffee</span>
          <span class="hljs-attr">method:</span> <span class="hljs-string">post</span>

  <span class="hljs-attr">getCoffee:</span>
    <span class="hljs-attr">handler:</span> <span class="hljs-string">getCoffee.handler</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">COFFEE_ORDERS_TABLE:</span> <span class="hljs-string">CoffeeOrders-${self:provider.stage}</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">coffee</span>
          <span class="hljs-attr">method:</span> <span class="hljs-string">get</span>

  <span class="hljs-attr">updateCoffee:</span>
    <span class="hljs-attr">handler:</span> <span class="hljs-string">updateCoffee.handler</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">COFFEE_ORDERS_TABLE:</span> <span class="hljs-string">CoffeeOrders-${self:provider.stage}</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">coffee</span>  
          <span class="hljs-attr">method:</span> <span class="hljs-string">put</span>  

  <span class="hljs-attr">deleteCoffee:</span>  
    <span class="hljs-attr">handler:</span> <span class="hljs-string">deleteCoffee.handler</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">COFFEE_ORDERS_TABLE:</span> <span class="hljs-string">CoffeeOrders-${self:provider.stage}</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">coffee</span>
          <span class="hljs-attr">method:</span> <span class="hljs-string">delete</span>
<span class="hljs-attr">resources:</span>
  <span class="hljs-attr">Resources:</span>
    <span class="hljs-attr">CoffeeTable:</span>
      <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::DynamoDB::Table</span>
      <span class="hljs-attr">Properties:</span>
        <span class="hljs-attr">TableName:</span> <span class="hljs-string">CoffeeOrders-${self:provider.stage}</span>
        <span class="hljs-attr">AttributeDefinitions:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">AttributeName:</span> <span class="hljs-string">OrderId</span>
            <span class="hljs-attr">AttributeType:</span> <span class="hljs-string">S</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">AttributeName:</span> <span class="hljs-string">CustomerName</span>
            <span class="hljs-attr">AttributeType:</span> <span class="hljs-string">S</span>
        <span class="hljs-attr">KeySchema:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">AttributeName:</span> <span class="hljs-string">OrderId</span>
            <span class="hljs-attr">KeyType:</span> <span class="hljs-string">HASH</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">AttributeName:</span> <span class="hljs-string">CustomerName</span>
            <span class="hljs-attr">KeyType:</span> <span class="hljs-string">RANGE</span>
        <span class="hljs-attr">BillingMode:</span> <span class="hljs-string">PAY_PER_REQUEST</span>
</code></pre>
<p>The <strong>serverless.yml</strong> configuration defines how Alyx's Coffee Shop API will run in a serverless environment on AWS. The <strong>provider</strong> section specifies that the application will use AWS as the cloud provider, with <strong>Node.js</strong> as the runtime environment.</p>
<p>The region is set to <strong>us-east-1</strong> and the <strong>stage</strong> variable allows for dynamic deployment across different environments, like dev and prod. This means that the same code can deploy to different environments, with resources being named accordingly to avoid conflicts.</p>
<p>In the <strong>iam</strong> section, permissions are granted to Lambda functions to interact with the DynamoDB table. The <strong>${self:provider.stage}</strong> syntax dynamically names the DynamoDB table, so that each environment has its own separate resources, like <strong>CoffeeOrders-dev</strong> for the development environment and <strong>CoffeeOrders-prod</strong> for production. This dynamic naming helps manage multiple environments without manually configuring separate tables for each one.</p>
<p>The <strong>functions</strong> section defines the four core Lambda functions, <strong>createCoffee</strong>, <strong>getCoffee</strong>, <strong>updateCoffee</strong> and <strong>deleteCoffee</strong>. These handle the CRUD operations for the Coffee Shop API.</p>
<p>Each function is connected to a specific HTTP method in the API Gateway, such as <strong>POST</strong>, <strong>GET</strong>, <strong>PUT</strong> and <strong>DELETE</strong>. These functions interact with the DynamoDB table that’s dynamically named based on the current stage.</p>
<p>The last <strong>resources</strong> section defines the DynamoDB table itself. It sets up the table with the attributes <strong>OrderId</strong> and <strong>CustomerName</strong>, which are used as the primary key. The table is configured to use a pay-per-request billing mode, making it cost-effective for Alyx's growing business.</p>
<p>By automating the deployment of these resources using the Serverless Framework, Alyx can easily manage her infrastructure, freeing her from the burden of manually provisioning and scaling resources.</p>
<h2 id="heading-step-3-develop-the-lambda-functions-for-crud-operations">Step 3: Develop the Lambda Functions for CRUD Operations</h2>
<p>In this step, we implement the core logic of Alyx’s Coffee Shop API by creating Lambda functions with JavaScript that perform the essential CRUD operations <strong>createCoffee</strong>, <strong>getCoffee</strong>, <strong>updateCoffee</strong> and <strong>deleteCoffee</strong>.</p>
<p>These functions utilize the AWS SDK to interact with AWS services, particularly DynamoDB. Each function will be responsible for handling specific API requests such as creating an order, retrieving orders, updating order statuses, and deleting orders.</p>
<h3 id="heading-create-coffee-lambda-function">Create Coffee Lambda function</h3>
<p>This function creates an order:</p>
<pre><code class="lang-yaml"><span class="hljs-string">const</span> <span class="hljs-string">AWS</span> <span class="hljs-string">=</span> <span class="hljs-string">require('aws-sdk');</span>
<span class="hljs-string">const</span> <span class="hljs-string">dynamoDb</span> <span class="hljs-string">=</span> <span class="hljs-string">new</span> <span class="hljs-string">AWS.DynamoDB.DocumentClient();</span>
<span class="hljs-string">const</span> { <span class="hljs-attr">v4:</span> <span class="hljs-string">uuidv4</span> } <span class="hljs-string">=</span> <span class="hljs-string">require('uuid');</span>

<span class="hljs-string">module.exports.handler</span> <span class="hljs-string">=</span> <span class="hljs-string">async</span> <span class="hljs-string">(event)</span> <span class="hljs-string">=&gt;</span> {
  <span class="hljs-string">const</span> <span class="hljs-string">requestBody</span> <span class="hljs-string">=</span> <span class="hljs-string">JSON.parse(event.body);</span>
  <span class="hljs-string">const</span> <span class="hljs-string">customerName</span> <span class="hljs-string">=</span> <span class="hljs-string">requestBody.customer_name;</span>
  <span class="hljs-string">const</span> <span class="hljs-string">coffeeBlend</span> <span class="hljs-string">=</span> <span class="hljs-string">requestBody.coffee_blend;</span>
  <span class="hljs-string">const</span> <span class="hljs-string">orderId</span> <span class="hljs-string">=</span> <span class="hljs-string">uuidv4();</span>

  <span class="hljs-string">const</span> <span class="hljs-string">params</span> <span class="hljs-string">=</span> {
    <span class="hljs-attr">TableName:</span> <span class="hljs-string">process.env.COFFEE_ORDERS_TABLE</span>,
    <span class="hljs-attr">Item:</span> {
      <span class="hljs-attr">OrderId:</span> <span class="hljs-string">orderId</span>,
      <span class="hljs-attr">CustomerName:</span> <span class="hljs-string">customerName</span>,
      <span class="hljs-attr">CoffeeBlend:</span> <span class="hljs-string">coffeeBlend</span>,
      <span class="hljs-attr">OrderStatus:</span> <span class="hljs-string">'Pending'</span>
    }
  }<span class="hljs-string">;</span>

  <span class="hljs-string">try</span> {
    <span class="hljs-string">await</span> <span class="hljs-string">dynamoDb.put(params).promise();</span>
    <span class="hljs-string">return</span> {
      <span class="hljs-attr">statusCode:</span> <span class="hljs-number">200</span>,
      <span class="hljs-attr">body:</span> <span class="hljs-string">JSON.stringify(</span>{ <span class="hljs-attr">message:</span> <span class="hljs-string">'Order created successfully!'</span>, <span class="hljs-attr">OrderId:</span> <span class="hljs-string">orderId</span> }<span class="hljs-string">)</span>
    }<span class="hljs-string">;</span>
  } <span class="hljs-string">catch</span> <span class="hljs-string">(error)</span> {
    <span class="hljs-string">return</span> {
      <span class="hljs-attr">statusCode:</span> <span class="hljs-number">500</span>,
      <span class="hljs-attr">body:</span> <span class="hljs-string">JSON.stringify(</span>{ <span class="hljs-attr">error:</span> <span class="hljs-string">`Could</span> <span class="hljs-attr">not create order:</span> <span class="hljs-string">$</span>{<span class="hljs-string">error.message</span>}<span class="hljs-string">`</span> }<span class="hljs-string">)</span>
    }<span class="hljs-string">;</span>
  }
}<span class="hljs-string">;</span>
</code></pre>
<p>This Lambda function handles the creation of a new coffee order in the DynamoDB table. First we import the AWS SDK and initialize a <strong>DynamoDB.DocumentClient</strong> to interact with DynamoDB. The <strong>uuid</strong> library is also imported to generate unique order IDs.</p>
<p>Inside the <strong>handler</strong> function, we parse the incoming request body to extract customer information, such as the customer's name and preferred coffee blend. A unique <strong>orderId</strong> is generated using <strong>uuidv4()</strong> and this data is prepared for insertion into DynamoDB.</p>
<p>The <strong>params</strong> object defines the table where the data will be stored, with <strong>TableName</strong> dynamically set to the value of the environment variable <strong>COFFEE_ORDERS_TABLE</strong>. The new order includes fields such as <strong>OrderId</strong>, <strong>CustomerName</strong>, <strong>CoffeeBlend</strong>, and an initial status of <strong>Pending</strong>.</p>
<p>In the <strong>try</strong> block, the code attempts to add the order to the DynamoDB table using the <strong>put()</strong> method. If successful, the function returns a status code of <strong>200</strong> with a success message and the <strong>OrderId.</strong> If there’s an error, the code catches it and returns a <strong>500</strong> status code along with an error message.</p>
<h3 id="heading-get-coffee-lambda-function">Get Coffee Lambda function</h3>
<p>This function retrieves all coffee items:</p>
<pre><code class="lang-javascript"><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> dynamoDb = <span class="hljs-keyword">new</span> AWS.DynamoDB.DocumentClient();

<span class="hljs-built_in">module</span>.exports.handler = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> params = {
    <span class="hljs-attr">TableName</span>: process.env.COFFEE_ORDERS_TABLE
  };

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> dynamoDb.scan(params).promise();
    <span class="hljs-keyword">return</span> {
      <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(result.Items)
    };
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> {
      <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-attr">error</span>: <span class="hljs-string">`Could not retrieve orders: <span class="hljs-subst">${error.message}</span>`</span> })
    };
  }
};
</code></pre>
<p>This Lambda function is responsible for retrieving all coffee orders from a DynamoDB table and exemplifies a serverless approach to retrieving data from DynamoDB in a scalable manner.</p>
<p>We again use the AWS SDK to initialize a <strong>DynamoDB.DocumentClient</strong> instance to interact with DynamoDB. The <strong>handler</strong> function constructs the <strong>params</strong> object, specifying the <strong>TableName</strong>, which is dynamically set using the <strong>COFFEE_ORDERS_TABLE</strong> environment variable.</p>
<p>The <strong>scan()</strong> method retrieves all items from the table. Again, if the operation is successful, the function returns a status code of <strong>200</strong> along with the retrieved items in JSON format. In case of an error, a <strong>500</strong> status code and an error message are returned.</p>
<h3 id="heading-update-coffee-lambda-function">Update Coffee Lambda function</h3>
<p>This function updates a coffee item by its ID:</p>
<pre><code class="lang-javascript"><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> dynamoDb = <span class="hljs-keyword">new</span> AWS.DynamoDB.DocumentClient();

<span class="hljs-built_in">module</span>.exports.handler = <span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> requestBody = <span class="hljs-built_in">JSON</span>.parse(event.body);
  <span class="hljs-keyword">const</span> { order_id, new_status, customer_name } = requestBody;

  <span class="hljs-keyword">const</span> params = {
    <span class="hljs-attr">TableName</span>: process.env.COFFEE_ORDERS_TABLE,
    <span class="hljs-attr">Key</span>: {
      <span class="hljs-attr">OrderId</span>: order_id,
      <span class="hljs-attr">CustomerName</span>: customer_name
    },
    <span class="hljs-attr">UpdateExpression</span>: <span class="hljs-string">'SET OrderStatus = :status'</span>,
    <span class="hljs-attr">ExpressionAttributeValues</span>: {
      <span class="hljs-string">':status'</span>: new_status
    }
  };

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">await</span> dynamoDb.update(params).promise();
    <span class="hljs-keyword">return</span> {
      <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-attr">message</span>: <span class="hljs-string">'Order status updated successfully!'</span>, <span class="hljs-attr">OrderId</span>: order_id })
    };
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> {
      <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-attr">error</span>: <span class="hljs-string">`Could not update order: <span class="hljs-subst">${error.message}</span>`</span> })
    };
  }
};
</code></pre>
<p>This Lambda function handles updating the status of a specific coffee order in the DynamoDB table.</p>
<p>The <strong>handler</strong> function extracts the <strong>order_id</strong>, <strong>new_status</strong>, and <strong>customer_name</strong> from the request body. It then constructs the <strong>params</strong> object to specify the table name and the primary key for the order (using <strong>OrderId</strong> and <strong>CustomerName</strong>). The <strong>UpdateExpression</strong> sets the new status of the order.</p>
<p>In the <strong>try</strong> block, the code attempts to update the order in DynamoDB using the <strong>update()</strong> method. Once again, of course if successful, the function returns a status code of <strong>200</strong> with a success message. If an error occurs, it catches the error and returns a <strong>500</strong> status code along with an error message.</p>
<h3 id="heading-delete-coffee-lambda-function">Delete Coffee Lambda function</h3>
<p>This function deletes a coffee item by its ID:</p>
<pre><code class="lang-javascript"><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> dynamoDb = <span class="hljs-keyword">new</span> AWS.DynamoDB.DocumentClient();

<span class="hljs-built_in">module</span>.exports.handler = <span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> requestBody = <span class="hljs-built_in">JSON</span>.parse(event.body);
  <span class="hljs-keyword">const</span> { order_id, customer_name } = requestBody;

  <span class="hljs-keyword">const</span> params = {
    <span class="hljs-attr">TableName</span>: process.env.COFFEE_ORDERS_TABLE,
    <span class="hljs-attr">Key</span>: {
      <span class="hljs-attr">OrderId</span>: order_id,
      <span class="hljs-attr">CustomerName</span>: customer_name
    }
  };

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">await</span> dynamoDb.delete(params).promise();
    <span class="hljs-keyword">return</span> {
      <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-attr">message</span>: <span class="hljs-string">'Order deleted successfully!'</span>, <span class="hljs-attr">OrderId</span>: order_id })
    };
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> {
      <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-attr">error</span>: <span class="hljs-string">`Could not delete order: <span class="hljs-subst">${error.message}</span>`</span> })
    };
  }
};
</code></pre>
<p>The Lambda function deletes a specific coffee order from the DynamoDB table. In the handler function, the code parses the request body to extract the <strong>order_id</strong> and <strong>customer_name</strong>. These values are used as the primary key to identify the item to be deleted from the table. The <strong>params</strong> object specifies the table name and key for the item to be deleted.</p>
<p>In the <strong>try</strong> block, the code attempts to delete the order from DynamoDB using the <strong>delete()</strong> method. If successful, again it returns a <strong>200</strong> status code with a success message, indicating that the order was deleted. If an error occurs, the code catches it and returns a <strong>500</strong> status code along with an error message.</p>
<p>Now that we’ve explained each Lambda function, let’s set up a multi-stage CI/CD pipeline.</p>
<h2 id="heading-step-4-set-up-cicd-pipeline-multi-stage-deployments-for-dev-and-prod-environments">Step 4: Set Up CI/CD Pipeline Multi-stage Deployments for Dev and Prod Environments</h2>
<p>To set up AWS secrets in your GitHub repository, first navigate to the repository’s settings. Select <strong>Settings</strong> on the top right, then go to the bottom left and select <strong>Secrets and variables.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724352977158/9250d55a-941a-4bfd-9f7d-843e9b40d8b6.png" alt="Select &quot;Settings&quot; option in GitHub repo at top right." class="image--center mx-auto" width="996" height="126" loading="lazy"></p>
<p>Next, click on <strong>Actions</strong> as seen in the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353027861/52692cba-1bd1-4773-9441-a080af16f513.png" alt="Select &quot;Actions&quot; option to set secret variables for GitHub Actions." class="image--center mx-auto" width="443" height="277" loading="lazy"></p>
<p>From there, select <strong>New repository secret</strong> to create secrets.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353092604/a54b12fa-31e7-43d0-b4d5-2abe6a641181.png" alt="Select button to create new repository secret variables." class="image--center mx-auto" width="666" height="135" loading="lazy"></p>
<p>Three secrets are needed to create for your pipeline, <strong>AWS_ACCESS_KEY_ID</strong>, <strong>AWS_SECRET_ACCESS_KEY</strong>, and <strong>SERVERLESS_ACCESS_KEY</strong>.</p>
<p>Use your AWS account access key credentials for the first two variables and then the serverless access key previously saved to create the <strong>SERVERLESS_ACCESS_KEY</strong>. These secrets will securely authenticate your CI/CD pipeline as seen in the image below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353131423/5b4af7c7-ff3e-431f-a9ef-1ddf74fa9e46.png" alt="Three secret variables needed to authenticate to AWS and Serverless Framework account." class="image--center mx-auto" width="974" height="306" loading="lazy"></p>
<p>Make sure that your main branch is named “<strong>main</strong>,” as this will serve as the production branch. Next, create a new branch called “<strong>dev</strong>” for development work.</p>
<p>You can also create feature-specific branches, such as “<strong>dev/feature</strong>,” for more granular development. GitHub Actions will use these branches to deploy changes automatically, with <strong>dev</strong> representing the development environment and <strong>main</strong> representing production.</p>
<p>This branching strategy allows you to manage the CI/CD pipeline efficiently, deploying new code changes whenever there's a merge into either the dev or prod environments.</p>
<h3 id="heading-how-to-use-github-actions-to-deploy-the-yaml-file">How to Use GitHub Actions to Deploy the YAML File</h3>
<p>To automate the deployment process for the Coffee Shop API, you'll utilize GitHub Actions, which integrates with your GitHub repository.</p>
<p>This deployment pipeline is triggered whenever code is pushed to the main or dev branches. By configuring environment-specific deployments, you'll ensure that updates to the dev branch deploy to the development environment, while changes to the main branch trigger production deployments.</p>
<p>Now, let’s review the code:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">deploy-coffee-shop-api</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">dev</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node.js</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">node-version:</span> <span class="hljs-string">'20.x'</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">|
        cd coffee-shop-api
        npm install
</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Serverless</span> <span class="hljs-string">Framework</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span> <span class="hljs-string">-g</span> <span class="hljs-string">serverless</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">AWS</span> <span class="hljs-string">(Dev)</span>
      <span class="hljs-attr">if:</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/dev'</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">|
        cd coffee-shop-api
        npx serverless deploy --stage dev
</span>      <span class="hljs-attr">env:</span>
        <span class="hljs-attr">AWS_ACCESS_KEY_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ACCESS_KEY_ID</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">AWS_SECRET_ACCESS_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_SECRET_ACCESS_KEY</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">SERVERLESS_ACCESS_KEY:</span> <span class="hljs-string">${{secrets.SERVERLESS_ACCESS_KEY}}</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">AWS</span> <span class="hljs-string">(Prod)</span>
      <span class="hljs-attr">if:</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/main'</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">|
        cd coffee-shop-api
        npx serverless deploy --stage prod
</span>      <span class="hljs-attr">env:</span>
        <span class="hljs-attr">AWS_ACCESS_KEY_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ACCESS_KEY_ID</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">AWS_SECRET_ACCESS_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_SECRET_ACCESS_KEY</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">SERVERLESS_ACCESS_KEY:</span> <span class="hljs-string">${{secrets.SERVERLESS_ACCESS_KEY}}</span>
</code></pre>
<p>The GitHub Actions YAML configuration is what automates the deployment process of the Coffee Shop API to AWS using the Serverless Framework. The workflow triggers whenever changes are pushed to the main or dev branches.</p>
<p>It begins by checking out the repository’s code, then setting up Node.js with version 20.x to match the runtime used by the Lambda functions. After that, it installs the project dependencies by navigating to the <strong>coffee-shop-api</strong> directory and running <strong>npm install</strong>.</p>
<p>The workflow also installs the Serverless Framework globally, allowing the serverless CLI to be used for deployments. Depending on which branch is updated, the workflow conditionally deploys to the appropriate environment.</p>
<p>If the changes are pushed to the dev branch, it deploys to the dev stage. If they are pushed to the main branch, it deploys to the prod stage. The deployment commands, <code>npx serverless deploy --stage dev</code> or <code>npx serverless deploy --stage prod</code> are executed within the coffee-shop-api directory.</p>
<p>For a secure deployment, the workflow accesses AWS credentials and the Serverless access key via environment variables stored in GitHub Secrets. This allows the CI/CD pipeline to authenticate with AWS and the Serverless Framework without exposing sensitive information in the repository.</p>
<p>Now, we can proceed to test out the pipeline.</p>
<h2 id="heading-step-5-test-the-dev-and-prod-pipelines">Step 5: Test the Dev and Prod Pipelines</h2>
<p>First, you'll need to verify that the main (prod) branch is called “<strong>main</strong>”. Then create a dev branch called “<strong>dev</strong>”. Once you make any valid changes to the dev branch, commit them to trigger the GitHub Actions pipeline. This will automatically deploy the updated resources to the development environment. After verifying everything in dev, you can then merge the dev branch into the main branch.</p>
<p>Merging changes into the main branch also automatically triggers the deployment pipeline for the production environment. This way, all necessary updates are applied and production resources are deployed seamlessly.</p>
<p>You can monitor the deployment process and review detailed logs of each GitHub Actions run by navigating to the <strong>Actions</strong> tab in your GitHub repository.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353173167/f1775dbc-732c-432d-9ee0-9572b8b9908f.png" alt="Select &quot;Actions&quot; in the top right of GitHub repository options." class="image--center mx-auto" width="519" height="220" loading="lazy"></p>
<p>The logs provide visibility into each step of the pipeline, helping you verify that everything is working as expected.</p>
<p>You can select any build run to review detailed logs for both the development and production environment deployments so you can track the progress and ensure that everything is running smoothly.</p>
<p>Navigate to the specific build run in GitHub Actions, as demonstrated in the image below. There, you can view the execution details and outcomes for either the development or production pipelines.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353205715/dd221126-4fed-4032-8b51-e883f1177173.png" alt="Pipeline run logs for the different branch environments (main, dev)" class="image--center mx-auto" width="1258" height="379" loading="lazy"></p>
<p>Make sure to thoroughly test both the development and production environments to confirm successful pipeline executing.</p>
<h2 id="heading-step-6-test-and-validate-prod-and-dev-apis-using-postman">Step 6: Test and Validate Prod and Dev APIs using Postman</h2>
<p>Now that the APIs and resources are deployed and configured, we need to locate the unique API endpoints (URLs) generated by AWS to begin making requests to test functionality.</p>
<p>These URLs can test the API functionality by simply pasting them into a web browser. The API URLs are found in the output results of your CI/CD build.</p>
<p>To retrieve them, navigate to the GitHub Actions logs, select the most recent environment’s successful build, and click <strong>deploy</strong> to check the deployment details for the generated API endpoints.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353236275/7cbed3e1-d65a-4fa6-9dff-9974d1c2022a.png" alt="&quot;Deploy&quot; button that allows you to view log details." class="image--center mx-auto" width="512" height="253" loading="lazy"></p>
<p>Click on the <strong>Deploy to AWS</strong> stage for the selected environment (Prod or Dev) in your GitHub Actions logs. Once there, you’ll find the generated API URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353272312/43eee369-618f-45f9-b9aa-6ffb6e19061b.png" alt="Detailed logs of a specific build run to review for errors or success." class="image--center mx-auto" width="1122" height="977" loading="lazy"></p>
<p>Copy and save this URL, as it will be needed when testing your API’s functionality. This URL is your gateway to verifying that the deployed API works as expected.</p>
<p>Now copy one of the generated API URLs and paste it into your browser. You will see an empty array or list displayed in the response. This actually confirms that the API is functioning correctly and that you are successfully retrieving data from the DynamoDB table.</p>
<p>Even though the list is empty, it indicates that the API can connect to the database and return information.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353307388/23791725-71d7-4b1d-908c-c0f5e0fb073b.png" alt="Empty list result when inserting API URL in browser." class="image--center mx-auto" width="597" height="161" loading="lazy"></p>
<p>To verify that your API works across both environments, repeat the steps for the other API environment (Prod and Dev).</p>
<p>For more comprehensive testing, we’ll use Postman to test all the API methods, <strong>Create</strong>, <strong>Read</strong>, <strong>Update</strong> and <strong>Delete</strong>, and perform these tests for both the development and production environments.</p>
<p>To test the <strong>GET</strong> method, use Postman to send a GET request to the API’s endpoint using the URL. You will receive the same response, an empty list of coffee orders as seen in the bottom of the image below. This confirms the API’s ability to retrieve data successfully, as shown in the image below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353336998/17fff84a-a784-464f-a89e-9c73f3e863a0.png" alt="Testing the GET method using Postman." class="image--center mx-auto" width="1398" height="898" loading="lazy"></p>
<p>To actually create an order, let’s test the <strong>POST</strong> method. Use Postman again to make a POST request to the API endpoint, providing the customer’s name and coffee blend in the request body, as show below :</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"customer_name"</span>: <span class="hljs-string">"REXTECH"</span>,
  <span class="hljs-attr">"coffee_blend"</span>: <span class="hljs-string">"Black"</span>
}
</code></pre>
<p>The response will be a success message with a unique OrderId of the order placed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353370197/4f3ab8df-4f1f-4c66-888c-4069b60151f9.png" alt="Testing the POST method using Postman." class="image--center mx-auto" width="1396" height="1016" loading="lazy"></p>
<p>Verify that the new order was saved in the DynamoDB table by reviewing the items in the environments specific table :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353402967/afbd2080-b66f-46ac-ac79-24a4d360871d.png" alt="Verifying new order is stored in DynamoDB table." class="image--center mx-auto" width="1135" height="223" loading="lazy"></p>
<p>To test the <strong>PUT</strong> method, make a PUT request to the API endpoint by providing the previous order ID and a new order status in the request body as shown below :</p>
<pre><code class="lang-json">{                                                 
  <span class="hljs-attr">"order_id"</span>: <span class="hljs-string">"42a81c27-1421-4025-9bef-72b14e723c34"</span>,
  <span class="hljs-attr">"new_status"</span>: <span class="hljs-string">"Ready"</span>,                                             
  <span class="hljs-attr">"customer_name"</span>: <span class="hljs-string">"REXTECH"</span>                                             
}
</code></pre>
<p>The response will be a successful order update message with the OrderId of the order placed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353432881/f5354746-9b42-4fc9-bb70-5c18f076ecea.png" alt="Testing the PUT method using Postman." class="image--center mx-auto" width="1398" height="942" loading="lazy"></p>
<p>You can also verify that the order status was updated from the DynamoDB table item.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353463923/e6a2978c-bbb5-49c0-9b94-36ea404b8c11.png" alt="Verifying order status update in DynamoDB table." class="image--center mx-auto" width="1140" height="226" loading="lazy"></p>
<p>To test the <strong>DELETE</strong> method, using Postman, make a DELETE request providing the previous order ID and the customer name in the request body as shown below:</p>
<pre><code class="lang-plaintext">{                                                 
  "order_id": "42a81c27-1421-4025-9bef-72b14e723c34",
  "customer_name": "REXTECH"
}
</code></pre>
<p>The response will be a successful order deleted message with the order ID of the order placed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353509090/e61a8ab8-7ce3-44b1-a122-34d29b5a5734.png" alt="Testing the DELETE method using Postman." class="image--center mx-auto" width="1400" height="1018" loading="lazy"></p>
<p>Again, you can verify that the order has been deleted in the DynamoDB table.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353541300/d6ed82aa-12ca-4cc2-9b0b-1b86be9557ee.png" alt="Verifying empty items in DynamoDB table." class="image--center mx-auto" width="1144" height="212" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That’s it – congratulations! You’ve successfully completed all the steps. We’ve built a serverless REST API that supports CRUD (<strong>Create, Read, Update, Delete)</strong> functionality with API Gateway, Lambda, DynamoDB, Serverless Framework and Node.js, automating deployment of approved code changes with Github Actions.</p>
<p>If you’ve gotten this far, <strong>thanks for reading!</strong> I hope it was worthwhile to you.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724353582971/091ac912-1d87-4179-addc-cc81a90c8657.png" alt="091ac912-1d87-4179-addc-cc81a90c8657" class="image--center mx-auto" width="847" height="502" loading="lazy"></p>
<p><a target="_blank" href="https://www.linkedin.com/in/ifeanyi-otuonye/">Ifeanyi Otuonye</a> is a 6X AWS Certified Cloud Engineer skilled in DevOps, Technical Writing and instructional expertise as a Technical Instructor. He is motivated by his eagerness to learn and develop and thrives in collaborative environments. Before transitioning to the Cloud, he spend six years as a Professional Track and Field athlete.</p>
<p>In the early 2022, he strategically embarked on an mission to be a Cloud/DevOps Engineer through self study and joining a 6 month accelerated Cloud program.</p>
<p>In May 2023, he accomplished that goal and landed his first Cloud Engineering role and has now set another personal mission to empower other individuals on their journey to the Cloud.</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[ 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 Build a Complete Back End System with Serverless ]]>
                </title>
                <description>
                    <![CDATA[ By Sam Williams This article will teach you how to build and deploy everything you need to be able to build a back-end for your application. We'll be using AWS to host all of this and deploying it all using the Serverless Framework. By the end of thi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/complete-back-end-system-with-serverless/</link>
                <guid isPermaLink="false">66d460d1c7632f8bfbf1e49b</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Backend Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 27 Dec 2019 09:33:44 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/12/thumbnail.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sam Williams</p>
<p>This article will teach you how to build and deploy everything you need to be able to build a back-end for your application. We'll be using AWS to host all of this and deploying it all using the Serverless Framework.</p>
<p>By the end of this article you'll know how to:</p>
<ul>
<li><a class="post-section-overview" href="#">Set up your AWS account to work with the Serverless Framework</a></li>
<li><a class="post-section-overview" href="#">Set up a Serverless Project and deploy a Lambda</a></li>
<li><a class="post-section-overview" href="#">Create private cloud storage with S3 bucket and upload files from your computer</a></li>
<li><a class="post-section-overview" href="#">Deploy an API using API Gateway and AWS Lambda</a></li>
<li><a class="post-section-overview" href="#">Create a serverless database table with AWS DynamoDB</a></li>
<li><a class="post-section-overview" href="#">Create an API to get data from your DynamoDB table</a></li>
<li><a class="post-section-overview" href="#">Create an API to add data to your DynamoDB table</a></li>
<li><a class="post-section-overview" href="#">Create APIs to store files and get files from your S3 bucket</a></li>
<li><a class="post-section-overview" href="#">Secure all of your API endpoints with API keys</a></li>
</ul>
<p>Being able to do all these things gives you the ability to create all the functionality needed from most application back ends.</p>
<p><a class="anchor" id="setup"></a></p>
<h1 id="heading-serverless-setup-with-aws"><strong>Serverless Setup with AWS</strong></h1>
<p>The Serverless Framework is a tool that we can use as developers to configure and deploy services from our computers. There's a bit of setup to allow all of this to work together and this section will show you how to do that.</p>
<p><a target="_blank" href="https://www.youtube.com/embed/videoseries?list=PLmexTtcbIn_gP8bpsUsHfv-58KsKPsGEo">Embedded content</a></p>
<p>To allow Serverless to do work on your account, you need to set up a user for it. To do this, navigate into AWS and search for "IAM" (Identity and Access Management).</p>
<p>Once on the IAM Page, click on <em>Users</em> in the list on the left hand side. This will open the list of users on your account. From here we'll be clicking <em>Add user.</em></p>
<p>We need to create a user which has <em>Programmatic access</em> and I've called my user <em>ServerlessAccount</em>, but the name doesn't matter too much.</p>
<p><img src="https://completecoding.io/content/images/2019/08/createUser-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, we need to give the user some permissions. When on the permissions screen, select <em>Attach existing policies directly</em> and then select <em>AdministratorAccess</em>. This will give the Serverless Framework permission to create all the resources it needs to.</p>
<p>We don't need to add any tags, so we can move straight onto <em>Review</em>.</p>
<p><img src="https://completecoding.io/content/images/2019/08/credential.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On the review window, you'll see the user has been given an <em>Access key ID</em> and a <em>Secret access key</em>. We'll be needing those in the next part so keep this page open.</p>
<h3 id="heading-serverless-install-and-configuration"><strong>Serverless Install and Configuration</strong></h3>
<p>Now that we've created our user, we need to install the Serverless Framework on our machine.</p>
<p>Open up a terminal and run this command to install Serverless globally on your computer. If you haven't got NodeJS installed check out <a target="_blank" href="https://nodejs.org/en/download/">this page.</a></p>
<pre><code>npm install -g serverless
</code></pre><p>Now that we've got Serverless installed, we need to set up the credentials for Serverless to use. Run this command, putting your <em>access key ID</em> and <em>Secret access key</em> in the correct places:</p>
<pre><code class="lang-js">serverless config credentials --provider aws --key ${Your access key ID} --secret ${Your secret access key} --profile serverlessUser
</code></pre>
<p>Once this has been run, you're all set up with Serverless.</p>
<p><a class="anchor" id="firstlambda"></a></p>
<h1 id="heading-deploying-your-first-aws-lambda"><strong>Deploying Your First AWS Lambda</strong></h1>
<p>With out serverlessUser set up, we want to deploy something using the Serverless Framework. We can use Serverless templates to setup a basic project that we can deploy. This will be the base for the whole of this Serverless project.</p>
<p><a target="_blank" href="https://www.youtube.com/embed/sku9Rrci-tE?feature=oembed">Embedded content</a></p>
<p>In your terminal we can create a Serverless project from a template. This command will create a NodeJS Serverless project in the <code>myServerlessProject</code> folder:</p>
<pre><code>serverless create --template aws-nodejs --path myServerlessProject
</code></pre><p>If you now open the folder up in your code editor we can look at what we've created.</p>
<p><img src="https://completecoding.io/content/images/2019/12/folderStruct.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We've got two file worth talking about: <code>handler.js</code> and <code>serverless.yml</code></p>
<h3 id="heading-handlerjs"><strong>handler.js</strong></h3>
<p>This file is a function that will be uploaded as a Lambda function to your AWS account. Lambda functions are great and we'll use a lot more of them later on in the series.</p>
<h3 id="heading-serverlessyml"><strong>serverless.yml</strong></h3>
<p>This is a very important file for us. This is where all the configuration for our deployment goes. It tells Serverless what runtime to use, which account to deploy to, and what to deploy.</p>
<p>We need to make a change to this file so that our deployment works properly. In the <code>provider</code> object we need to add a new line of <code>profile: serverlessUser</code>. This tells Serverless to use the AWS credentials we created in the last section.</p>
<p>We can scroll down to <code>functions</code> and see that we have one function which is called <code>hello</code> and points to the function within the <code>handler.js</code> file. This means we will be deploying this Lambda function as part of this project.</p>
<p>We'll learn a lot more about this <code>serverless.yml</code> file later on in this article.</p>
<h2 id="heading-deploying-our-project"><strong>Deploying Our Project</strong></h2>
<p>Now that we've looked at the files it's time to do our first deployment. Open up a terminal and navigate to our project folder. Deploying is as simple as typing:</p>
<pre><code>serverless deploy
</code></pre><p>This takes a while, but when it's done we can check that everything has deployed successfully.</p>
<p>Open up your browser and navigate to your AWS account. Search for <code>Lambda</code> and you'll see a list of all your Lambda functions. (If you don't see any then check that your region is set to <code>N. Virginia</code>). You should see the <code>myserverlessproject-dev-hello</code> Lambda which contains the exact code that is in the <code>handler.js</code> file in your project folder.</p>
<p><a class="anchor" id="s3"></a></p>
<h1 id="heading-deploying-an-s3-bucket-and-uploading-files"><strong>Deploying an S3 Bucket and Uploading Files</strong></h1>
<p>In this section we're going to learn how we can deploy an Amazon S3 bucket and then sync up files from our computer. This is how we can start using S3 as cloud storage for our files.</p>
<p><a target="_blank" href="https://www.youtube.com/embed/8dc72i41r1A?feature=oembed">Embedded content</a></p>
<p>Open up the <code>serverless.yml</code> file and remove all the commented out lines. Scroll to the bottom of the file and add the following code to include our S3 resources:</p>
<pre><code>resources:
    Resources:
        DemoBucketUpload:
            Type: AWS::S3::Bucket
            <span class="hljs-attr">Properties</span>:
                BucketName: EnterAUniqueBucketNameHere
</code></pre><p>Change the name of the bucket and we're ready to deploy again. Open up your terminal again and run <code>serverless deploy</code>. You may get an error saying that the bucket name is not unique, in which case you'll need to change the bucket name, save the file and rerun the command.</p>
<p>If it is successful we can then go and see our new S3 bucket in our AWS Console through our browser. Search for <code>S3</code> and then you should see your newly created bucket.</p>
<h2 id="heading-syncing-up-your-files"><strong>Syncing up your files</strong></h2>
<p>Having a bucket is great, but now we need to put files in the bucket. We're going to be using a Serverless plugin called S3 Sync to do this for us. To add this plugin to our project we need to define the plugins. After your provider object, add this code:</p>
<pre><code>plugins:
    - serverless-s3-sync
</code></pre><p>This plugin also needs some custom configuration, so we add another field to our <code>serverless.yml</code> file, changing out the bucket name for yours:</p>
<pre><code>custom:
    s3Sync:
        - bucketName: YourUniqueBucketName
          <span class="hljs-attr">localDir</span>: UploadData
</code></pre><p>This section of code is telling the S3 Sync plugin to upload the contents of the <code>UploadData</code> folder to our bucket. We don't currently have that folder so we need to create it and add some files. You can add a text file, an image, or whatever you want to be uploaded, just make sure there is at least 1 file in the folder.</p>
<p>The last thing we need to do is to install the plugin. Luckily, all Serverless plugins are also npm packages, so we can install it by running <code>npm install --save-dev serverless-s3-sync</code> in our terminal.</p>
<p>As we've done before, we can now run <code>serverless deploy</code> and wait for the deployment to complete. Once it is complete we can go back into our browser and into our bucket and we should see all the files that we put in the <code>UploadData</code> folder in our project.</p>
<p><img src="https://completecoding.io/content/images/2019/12/Screenshot-2019-12-10-at-07.12.07.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><a class="anchor" id="api"></a></p>
<h1 id="heading-creating-an-api-with-lambda-and-api-gateway"><strong>Creating an API with Lambda and API Gateway</strong></h1>
<p>In this section we'll learn to do one of the most useful things with Serverless: create an API. Creating an API allows you to do so many things, from getting data from databases, S3 storage, hitting other APIs, and much more!</p>
<p><a target="_blank" href="https://www.youtube.com/embed/Jruqo0KVOWk?feature=oembed">Embedded content</a></p>
<p>To create the API we first need to create a new Lambda function to handle the request. We're going to make a few Lambdas, so we're going to create a <code>lambdas</code> folder in our project with two subfolders, <code>common</code> and <code>endpoints</code>.</p>
<p><img src="https://completecoding.io/content/images/2019/12/Screenshot-2019-12-10-at-07.47.27.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Inside the endpoints folder we can add a new file called <code>getUser.js</code>. This API is going to allow someone to make a request and get back data based on the ID of a user. This is the code for the API:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Responses = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/API_Responses'</span>);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> event =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'event'</span>, event);

    <span class="hljs-keyword">if</span> (!event.pathParameters || !event.pathParameters.ID) {
        <span class="hljs-comment">// failed without an ID</span>
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'missing the ID from the path'</span> });
    }

    <span class="hljs-keyword">let</span> ID = event.pathParameters.ID;

    <span class="hljs-keyword">if</span> (data[ID]) {
        <span class="hljs-comment">// return the data</span>
        <span class="hljs-keyword">return</span> Responses._200(data[ID]);
    }

    <span class="hljs-comment">//failed as ID not in the data</span>
    <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'no ID in data'</span> });
};

<span class="hljs-keyword">const</span> data = {
    <span class="hljs-number">1234</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">'Anna Jones'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">25</span>, <span class="hljs-attr">job</span>: <span class="hljs-string">'journalist'</span> },
    <span class="hljs-number">7893</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">'Chris Smith'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">52</span>, <span class="hljs-attr">job</span>: <span class="hljs-string">'teacher'</span> },
    <span class="hljs-number">5132</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">'Tom Hague'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">23</span>, <span class="hljs-attr">job</span>: <span class="hljs-string">'plasterer'</span> },
};
</code></pre>
<p>If the request doesn't contain an ID then we return a failed response. If there is data for that ID then we return that data. If there isn't data for that user ID then we also return a failure response.</p>
<p>As you may have noticed we are requiring in the <code>Responses</code> object from <code>API_Responses</code>. These responses are going to be common to every API that we make, so making this code importable is a smart move. Create a new file called <code>API_Responses.js</code> in the <code>common</code> folder and add this code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Responses = {
    _200(data = {}) {
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">headers</span>: {
                <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
                <span class="hljs-string">'Access-Control-Allow-Methods'</span>: <span class="hljs-string">'*'</span>,
                <span class="hljs-string">'Access-Control-Allow-Origin'</span>: <span class="hljs-string">'*'</span>,
            },
            <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(data),
        };
    },

    _400(data = {}) {
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">headers</span>: {
                <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
                <span class="hljs-string">'Access-Control-Allow-Methods'</span>: <span class="hljs-string">'*'</span>,
                <span class="hljs-string">'Access-Control-Allow-Origin'</span>: <span class="hljs-string">'*'</span>,
            },
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">400</span>,
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(data),
        };
    },
};

<span class="hljs-built_in">module</span>.exports = Responses;
</code></pre>
<p>This set of functions are used to simplify the creation of the correct response needed when using a Lambda with API Gateway (which we'll do in a sec). The methods add headers, a status code, and stringify any data that needs to be returned.</p>
<p>Now that we have the code for our API, we need to set it up in our <code>serverless.yml</code> file. Scroll to the <code>functions</code> section of the <code>serverless.yml</code> file. In the last part of this guide we deployed the <code>hello</code> function, but we no longer need that. Delete the functions object and replace it with this:</p>
<pre><code class="lang-yml"><span class="hljs-attr">functions:</span>
    <span class="hljs-attr">getUser:</span>
        <span class="hljs-attr">handler:</span> <span class="hljs-string">lambdas/endpoints/getUser.handler</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">get-user/{ID}</span>
                  <span class="hljs-attr">method:</span> <span class="hljs-string">GET</span>
                  <span class="hljs-attr">cors:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>This code is creating a new Lambda function called <code>getUser</code> which is in the file of <code>lambdas/getUser</code> on the method of <code>handler</code>. We then define the events that can trigger this lambda function to run.</p>
<p>To make a Lambda into an API we can add a <code>http</code> event. This tells Serverless to add an API Gateway to this account and then we can define the API endpoint using <code>path</code>. In this case <code>get-user/{ID}</code> means the URL will be <code>https://${something-provided-by-API-Gateway}/get-user/{ID}</code>, where the ID is passed into the Lambda as a path parameter. We also set the method to <code>GET</code> and enable CORS so that we could access this endpoint from a front end application if we wanted.</p>
<p>We can now deploy again, and this time we can use the shorthand command <code>sls deploy</code>. This only saves a few characters, but helps avoid a lot of typos. When this is completed we'll get an output that also includes a list of endpoints. We can copy our endpoint and head over to a browser to test it out.</p>
<p><img src="https://completecoding.io/content/images/2019/12/Screenshot-2019-12-10-at-07.19.00.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If we paste our API URL into our browser and then add an ID to the end of 5132 we should get back a response of <code>{ name: 'Tom Hague', age: 23, job: 'plasterer' }</code>. If we enter a different ID such as 1234 we'll get different data, but entering an ID of 7890 or not entering an ID will return an error.</p>
<p>If we want to add more data to our API, we can simply add a new row to the data object in the <code>getUser.js</code> file. We can then run a special command which only deploys one function, <code>sls deploy -f ${functionName}</code>. So for us that is:</p>
<pre><code>sls deploy -f getUser
</code></pre><p>If you now make a request using the ID of the new data, the API will return that new data instead of an error.</p>
<p><a class="anchor" id="dynamo"></a></p>
<h1 id="heading-creating-a-database-on-aws"><strong>Creating a Database on AWS</strong></h1>
<p>DynamoDB is a fully hosted, non-relational database on AWS. This is the perfect solution for storing data that you need to access and update regularly. In this section we're going to learn how we can create a DynamoDB table with Serverless.</p>
<p><a target="_blank" href="https://www.youtube.com/embed/1de8NkTseqM?feature=oembed">Embedded content</a></p>
<p>In our <code>serverless.yml</code> file we're going to add some configuration to the <code>Resources</code> section:</p>
<pre><code>resources:
    Resources:
        DemoBucketUpload:
            Type: AWS::S3::Bucket
            <span class="hljs-attr">Properties</span>:
                BucketName: ${<span class="hljs-attr">self</span>:custom.bucketName}
        # New Code
        <span class="hljs-attr">MyDynamoDbTable</span>:
            Type: AWS::DynamoDB::Table
            <span class="hljs-attr">Properties</span>:
                TableName: ${<span class="hljs-attr">self</span>:custom.tableName}
                <span class="hljs-attr">AttributeDefinitions</span>:
                    - AttributeName: ID
                      <span class="hljs-attr">AttributeType</span>: S
                <span class="hljs-attr">KeySchema</span>:
                    - AttributeName: ID
                      <span class="hljs-attr">KeyType</span>: HASH
                <span class="hljs-attr">BillingMode</span>: PAY_PER_REQUEST
</code></pre><p>In this code we can see that we are creating a new DynamoDB table with a <code>TableName</code> of <code>${self:custom.tableName}</code>, defining an attribute of <code>ID</code> and setting the billing mode to pay per request.</p>
<p>This is our first look at the use of variables in our <code>serverless.yml</code> file. We can use variables for a few reasons and they can make our jobs much easier. In this case, we're referencing the variable <code>custom.tableName</code>. We can then reference this variable from multiple locations without having to copy and paste the table name. To get this to work we also need to add <code>tableName</code> to the custom section. In our case we're going to add the line <code>tableName: player-points</code> to create a table to store the points a player has. This table name only needs to be unique to your account.</p>
<p>When defining a table you need to define at least one of the fields which will be your unique identifying field. Because DynamoDB is a non-relational database, you don't need to define the full schema. In our case we've defined the <code>ID</code>, stating that it has an attribute type of string and a key type of <code>HASH</code>.</p>
<p>The last part of the definition is the billing mode. There are two ways to pay for DynamoDB:</p>
<ul>
<li>pay per request</li>
<li>provisioned resources.</li>
</ul>
<p>Provisioned resources lets you define how much data you're going to be reading and writing to the table. The issues with this are that if you start using more your requests get throttled, and that you pay for the resource even if no one is using it.</p>
<p>Pay Per Request it's much simpler as you just pay per request. This means if you have no one using it then you pay nothing and if your have hundreds of people using it at once, all the requests work. For this added flexibility you pay slightly more for Pay Per Request, but in the long run it usually works out to be cheaper.</p>
<p>Once we've run <code>sls deploy</code> again we can open up our AWS console and search for DynamoDB. We should be able to see our new table and we can see that there is nothing in there.</p>
<p>To add data to the table, click <code>Create item</code>, give it a unique ID, click the plus button and <code>append</code> to a new field and select the type of string. We need to give it a field of <code>name</code> and a value of <code>Jess</code>. Add a number field of <code>score</code> set to <code>12</code>. Click save and you now have data in your dynamo table.</p>
<p><img src="https://completecoding.io/content/images/2019/12/Screenshot-2019-12-10-at-07.57.54.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><a class="anchor" id="dynamoGet"></a></p>
<h1 id="heading-getting-data-from-your-dynamodb-table"><strong>Getting Data from your DynamoDB Table</strong></h1>
<p>Now that we have our Dynamo table created, we want to be able to get and add data to the table. We're going to start with getting data from the table with a get endpoint.</p>
<p><a target="_blank" href="https://www.youtube.com/embed/CpDFfSXRG04?feature=oembed">Embedded content</a></p>
<p>We're going to create a new file in our <code>endpoints</code> folder called <code>getPlayerScore.js</code>. This Lambda endpoint is going to handle the requests for a user and get that data from the Dynamo table.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Responses = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/API_Responses'</span>);
<span class="hljs-keyword">const</span> Dynamo = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/Dynamo'</span>);

<span class="hljs-keyword">const</span> tableName = process.env.tableName;

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> event =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'event'</span>, event);

    <span class="hljs-keyword">if</span> (!event.pathParameters || !event.pathParameters.ID) {
        <span class="hljs-comment">// failed without an ID</span>
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'missing the ID from the path'</span> });
    }

    <span class="hljs-keyword">let</span> ID = event.pathParameters.ID;

    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> Dynamo.get(ID, tableName).catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error in Dynamo Get'</span>, err);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    });

    <span class="hljs-keyword">if</span> (!user) {
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Failed to get user by ID'</span> });
    }

    <span class="hljs-keyword">return</span> Responses._200({ user });
};
</code></pre>
<p>The code used here is very similar to the code inside the <code>getUser.js</code> file. We are checking that a path parameter of ID exists, getting the user data, and then returning the user. The main difference is how we are getting the user.</p>
<p>We have imported the <code>Dynamo</code> function object and are calling <code>Dynamo.get</code>. We're passing in the ID and the table name and then catching any errors. We now need to create that <code>Dynamo</code> function object in a new file called <code>Dynamo.js</code> in the common folder.</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> documentClient = <span class="hljs-keyword">new</span> AWS.DynamoDB.DocumentClient();

<span class="hljs-keyword">const</span> Dynamo = {
    <span class="hljs-keyword">async</span> get(ID, TableName) {
        <span class="hljs-keyword">const</span> params = {
            TableName,
            <span class="hljs-attr">Key</span>: {
                ID,
            },
        };

        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> documentClient.get(params).promise();

        <span class="hljs-keyword">if</span> (!data || !data.Item) {
            <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`There was an error fetching the data for ID of <span class="hljs-subst">${ID}</span> from <span class="hljs-subst">${TableName}</span>`</span>);
        }
        <span class="hljs-built_in">console</span>.log(data);

        <span class="hljs-keyword">return</span> data.Item;
    },
};
<span class="hljs-built_in">module</span>.exports = Dynamo;
</code></pre>
<p>Reading and writing to Dynamo requires a reasonable amount of code. We could write that code every time we want to use Dynamo but it is much cleaner to have functions to simplify the process for us.</p>
<p>The file first imports AWS and then creates an instance of the DynamoDB Document Client. The document client is the easiest way for us to work with Dynamo from our Lambdas. We create a <code>Dynamo</code> object with an async get function. The only things we need to make a request are an ID and a table name. We format those into the correct parameter format for the DocumentClient, await a <code>documentClient.get</code> request, and make sure that we add <code>.promise()</code> to the end. This turns the request from a callback to a promise which is much easier to work with. We check that we managed to get an item from Dynamo and then we return that item.</p>
<p>We now have the all the code that we need, we have to update our <code>serverless.yml</code> file too. The first thing to do is to add our new API endpoint by adding it to our list of functions.</p>
<pre><code>    getPlayerScore:
        handler: lambdas/endpoints/getPlayerScore.handler
        <span class="hljs-attr">events</span>:
            - http:
                  path: get-player-score/{ID}
                  <span class="hljs-attr">method</span>: GET
                  <span class="hljs-attr">cors</span>: <span class="hljs-literal">true</span>
</code></pre><p>There are two more changes that we need to make to get our endpoint working:</p>
<ul>
<li>environment variables</li>
<li>permissions</li>
</ul>
<p>You may have noticed in the <code>getPlayerScore.js</code> file we had a line of code like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> tableName = process.env.tableName;
</code></pre>
<p>This is where we are getting the table name from the environment variables of the Lambda. To create our Lambda with the correct environment variables, we need to set a new object in the provider called <code>environment</code> with a field of <code>tableName</code> and a value of <code>${self:custom.tableName}</code>. This will ensure that we are making the request to correct table.</p>
<p>We also need to give our Lambdas permissions to access Dynamo. We have to add another field to the provider called <code>iamRoleStatements</code>. This has an array of policies which can allow or disallow access to certain services or resources:</p>
<pre><code>provider:
    name: aws
    <span class="hljs-attr">runtime</span>: nodejs10.x
    <span class="hljs-attr">profile</span>: serverlessUser
    <span class="hljs-attr">region</span>: eu-west<span class="hljs-number">-1</span>
    <span class="hljs-attr">environment</span>:
        tableName: ${<span class="hljs-attr">self</span>:custom.tableName}
    <span class="hljs-attr">iamRoleStatements</span>:
        - Effect: Allow
          <span class="hljs-attr">Action</span>:
              - dynamodb:*
          Resource: <span class="hljs-string">'*'</span>
</code></pre><p>As all this has been added to the provider object, it will be applied to all Lambdas.</p>
<p>We can now run <code>sls deploy</code> again to deploy our new endpoint. When that is done we should get an output with a new endpoint of <code>https://${something-provided-by-API-Gateway}/get-player-score/{ID}</code>. If we copy that URL into a browser tab and add the ID of the player that we created in the last section, we should get a response.</p>
<p><img src="https://completecoding.io/content/images/2019/12/Screenshot-2019-12-10-at-07.59.02.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><a class="anchor" id="dynamoPut"></a></p>
<h1 id="heading-adding-new-data-to-dynamodb"><strong>Adding New Data to DynamoDB</strong></h1>
<p>Being able to get data from Dynamo is cool, but it's quite useless if we can't also add new data to the table as well. We're going to be creating a POST endpoint to create new data in our Dynamo table.</p>
<p><a target="_blank" href="https://www.youtube.com/embed/AguTaMQGACE?feature=oembed">Embedded content</a></p>
<p>Start by creating a new file in our endpoints folder called <code>createPlayerScore.js</code> and adding this code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Responses = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/API_Responses'</span>);
<span class="hljs-keyword">const</span> Dynamo = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/Dynamo'</span>);

<span class="hljs-keyword">const</span> tableName = process.env.tableName;

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> event =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'event'</span>, event);

    <span class="hljs-keyword">if</span> (!event.pathParameters || !event.pathParameters.ID) {
        <span class="hljs-comment">// failed without an ID</span>
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'missing the ID from the path'</span> });
    }

    <span class="hljs-keyword">let</span> ID = event.pathParameters.ID;
    <span class="hljs-keyword">const</span> user = <span class="hljs-built_in">JSON</span>.parse(event.body);
    user.ID = ID;

    <span class="hljs-keyword">const</span> newUser = <span class="hljs-keyword">await</span> Dynamo.write(user, tableName).catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error in dynamo write'</span>, err);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    });

    <span class="hljs-keyword">if</span> (!newUser) {
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Failed to write user by ID'</span> });
    }

    <span class="hljs-keyword">return</span> Responses._200({ newUser });
};
</code></pre>
<p>This code is very similar to the <code>getPlayerScore</code> code with a few changes. We are getting the user from the body of the request, adding the ID to the user, and then passing that to a <code>Dynamo.write</code> function. We need to parse the event body as API Gateway stringifies it before passing it to the Lambda.</p>
<p>We now need to modify the common <code>Dynamo.js</code> file to add the <code>.write</code> method. This performs very similar steps to the <code>.get</code> function and returns the newly created data:</p>
<pre><code class="lang-js">    <span class="hljs-keyword">async</span> write(data, TableName) {
        <span class="hljs-keyword">if</span> (!data.ID) {
            <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'no ID on the data'</span>);
        }

        <span class="hljs-keyword">const</span> params = {
            TableName,
            <span class="hljs-attr">Item</span>: data,
        };

        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> documentClient.put(params).promise();

        <span class="hljs-keyword">if</span> (!res) {
            <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`There was an error inserting ID of <span class="hljs-subst">${data.ID}</span> in table <span class="hljs-subst">${TableName}</span>`</span>);
        }

        <span class="hljs-keyword">return</span> data;
    }
</code></pre>
<p>We've created the endpoint and common code, so the last thing we need to do is modify the <code>serverless.yml</code> file. As we added the environment variable and permissions in the last section, we just need to add the function and API configuration. This endpoint is different from the previous two because the method is <code>POST</code> instead of <code>GET</code>:</p>
<pre><code>    createPlayerScore:
        handler: lambdas/endpoints/createPlayerScore.handler
        <span class="hljs-attr">events</span>:
            - http:
                  path: create-player-score/{ID}
                  <span class="hljs-attr">method</span>: POST
                  <span class="hljs-attr">cors</span>: <span class="hljs-literal">true</span>
</code></pre><p>Deploying this with <code>sls deploy</code> will now create three endpoints, including our <code>create-player-score</code> endpoint. Testing a <code>POST</code> endpoint is more complex than a <code>GET</code> request, but luckily there are tools to help us out. I use <a target="_blank" href="https://www.getpostman.com/">Postman</a> to test all my endpoints as it makes it quick and easy.</p>
<p>Create a new request and paste in your <code>create-player-score</code> URL. You need to change the request type to <code>POST</code> and set the ID at the end of the URL. Because we're doing a POST request we can send up data within the body of the request. Click <code>body</code> then <code>raw</code> and select <code>JSON</code> as the body type. You can then add the data that you want to put into your table. When you click <code>Send</code>, you should get a successful response:</p>
<p><img src="https://completecoding.io/content/images/2019/12/postman.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To validate that your data has been added to the table, you can make a get-player-score request with the ID of the new data you just created. You can also go into the Dynamo console and look at all the items in the table.</p>
<p><a class="anchor" id="s3API"></a></p>
<h1 id="heading-creating-s3-get-and-post-endpoints"><strong>Creating S3 GET and POST Endpoints</strong></h1>
<p>Dynamo is a brilliant database storage solution, but sometimes it isn't the best storage solution. If you've got data that isn't going to change and you want to save some money, or if you want to store files other than JSON, then you might want to consider Amazon S3.</p>
<p><a target="_blank" href="https://www.youtube.com/embed/MlKpK0WqTSs?feature=oembed">Embedded content</a></p>
<p>Creating endpoints to get and create files in S3 is very similar to DynamoDB. We need to create two endpoint files, a common S3 file, and modify the <code>serverless.yml</code> file.</p>
<p>We're going to start with adding a file to S3. Create a <code>createFile.js</code> file in the endpoints folder and add this code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Responses = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/API_Responses'</span>);
<span class="hljs-keyword">const</span> S3 = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/S3'</span>);

<span class="hljs-keyword">const</span> bucket = process.env.bucketName;

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> event =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'event'</span>, event);

    <span class="hljs-keyword">if</span> (!event.pathParameters || !event.pathParameters.fileName) {
        <span class="hljs-comment">// failed without an fileName</span>
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'missing the fileName from the path'</span> });
    }

    <span class="hljs-keyword">let</span> fileName = event.pathParameters.fileName;
    <span class="hljs-keyword">const</span> data = <span class="hljs-built_in">JSON</span>.parse(event.body);

    <span class="hljs-keyword">const</span> newData = <span class="hljs-keyword">await</span> S3.write(data, fileName, bucket).catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error in S3 write'</span>, err);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    });

    <span class="hljs-keyword">if</span> (!newData) {
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Failed to write data by filename'</span> });
    }

    <span class="hljs-keyword">return</span> Responses._200({ newData });
};
</code></pre>
<p>This code is almost identical to the <code>createPlayerScore.js</code> code, but uses a <code>filename</code> instead of an <code>ID</code> and <code>S3.write</code> instead of <code>Dynamo.write</code>.</p>
<p>Now we need to create our <code>S3</code> common code to simplify requests made to S3:</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> s3Client = <span class="hljs-keyword">new</span> AWS.S3();

<span class="hljs-keyword">const</span> S3 = {
    <span class="hljs-keyword">async</span> write(data, fileName, bucket) {
        <span class="hljs-keyword">const</span> params = {
            <span class="hljs-attr">Bucket</span>: bucket,
            <span class="hljs-attr">Body</span>: <span class="hljs-built_in">JSON</span>.stringify(data),
            <span class="hljs-attr">Key</span>: fileName,
        };
        <span class="hljs-keyword">const</span> newData = <span class="hljs-keyword">await</span> s3Client.putObject(params).promise();
        <span class="hljs-keyword">if</span> (!newData) {
            <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'there was an error writing the file'</span>);
        }
        <span class="hljs-keyword">return</span> newData;
    },
};
<span class="hljs-built_in">module</span>.exports = S3;
</code></pre>
<p>Again, the code in this file is very similar to the code in <code>Dynamo.js</code>, with a few differences around the parameters for the request.</p>
<p>The last thing we need to do for writing to S3 is change the <code>severless.yml</code> file. We need to do four things: add environment variables, add permissions, add the function, and add an S3 bucket.</p>
<p>In the provider we can add a new environment variable of <code>bucketName: ${self:custom.s3UploadBucket}</code>.</p>
<p>To add permission to read and write to S3 we can add a new permission to the existing policy. Straight after <code>- dynamodb:*</code> we can add the line <code>- s3:*</code>.</p>
<p>Adding the function is the same as we've been doing with all our other functions. Make sure that the path has a parameter of <code>fileName</code> as that is what you are checking for in your endpoint code:</p>
<pre><code>    createFile:
        handler: lambdas/endpoints/createFile.handler
        <span class="hljs-attr">events</span>:
            - http:
                  path: create-file/{fileName}
                  <span class="hljs-attr">method</span>: POST
                  <span class="hljs-attr">cors</span>: <span class="hljs-literal">true</span>
</code></pre><p>Lastly we need to create a new bucket to upload these files into. In the <code>custom</code> section we need to add a new field <code>s3UploadBucket</code> and set it to a unique bucket name. We also need to configure the resource. After the Dynamo table config, we can add this to create a new bucket for our file uploads:</p>
<pre><code>        s3UploadBucket:
            Type: AWS::S3::Bucket
            <span class="hljs-attr">Properties</span>:
                BucketName: ${<span class="hljs-attr">self</span>:custom.s3UploadBucket}
</code></pre><p>With this set up it is time to deploy again. Running <code>sls deploy</code> again will deploy the new upload bucket as well as the S3 write endpoint. To test the write endpoint, we'll need to head back over to Postman.</p>
<p>Copy in the <code>create-file</code> URL that you get when Serverless has completed the deployment and paste it into Postman and change the request type to <code>POST</code>. Next, what we need to do is to add the filename that we are uploading. In our case we're going to be uploading <code>car.json</code>. The last thing we need to do is add the data to the request. Select <code>Body</code> then <code>raw</code> with a type of <code>JSON</code>. You can add whatever JSON data you would like but here's some example data:</p>
<pre><code>{
    <span class="hljs-string">"model"</span>: <span class="hljs-string">"Ford Focus"</span>,
    <span class="hljs-string">"year"</span>: <span class="hljs-number">2018</span>,
    <span class="hljs-string">"colour"</span>: <span class="hljs-string">"red"</span>
}
</code></pre><p>When you post this data up, you should get a <code>200</code> response with an <code>ETag</code> reference to the file. Going into the console and your new S3 bucket you should be able to see <code>car.json</code>.</p>
<h2 id="heading-getting-data-from-s3"><strong>Getting Data from S3</strong></h2>
<p>Now that we can upload data to S3, we want to be able to get it back too. We start by creating a <code>getFile.js</code> file inside the endpoints folder:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Responses = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/API_Responses'</span>);
<span class="hljs-keyword">const</span> S3 = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/S3'</span>);

<span class="hljs-keyword">const</span> bucket = process.env.bucketName;

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> event =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'event'</span>, event);

    <span class="hljs-keyword">if</span> (!event.pathParameters || !event.pathParameters.fileName) {
        <span class="hljs-comment">// failed without an fileName</span>
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'missing the fileName from the path'</span> });
    }

    <span class="hljs-keyword">const</span> fileName = event.pathParameters.fileName;

    <span class="hljs-keyword">const</span> file = <span class="hljs-keyword">await</span> S3.get(fileName, bucket).catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error in S3 get'</span>, err);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    });

    <span class="hljs-keyword">if</span> (!file) {
        <span class="hljs-keyword">return</span> Responses._400({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Failed to read data by filename'</span> });
    }

    <span class="hljs-keyword">return</span> Responses._200({ file });
};
</code></pre>
<p>This should look pretty similar to previous <code>GET</code> endpoints we've created before. Differences are the use of the <code>fileName</code> path parameter, <code>S3.get</code>, and returning the file.</p>
<p>Inside the common <code>s3.js</code> file we need to add the <code>get</code> function. The main difference between this and getting from Dynamo is that when we get from S3, the result is not a JSON response, but a <code>Buffer</code>. This means that if we upload a JSON file, it won't come back down in JSON format, so we check if we're getting a JSON file and then transform it back to JSON:</p>
<pre><code class="lang-js">    <span class="hljs-keyword">async</span> get(fileName, bucket) {
        <span class="hljs-keyword">const</span> params = {
            <span class="hljs-attr">Bucket</span>: bucket,
            <span class="hljs-attr">Key</span>: fileName,
        };
        <span class="hljs-keyword">let</span> data = <span class="hljs-keyword">await</span> s3Client.getObject(params).promise();
        <span class="hljs-keyword">if</span> (!data) {
            <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Failed to get file <span class="hljs-subst">${fileName}</span>, from <span class="hljs-subst">${bucket}</span>`</span>);
        }
        <span class="hljs-keyword">if</span> (fileName.slice(fileName.length - <span class="hljs-number">4</span>, fileName.length) == <span class="hljs-string">'json'</span>) {
            data = data.Body.toString();
        }
        <span class="hljs-keyword">return</span> data;
    }
</code></pre>
<p>Back in our <code>serverless.yml</code> file, we can add a new function and endpoint for getting files. We've already configured the permissions and environment variables:</p>
<pre><code>    getFile:
        handler: lambdas/endpoints/getFile.handler
        <span class="hljs-attr">events</span>:
            - http:
                  path: get-file/{fileName}
                  <span class="hljs-attr">method</span>: GET
                  <span class="hljs-attr">cors</span>: <span class="hljs-literal">true</span>
</code></pre><p>As we're creating a new endpoint we need to do a full deployment again with <code>sls deploy</code>. We can then take the new <code>get-file</code> endpoint and paste it into a browser or Postman. If we add <code>car.json</code> to the end of the request we'll receive the JSON data that we uploaded earlier in this section.</p>
<p><a class="anchor" id="l9apikey"></a></p>
<h1 id="heading-securing-your-endpoints-with-api-keys"><strong>Securing Your Endpoints with API Keys</strong></h1>
<p>Being able to create API endpoints quickly and easily with Serverless is great for starting a project and creating a proof of concept. When it comes to creating a production version of your application, you need to start being more careful about who can access your endpoints. You don't want anybody being able to hit your APIs.</p>
<p><a target="_blank" href="https://www.youtube.com/embed/n5aSq1L5nIw?feature=oembed">Embedded content</a></p>
<p>To secure your APIs there are loads of methods, and in this section we're going to be implementing API keys. If you don't pass the API key with the request then it fails with an unauthorised message. You can then control who you give the API keys to, and therefore who has access to your APIs.</p>
<p>You can also add usage policies to your API keys so that you can control how much each person uses your API. This allows you to created tiered usage plans for your service.</p>
<p>To start we're going to be creating a simple API Key. To do this we need to go into our <code>serverless.yml</code> file and add some configuration to the provider.</p>
<pre><code class="lang-js">    apiKeys:
        myFirstAPIKey
</code></pre>
<p>This will create a new API key. Now we need to tell Serverless which API endpoints to protect with the API key. This has been done so that we can have some of the APIs protected, whilst some of them stay public. We specify that an endpoint needs to be protected by adding the option <code>private: true</code>:</p>
<pre><code class="lang-js">    getUser:
        handler: lambdas/endpoints/getUser.handler
        <span class="hljs-attr">events</span>:
            - http:
                  path: get-user/{ID}
                  <span class="hljs-attr">method</span>: GET
                  <span class="hljs-attr">cors</span>: <span class="hljs-literal">true</span>
                  <span class="hljs-attr">private</span>: <span class="hljs-literal">true</span>
</code></pre>
<p>You can then add this field to as many of your APIs as you would like. To deploy this we can run <code>sls deploy</code> again. When this completes, you will get back an API key in the return values. This is very important and we'll use it very soon. If you try and make a request to your <code>get-user</code> API you should get a 401 Unauthorised error.</p>
<p>To get the request to succeed, you now need to pass up an API key in the headers of the request. To do this we need to use Postman or another API request tool and add header to our get request. We do this by selecting <code>Authorisation</code> using the <code>API type</code>. The key needs to be <code>X-API-KEY</code> and the value is the key that you got as an output from your Serverless deploy:</p>
<p><img src="https://completecoding.io/content/images/2019/12/Screenshot-2019-12-20-at-20.05.53.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now when we make the request we get a successful response. This means that the only people who can access your API are people who you have given your API key to.</p>
<p>This is great, but we can do more. We can add a usage policy to this API key. This is where we can limit the number of requests a month as well as the rate at which requests can be made. This is great for running a SAAS product as you can provide an API key that gives users a set amount of API calls.</p>
<p>To create a usage plan we need to add a new object in the provider. The <code>quota</code> section defines how many requests can be made using that API key. You can change the period to either <code>DAY</code> or <code>WEEK</code> if that would suit your application better.</p>
<p>The <code>throttle</code> section allows you to control how frequently your API endpoints can be hit. Adding a throttle <code>rate limit</code> sets a maximum number of requests per second. This is very useful as it stops people from setting up a denial of service attack. The <code>burstLimit</code> allows the API to be hit more often than your <code>rateLimit</code> but only for a short period of time, normally a few seconds:</p>
<pre><code>    usagePlan:
        quota:
            limit: <span class="hljs-number">10</span>
            <span class="hljs-attr">period</span>: MONTH
        <span class="hljs-attr">throttle</span>:
            burstLimit: <span class="hljs-number">2</span>
            <span class="hljs-attr">rateLimit</span>: <span class="hljs-number">1</span>
</code></pre><p>If we were to deploy this again, the deployment would fail as we would be trying to deploy the same API key. API keys need to be unique so we have to change the name of the API key. When we deploy this and copy our new API key into Postman, we'll be able to make requests as we normally would. If we try and make too many requests per second or reach the maximum number of requests then we'll get a 429 error of</p>
<pre><code>{
    <span class="hljs-string">"message"</span>: <span class="hljs-string">"Limit Exceeded"</span>
}
</code></pre><p>This means that you can't use this API key again until next month.</p>
<p>Whilst creating a usage plan is great, you often want to give different people different levels of access to your services. You might give free users 100 requests per month and paying users get 1000. You might want different payment plans which give different number of requests. You would also probably want a master API key for yourself which has unlimited requests!</p>
<p>To do this we can set up multiple groups of API keys that each have their own usage policy. We need to change the <code>apiKeys</code> and <code>usagePlan</code> sections:</p>
<pre><code>    apiKeys:
        - free:
              - MyAPIKey3
        - paid:
              - MyPaidKey3
    <span class="hljs-attr">usagePlan</span>:
        - free:
              quota:
                  limit: <span class="hljs-number">10</span>
                  <span class="hljs-attr">period</span>: MONTH
              <span class="hljs-attr">throttle</span>:
                  burstLimit: <span class="hljs-number">2</span>
                  <span class="hljs-attr">rateLimit</span>: <span class="hljs-number">1</span>
        - paid:
              quota:
                  period: MONTH
                  <span class="hljs-attr">limit</span>: <span class="hljs-number">1000</span>
              <span class="hljs-attr">throttle</span>:
                  burstLimit: <span class="hljs-number">20</span>
                  <span class="hljs-attr">rateLimit</span>: <span class="hljs-number">10</span>
</code></pre><p>Once you've saved and deployed this you'll get two new API keys, each with a different level of access to your API endpoints.</p>


<p>Thanks for reading this guide! If you've found it useful, please subscribe to my <a target="_blank" href="https://www.youtube.com/channel/UC8uBP0Un18DJAnWjm1CPqBg">Youtube channel</a> where I release weekly videos on Serverless and software development.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Serverless by Building your own Slack App ]]>
                </title>
                <description>
                    <![CDATA[ By Lekha Surasani Serverless architecture is the industry's latest buzzword and many of the largest tech companies have begun to embrace it.  In this article, we'll learn what it is and why you should use it. We'll also set up AWS, create our serverl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/make-a-serverless-slack-app/</link>
                <guid isPermaLink="false">66d46018787a2a3b05af43d4</guid>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 08 Aug 2019 11:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/08/serverless-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Lekha Surasani</p>
<p>Serverless architecture is the industry's latest buzzword and many of the largest tech companies have begun to embrace it. </p>
<p>In this article, we'll learn what it is and why you should use it. We'll also set up AWS, create our serverless app, and create a slack app!</p>
<h2 id="heading-what-is-serverless">What is Serverless?</h2>
<p>Serverless is a cloud computing paradigm in which the developer no longer has to worry about maintaining a server – they just focus on the code. </p>
<p>Cloud providers, such as AWS or Azure, are now responsible for executing code and maintaining servers by dynamically allocating their resources. A variety of events can trigger code execution, including cron jobs, http requests, or database events. </p>
<p>The code that developers send to the cloud is usually just a function so, many times, serverless architecture is implemented using Functions-as-a-Service, or FaaS. The major cloud providers provide frameworks for FaaS, such as AWS Lambda and Azure Functions.</p>
<h2 id="heading-why-serverless">Why Serverless?</h2>
<p>Not only does serverless allow developers to just focus on code, but it has many other benefits as well. </p>
<p>Since cloud providers are now responsible for executing code and dynamically allocate resources based on event triggers, you typically only pay per request, or when your code is being executed. </p>
<p>Additionally, since cloud providers are handling your servers, you don't have to worry about scaling up – the cloud provider will handle it. This makes serverless apps lower cost, easier to maintain, and easier to scale.</p>
<hr>
<h2 id="heading-setting-up-aws-lambda">Setting up AWS Lambda</h2>
<p>For this tutorial, I will be using AWS Lambda, so first, we'll create an <a target="_blank" href="https://aws.amazon.com/">AWS account</a>. I find AWS's UI hard to understand and difficult to navigate, so I will be adding screenshots for each step.</p>
<p>Once you log in, you should see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-17.png" alt="Image" width="600" height="400" loading="lazy">
<em>Main screen</em></p>
<p>Next, we'll set up an IAM user. An <a target="_blank" href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html">IAM</a> (Identity and Access Management) user interacts with AWS and its resources on your behalf. This allows you to create different IAM users with different permissions and purposes, without compromising the security of your root user account.</p>
<p>Click on the "services" tab at the top of the page, and type "IAM" into the bar:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-27.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click on the first result, and you'll see, on the left-hand sidebar, that you're at the dashboard. Click on the "Users" option to get to create our new IAM user. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-28.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click on the "Add user" button to create a new user. Fill in the details as follows:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-29.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can name your user anything you'd like, but I went with <code>serverless-admin</code>. Be sure that your user has "Programmatic access" to AWS, <strong>not</strong> "AWS Management Console Access". You'd use the latter for teammates, or other <em>humans</em> who need access to AWS. We just need this user to interact with AWS Lambda, so we can just give them programmatic access. </p>
<p>For permissions, I've chosen to attach existing policies since I don't have any groups, and I don't have any existing users that I want to copy permissions for. In this example, I will create the user with Administrator access since it's just for a personal project; however, if you were to use a serverless app in an actual production environment, your IAM user should be scoped to only access Lambda-necessary parts of AWS. (Instructions can be found <a target="_blank" href="https://serverless.com/blog/abcs-of-iam-permissions/">here</a>).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-58.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I didn't add any tags and created the user. It's vital to save the information given to you on the next screen - the Access ID and Secret Access Key.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-04-IAM-Management-Console.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Don't leave this screen without copying down both! You won't be able to see the Secret access key again after this screen.</p>
<p>Finally, we'll add these credentials to command line AWS. Use this <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html">guide</a> to get aws cli setup.</p>
<p>Make sure you have it installed by running <code>aws --version</code>. You should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-2.02.27-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then run <code>aws configure</code> and fill in the prompts:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-5.42.53-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I have the default region as <code>us-east-2</code> already set up, but you can use <a target="_blank" href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html">this</a> to determine what your region is.</p>
<p>To make sure that you have your credentials set up correctly, you can run <code>cat ~/.aws/credentials</code> in your terminal.</p>
<p>If you want to configure a profile other than your default, you can run the command as follows: <code>aws configure --profile [profile name]</code>.</p>
<p>If you had trouble following the steps, you can also check out <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html">AWS's documentation</a>.</p>
<hr>
<h2 id="heading-set-up-serverless">Set up serverless</h2>
<p>Go to your terminal and install the <code>serverless</code> package globally using <code>npm</code>: <code>npm i -g serverless</code>. (<a target="_blank" href="https://serverless.com/">More info on serverless here</a>)<br>and your terminal should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-1.55.12-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, navigate to the directory where you want to create the app, then run <code>serverless</code> and follow the prompts:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-5.55.03-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For this application, we'll be using Node.js. You can name your app anything you want, but I've called mine <code>exampleSlackApp</code>.</p>
<p>Open your favorite code editor to the contents in <code>exampleSlackApp</code> (or whatever you've called your application).</p>
<p>First, we'll take a look at <code>serverless.yml</code>. You'll see there's a lot of commented code here describing the different options you can use in the file. Definitely give it a read, but I've deleted it down to just:</p>
<pre><code>service: exampleslackapp

<span class="hljs-attr">provider</span>:
  name: aws
  <span class="hljs-attr">runtime</span>: nodejs10.x
  <span class="hljs-attr">region</span>: us-east<span class="hljs-number">-2</span>

<span class="hljs-attr">functions</span>:
  hello:
    handler: handler.hello
</code></pre><p> I've included <code>region</code> since the default is <code>us-east-1</code> but my aws profile is configured for <code>us-east-2</code>.</p>
<p>Let's deploy what we already have by running <code>serverless deploy</code> in the directory of the app that <code>serverless</code> just created for us. The output should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-05-at-12.07.10-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And if you run <code>serverless invoke -f hello</code> in your terminal, it'll run the app, and you should see:</p>
<pre><code>{
    <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">200</span>,
    <span class="hljs-string">"body"</span>: <span class="hljs-string">"{\n  \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n  \"input\": {}\n}"</span>
}
</code></pre><p>For further proof that our slack app is live, you can head back to AWS console. Go to the services dropdown, search for "Lambda", and click on the first option ("Run code without thinking about servers").</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-32.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And here's your app!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-33.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, we'll explore actually using serverless by building our slack app. Our slack app will post a random <a target="_blank" href="https://en.wikipedia.org/wiki/Ron_Swanson">Ron Swanson</a> quote to slack using a <a target="_blank" href="https://api.slack.com/slash-commands">slash command</a> like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-10.23.40-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The following steps don't necessarily have to be done in the order that I've done them, so if you want to skip around, feel free!</p>
<hr>
<h2 id="heading-adding-the-api-to-our-code">Adding the API to our code</h2>
<p>I'm using <a target="_blank" href="https://github.com/jamesseanwright/ron-swanson-quotes#ron-swanson-quotes-api?ref=public-apis">this API</a> to generate Ron Swanson quotes since the docs are fairly simple (and of course, it's free). To see how requests are make and what gets returned, you can just put this URL in your browser:</p>
<p><a target="_blank" href="https://ron-swanson-quotes.herokuapp.com/v2/quotes"><code>https://ron-swanson-quotes.herokuapp.com/v2/quotes</code></a></p>
<p>You should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>So, we can take our initial function and modify it as such:</p>
<pre><code><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  getRon();
};
</code></pre><p>and <code>getRon</code> looks like:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params"></span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
}
</code></pre><p>Now, let's check if it works. To test this code locally, in your terminal: <code>serverless invoke local -f hello</code>. Your output should look something like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-9.41.53-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Spoiler: There was a wrong way to consume alcohol</em></p>
<p><code>serverless invoke -f hello</code> would run the code that you've deployed, as we saw in previous sections. <code>serverless invoke local -f hello</code>, however, runs your local code, so it's useful for testing. Go ahead and deploy using <code>serverless deploy</code>!</p>
<hr>
<h2 id="heading-create-your-slack-app">Create your Slack App</h2>
<p>To create your slack app, follow this <a target="_blank" href="https://api.slack.com/apps?new_app=1">link</a>. It'll make you sign into a slack workspace first, so be sure you're a part of one that you can add this app to. I've created a testing one for my purposes. You'll be prompted with this modal. You can fill in whatever you want, but here's what I have as an example:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-61.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From there, you'll be taken to the homepage for your app. You should definitely explore these pages and the options. For example, I've added the following customization to my app:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-62.png" alt="Image" width="600" height="400" loading="lazy">
<em>Display information can be found from the "Basic Information" tab on the app</em></p>
<p>Next, we need to add some permissions to the app:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Slack-API-Applications-lekha_test-Slack.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To get an OAuth Access Token, you have to add some scope and permissions, which you can do by scrolling down:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-64.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I've added "Modify your public channels" so that the bot could write to a channel, "Send messages as Ron Swanson" so when the message gets posted, it looks like a user called Ron Swanson is posting the message, and slash commands so the user can "request" a quote as shown in the screenshot at the beginning of the article. After you save the changes, you should be able to scroll back up to OAuths &amp; Permissions to see:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-65.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click the button to Install App to Workspace, and you'll have an OAuth Access Token! We'll come back to this in a second, so either copy it down or remember it's in this spot.</p>
<hr>
<h2 id="heading-connect-code-and-slack-app">Connect Code and Slack App</h2>
<p>In AWS Lambda, find your slack app function. Your Function Code section should show our updated code with the call to our Ron Swanson API (if it does not, go back to your terminal and run <code>serverless deploy</code>). </p>
<p>Scroll below that to the section that says "<a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/env_variables.html">Environment Variables</a>", and put your Slack OAuth Access Token here (you can name the key whatever you'd like):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Lambda-Management-Console.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Let's go back to our code and add Slack into our function. At the top of our file, we can declare a <code>const</code> with our new OAuth Token: </p>
<p><code>const SLACK_OAUTH_TOKEN = process.env.OAUTH_TOKEN</code>. </p>
<p><code>process.env</code> just grabs our environment variables (<a target="_blank" href="https://nodejs.org/dist/latest-v8.x/docs/api/process.html#process_process_env">additional reading</a>). Next, let's take a look at the <a target="_blank" href="https://api.slack.com/methods/chat.postMessage">Slack API</a> to figure out how to post a message to a channel.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-67.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-76.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The two pictures above I've taken from the API are the most relevant to us. So, to make this API request, I'll use <code>request</code> by passing in an object called <code>options</code>:</p>
<pre><code>  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: <span class="hljs-string">'general'</span>, <span class="hljs-comment">// hard coding for now</span>
      <span class="hljs-attr">text</span>: <span class="hljs-string">'I am here'</span>,
    }
  }
</code></pre><p>and we can make the request:</p>
<pre><code>  request(options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
</code></pre><p>Finally, I'll wrap the whole thing in a function:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postRon</span>(<span class="hljs-params">quote</span>) </span>{
  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: <span class="hljs-string">'general'</span>,
      <span class="hljs-attr">text</span>: quote,
    }
  }

  request(options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
}
</code></pre><p>and we can call it from <code>getRon</code> like this:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params"></span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
    postRon(body.substring(<span class="hljs-number">2</span>, body.length - <span class="hljs-number">2</span>)) <span class="hljs-comment">// here for parsing, remove if you want to see how/why I did it</span>
  })
}
</code></pre><p>So our code should all in all look like this:</p>
<pre><code><span class="hljs-meta">'use strict'</span>;
<span class="hljs-keyword">let</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'request'</span>);

<span class="hljs-keyword">const</span> SLACK_OAUTH_TOKEN = process.env.OAUTH_TOKEN

<span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  getRon();
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params"></span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
    postRon(body.substring(<span class="hljs-number">2</span>, body.length - <span class="hljs-number">2</span>))
  })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postRon</span>(<span class="hljs-params">quote</span>) </span>{
  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: <span class="hljs-string">'general'</span>,
      <span class="hljs-attr">text</span>: quote,
    }
  }

  request(options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
}
</code></pre><p>Now let's test! Unfortunately, our environment variable in AWS Lambda isn't available to us when we run <code>serverless invoke local -f hello</code>. There are a few ways you can approach this, but for our purposes, you can just replace the value for <code>SLACK_OAUTH_TOKEN</code> with your actual OAuth Token (make sure it's a string). But be sure you switch it back before you push it up to version control! </p>
<p>Run <code>serverless invoke local -f hello</code>, and hopefully you should see a message like this in your #general channel:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-69.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Please note that I put down my channel name as 'general' since it's my test workspace; however, if you're in an actual workspace, you should create a separate channel for testing apps, and put the message there instead while you're testing.</em></p>
<p>And in your terminal, you should see something like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-10.48.38-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If that works, go ahead and deploy it using <code>serverless deploy</code>. If it does not, the best way to debug this is to adjust code and run <code>serverless invoke local -f hello</code>.</p>
<hr>
<h2 id="heading-adding-slash-command">Adding slash command</h2>
<p>The last and final part is adding a slash command! Go back to your function's home page in AWS Lambda and look for the button that says "Add trigger":</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-70.png" alt="Image" width="600" height="400" loading="lazy">
<em>We're going to add an API Gateway (as I already have).</em></p>
<p>Click on the button to get to the "Add trigger" page, and select "API Gateway" from the list:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-71.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p> I've filled in the information based on defaults mostly:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-72.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I've also left this API open for use – however, if you're using this in production,  you should discuss what standard protocol would be with your team. "Add" the API, and you should receive an API endpoint. Hold on to this, because we'll need it for the next step. </p>
<p>Let's switch back over to our slack app and add a slash command:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-73.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click on "Create New Command" and it should pop up with a new window to create a command. Here's how I filled mine out:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Slack-API-Applications-lekha_test-Slack-1-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can enter anything you want for "command" and "short description" but for "request URL", you should put your API endpoint.</p>
<p>Finally, we'll go back to our code to make some final adjustments. If you try to use the slash command, you should receive some kind of error back – this is because slack expects a response and AWS expects you to give a response when the endpoint is hit. So, we'll change our function to allow a <code>callback</code> (<a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html">for reference</a>):</p>
<pre><code><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event,context,callback</span>) =&gt;</span> {
  getRon(callback);
};
</code></pre><p>and then we'll change <code>getRon</code> to do something with the <code>callback</code>:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params">callback</span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
    callback(<span class="hljs-literal">null</span>, SUCCESS_RESPONSE)
    postRon(body.substring(<span class="hljs-number">2</span>, body.length - <span class="hljs-number">2</span>))
  })
}
</code></pre><p>where <code>SUCCESS_RESPONSE</code> is at the top of the file:</p>
<pre><code><span class="hljs-keyword">const</span> SUCCESS_RESPONSE = {
  <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
  <span class="hljs-attr">body</span>: <span class="hljs-literal">null</span>
}
</code></pre><p>You can put the callback here or in <code>postRon</code> – it just depends on what your purposes are with the callback. </p>
<p>Our code at this point now looks something like:</p>
<pre><code><span class="hljs-meta">'use strict'</span>;
<span class="hljs-keyword">let</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'request'</span>);

<span class="hljs-keyword">const</span> SLACK_OAUTH_TOKEN = OAUTH_TOKEN

<span class="hljs-keyword">const</span> SUCCESS_RESPONSE = {
  <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
  <span class="hljs-attr">body</span>: <span class="hljs-literal">null</span>
}

<span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event,context,callback</span>) =&gt;</span> {
  getRon(callback);
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params">callback</span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
    callback(<span class="hljs-literal">null</span>, SUCCESS_RESPONSE)
    postRon(body.substring(<span class="hljs-number">2</span>, body.length - <span class="hljs-number">2</span>))
  })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postRon</span>(<span class="hljs-params">quote</span>) </span>{
  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: <span class="hljs-string">'general'</span>,
      <span class="hljs-attr">text</span>: quote,
    }
  }

  request(options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
}
</code></pre><p>You should be able to use the <code>/ron</code> command in slack now and get a Ron Swanson quote back. If you don't, you can use Cloudwatch logs to see what went wrong:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Lambda-Management-Console-1-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The way our code works now, we've hardcoded in the channel name. But, what we actually want is for the quote to get posted in the message where you used <code>/ron</code>. </p>
<p>So, we can now use the <code>event</code> portion of our function. </p>
<pre><code><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event,context,callback</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(event)
  getRon(callback);
};
</code></pre><p>Use <code>/ron</code> to run the function, and then check your Cloudwatch logs to see what gets logged to the console (you may need to refresh). Check on the most recent logs and you should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-74.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The first item in this list (where it says "resource", "path", etc.) is the event, so if you expand that, you'll see a long list of things, but what we're looking for is 'body' all the way down at the bottom:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-75.png" alt="Image" width="600" height="400" loading="lazy">
<em>where's waldo: spot the param edition</em></p>
<p>Body is a string with some relevant information in it, one of them being "channel_id". We can use channel_id (or channel_name) and pass it into the function that creates our slack message. For your convenience, I've already parsed this string: <code>event.body.split("&amp;")[3].split("=")[1]</code> should give you the channel_id. I hardcoded in which entry (3) the channel_id was for simplicity.</p>
<p>Now, we can alter our code to save that string as a variable:</p>
<p><code>let channel = 'general'</code> (as our fallback)</p>
<pre><code><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event,context,callback</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(event)
  channel = event.body.split(<span class="hljs-string">"&amp;"</span>)[<span class="hljs-number">3</span>].split(<span class="hljs-string">"="</span>)[<span class="hljs-number">1</span>]
  <span class="hljs-built_in">console</span>.log(context)
  getGoat(callback);
};
</code></pre><p>and in <code>postRon</code>:</p>
<pre><code>  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: channel,
      <span class="hljs-attr">text</span>: quote,
    }
  }
</code></pre><p>Finally, if you use a slack command in any channel in your workspace, you should be able to see a Ron Swanson quote pop up! If not, as I mentioned before, the most common tools I use to debug serverless apps are <code>serverless invoke local -f &lt;function name&gt;</code> and Cloudwatch logs.</p>
<hr>
<p>Hopefully you were successfully able to create a functioning Slack application! I've included resources and background reading dispersed throughout the article and I'm happy to answer any questions you may have!</p>
<p><em>Final Repo with code:</em> <a target="_blank" href="https://github.com/lsurasani/ron-swanson-slack-app/">https://github.com/lsurasani/ron-swanson-slack-app/</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What you need to know to become a full-stack serverless developer ]]>
                </title>
                <description>
                    <![CDATA[ By Sam Williams The are the 4 areas of development you need to know to call yourself a full-stack developer Becoming a full-stack developer is the goal of a lot of developers. Being able to create a complete software product, getting to understand ho... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-you-need-to-become-a-full-stack-serverless-developer/</link>
                <guid isPermaLink="false">66d460e851f567b42d9f84a1</guid>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 17 Jul 2019 06:34:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/serverlesssStuff-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sam Williams</p>
<h2 id="heading-the-are-the-4-areas-of-development-you-need-to-know-to-call-yourself-a-full-stack-developer">The are the 4 areas of development you need to know to call yourself a full-stack developer</h2>
<p>Becoming a full-stack developer is the goal of a lot of developers. Being able to create a complete software product, getting to understand how the whole system works, and the very nice wage increase (Over £5,500**) are all reasons people want to level up their skills and become a full-stack developer.</p>
<p>The issue is that learning all of the skills you need can take a lot of time. We’ll cover the 4 areas of development that you need to know, and discuss the best ways that you can learn them.</p>
<h1 id="heading-front-end-website-hosting">Front End /Website Hosting</h1>
<p>Whenever you build any sort of application, it needs to have a front end. This is what your users will see and how they interact with your product.</p>
<p>This is often the first serverless skill that developers gain, often without realising it. This is often through GitHub pages or a hosting service.</p>
<p>Whilst these services are great for quick and simple project hosting, you will need something more robust for larger and more technical serverless web hosting.</p>
<h3 id="heading-what-youll-need-to-be-able-to-do">What you’ll need to be able to do</h3>
<ul>
<li>To be able to host the files required for a front end application.</li>
<li>To be able to serve these files on a given URL at scale</li>
<li>Point a registered domain name at these files</li>
</ul>
<h3 id="heading-how-to-do-this-with-serverless">How to do this with Serverless?</h3>
<ul>
<li>Host the files on Amazon S3 (file storage system)</li>
<li>Create a CloudFront distribution to serve the files at scale</li>
<li>Use Route 53 to register a domain name and point it at the Cloudfront Distribution</li>
</ul>
<h3 id="heading-why-serverless-is-the-best-way-to-do-this">Why Serverless is the best way to do this</h3>
<p>S3, CloudFront and Route 53 all scale so you don’t have to work out (guess) how many visitors your site will get</p>
<ul>
<li>You don’t need to set up or maintain the servers</li>
<li>You don’t need to set up DNS, nameservers or anything else to get the site up on your URL. Route 53 handles all of this.</li>
</ul>
<h1 id="heading-create-an-api">Create an API</h1>
<p>Every app needs APIs so that the front end can interact with the back end (databases, storage, email, etc.) which is where most of the power of a full-stack app comes from.</p>
<h3 id="heading-what-youll-need-to-be-able-to-do-1">What you’ll need to be able to do</h3>
<ul>
<li>To be able to create restful API endpoints</li>
<li>To be able to access your databases</li>
<li>To be able to access other services (Storage, SMS, email, other APIs)</li>
<li>Protect your endpoints with API keys</li>
</ul>
<h3 id="heading-how-to-do-this">How to do this?</h3>
<ul>
<li>Use API Gateway to build the API endpoints</li>
<li>Create Lambda functions to execute your logic and access other services (database access, SMS, email, etc.)</li>
<li>Create API keys that provide access to your API endpoints</li>
</ul>
<h3 id="heading-why-serverless-is-the-best-way-to-do-this-1">Why Serverless is the best way to do this</h3>
<ul>
<li>Each endpoint is an isolated function, so if one breaks it doesn’t crash the others</li>
<li>You have very easy access to the rest of the serverless services through the aws-sdk, reducing code and speeding up development</li>
<li>You can easily create, limit and remove API keys to make sure that the right people are able to invoke your API endpoints.</li>
</ul>
<h1 id="heading-databases">Databases</h1>
<p>All full-stack services need a way to store data about users, products, and everything else. This might be in a relational or non-relational database but you need to store the data somewhere organised.</p>
<h3 id="heading-what-youll-need-to-be-able-to-do-2">What you’ll need to be able to do</h3>
<ul>
<li>Create a scalable non-relational or relational database</li>
<li>Access this database</li>
</ul>
<h3 id="heading-how-to-do-this-1">How to do this?</h3>
<ul>
<li>Create a DynamoDB (non-relational) or Aurora (relational) database</li>
<li>Access your tables within your API Lambdas using the built-in tools within the AWS SDK</li>
</ul>
<h3 id="heading-why-serverless-is-the-best-way-to-do-this-2">Why Serverless is the best way to do this</h3>
<ul>
<li>Your tables automatically scale and have built-in redundancy, removing the need to manage and sync multiple copies of databases</li>
<li>You can easily access the databases with the AWS SDK without having to expose it to the outside world.</li>
</ul>
<h1 id="heading-deployment-and-maintainance">Deployment and Maintainance</h1>
<p>Once you’ve designed and built all of your systems, you need to deploy them into a production environment, maintain and upgrade them.</p>
<h3 id="heading-what-youll-need-to-be-able-to-do-3">What you’ll need to be able to do</h3>
<ul>
<li>Deploy all of the resources we’ve talked about so far</li>
<li>Provide version-controlled configuration for all of the resources</li>
<li>Maintain and update the software and hardware that your systems are running on</li>
</ul>
<h3 id="heading-how-to-do-this-2">How to do this?</h3>
<ul>
<li>Create the resources using the Serverless framework</li>
</ul>
<h3 id="heading-why-serverless-is-the-best-way-to-do-this-3">Why Serverless is the best way to do this</h3>
<ul>
<li>When you create your serverless.yml file, you define all of the resources that you need to get your application running</li>
<li>This serverless.yml file can be version controlled to track changes over time</li>
<li>You can deploy your whole architecture in minutes with a single command</li>
<li>All of the underlying software and hardware is maintained, updated and upgraded by your service provider (AWS) so you don’t need to worry about it</li>
</ul>
<hr>
<p>If you’ve liked this article and want to start learning how you can become a full-stack developer, I have a free 3 part video course on how to build and deploy your own serverless API.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/course.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://courses.completecoding.io/p/build-a-serverless-api/">Check out the course here</a></em></p>
<p>** London Front end developer (£42,994) vs London Fullstack Developer (48,767)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Building Serverless Contact Form For Static Websites ]]>
                </title>
                <description>
                    <![CDATA[ By Faizan Bashir Photo by Unsplash Introduction A few years ago AWS launched static hosting service S3, which was a paradigm shift for hosting static websites. The tech was crystal clear, all the static assets (HTML, CSS, and JS) would reside in an ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/building-serverless-contact-form-for-static-websites/</link>
                <guid isPermaLink="false">66d45ee8a326133d124409e1</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 05 Jul 2019 18:55:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/1_lYvXrG9rcgLg42weUyOfyg.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Faizan Bashir</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*lYvXrG9rcgLg42weUyOfyg.jpeg" alt="Image" width="2600" height="1950" loading="lazy"></p>
<p><em>Photo by</em> <a target="_blank" href="https://unsplash.com/photos/AtvuPUenaeI?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Unsplash</em></a></p>
<h3 id="heading-introduction">Introduction</h3>
<p>A few years ago AWS launched static hosting service S3, which was a paradigm shift for hosting static websites. The tech was crystal clear, all the static assets (HTML, CSS, and JS) would reside in an S3 bucket to host your impressive website. A pretty cool idea I personally liked it, really. Had it not been for that super important contact form hosting on S3 would have been cool but your contact form would be a joke unless you had another server in place to service AJAX requests from that form. The moment you had that service ready, the S3 solution wouldn’t appear so attractive at all.</p>
<p>In the age of cutting edge technology, there’s always jaw-dropping innovations around the corner. One of the awesome tech innovation happens to be serverless. Not that there are no servers involved but you can care less about them now. Serverless can be a proper and viable solution to a lot of problems, it is the most perfect solution for your static hosted contact form. Keep reading by the end of this post you will be able to handle your website forms in the most inexpensive and simplest manner possible.</p>
<hr>
<h3 id="heading-the-serverless-framework">The Serverless Framework</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*oDBqXrshDx-kEVUg1e6Rhw.png" alt="Image" width="2000" height="583" loading="lazy"></p>
<p><em>Source:</em> <a target="_blank" href="https://serverless.com/"><em>https://serverless.com/</em></a></p>
<blockquote>
<p><em>Serverless is your toolkit for deploying and operating serverless architectures. Focus on your application, not your infrastructure.</em>  </p>
<p><em>—</em> <a target="_blank" href="https://serverless.com/"><em>Serverless.com</em></a></p>
</blockquote>
<p>The Swiss army knife of Serverless technologies. Serverless Framework is a free and open-source web framework written in Node.js. Serverless was the first framework to be developed for building applications exclusively on AWS Lambda, the serverless computing platform provided by Amazon Web Services. Currently, applications developed with Serverless Framework can be deployed to other FaaS service providers. Here is the list of the Serverless cloud services supported by the Serverless Framework:</p>
<ul>
<li><p><a target="_blank" href="https://aws.amazon.com/lambda/"><strong>AWS Lambda</strong></a></p>
</li>
<li><p><a target="_blank" href="https://cloud.google.com/functions/"><strong>Google Cloud Functions</strong></a></p>
</li>
<li><p><a target="_blank" href="https://azure.microsoft.com/en-us/services/functions/"><strong>Azure Functions</strong></a></p>
</li>
<li><p><a target="_blank" href="https://www.ibm.com/cloud-computing/bluemix/openwhisk"><strong>IBM OpenWhisk</strong></a></p>
</li>
<li><p><a target="_blank" href="https://webtask.io/"><strong>Auth0 Webtask</strong></a></p>
</li>
<li><p><a target="_blank" href="https://fnproject.io/"><strong>Oracle Fn Project</strong></a></p>
</li>
<li><p><a target="_blank" href="https://spotinst.com/"><strong>Spotinst</strong></a></p>
</li>
<li><p><a target="_blank" href="https://kubeless.io/"><strong>Kubeless</strong></a></p>
</li>
</ul>
<hr>
<h3 id="heading-getting-started-with-serverless-framework">Getting started with Serverless Framework</h3>
<p>Obviously, you are pretty excited to get started with the Serverless Framework, let’s cut to the chase and start by installing Serverless.</p>
<p>Setting up Serverless is simple. You need to install it through npm and link it to your AWS account.</p>
<h4 id="heading-1-installing-serverless-globally">1. Installing Serverless Globally</h4>
<p>Time to get hands-on Serverless stuff.</p>
<pre><code class="lang-bash">$ npm install serverless -g
</code></pre>
<p>This command installs Serverless globally on your local machine. The Serverless commands are now available to you from your terminal.</p>
<p><strong>Note:</strong> Running Linux, you may want to run the above command as sudo.</p>
<h4 id="heading-2-create-an-iam-user-in-the-aws-console">2. Create an IAM user in the AWS Console</h4>
<p>Go to your <a target="_blank" href="https://console.aws.amazon.com/">AWS Console</a>, you will find the <a target="_blank" href="https://console.aws.amazon.com/iam/home">IAM service</a> listed below the “Security, Identity &amp; Compliance” group. Inside the IAM dashboard click on the Users tab and click “Add User” button.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*VtA7fGzE2a_h6yMTl69lBw.png" alt="Image" width="1600" height="741" loading="lazy"></p>
<p><em>AWS IAM Dashboard User Tab</em></p>
<p>Create a new user and allow the user <strong>programmatic access</strong> by clicking on the Programmatic access checkbox. Next, in the permissions section, you need to add a set of permissions to the user. From the list of available options under the “Attach existing policies directly” check the <strong>AdministratorAccess</strong>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*d_6PWCnAeK25k7P7CaL1uA.png" alt="Image" width="1600" height="745" loading="lazy"></p>
<p>After the user is created, you will have access to the users <strong>Access Key ID</strong> and<strong>Secret Access Key</strong>. You will be required to use these keys in the next step.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*7FqyvVFoRxZClqC16SevXw.png" alt="Image" width="1600" height="748" loading="lazy"></p>
<p><strong>Word of Caution:</strong> These are the kind of credentials you don’t want to lose even by mistake, remember you have provided <strong>AdministratorAccess</strong> to this user. The user with <strong>AdministratorAccess</strong> can do pretty much everything with your AWS account.</p>
<h4 id="heading-3-configuring-serverless-to-use-iam-credentials">3. Configuring Serverless to use IAM Credentials</h4>
<p>Great! With the keys, you can set up Serverless Framework to access your AWS account. Switch to your terminal and use this command to configure Serverless:</p>
<pre><code class="lang-bash">$ sls config credentials --provider aws --key xxxxxxxxxxxxxx --secret xxxxxxxxxxxxxx --profile &lt;username&gt;
</code></pre>
<p>Now your Serverless installation knows what account AWS to connect.</p>
<p><strong>Note:</strong> <code>sls</code> is an alias for the <code>serverless</code> command. You can use both to the same effect. But <code>sls</code> is kinda cool.</p>
<h4 id="heading-4-creating-a-service">4. Creating a service</h4>
<p>With the Serverless Framework hooked up with your AWS account, you can set up a Serverless project in a jiffy. Fire up the terminal and issue the following command:</p>
<pre><code class="lang-bash">$ sls create --template aws-python --path &lt;your-folder-path&gt;
</code></pre>
<p>The <code>--template</code> flag is used to specify a preset template with the given settings. In the above command the template <code>aws-python</code> will set up the project configured to use AWS as the provider and Python as the runtime. The command will auto-generate <code>serverless.yml</code> , <code>handler.py</code> and <code>.gitignore</code>file with preset values.</p>
<p>The configuration is defined in the <code>serverless.yml</code> file. This file is the most important file in the Serverless Framework. It’s almost magical, given how it can spin up the infrastructure you have defined in it. The contents of the auto-generated <code>serverless.yml</code> file will look something like this:</p>
<pre><code class="lang-bash">service: &lt;your-service-name&gt;

provider:  
  name: aws  
  runtime: python2.7

<span class="hljs-built_in">functions</span>:  
  hello:    
    handler: handler.hello
</code></pre>
<p>The <code>provider</code> section defines everything related to the service provider, there are a lot more properties to configure it further you can take a look at them <a target="_blank" href="https://serverless.com/framework/docs/providers/aws/guide/serverless.yml/">here</a>. In the auto-generated <code>serverless.yml</code> file, you need to add two important tags under the <code>provider</code> section, which are as follows:</p>
<pre><code class="lang-bash">region: &lt;your-aws-region&gt;
profile: &lt;aws-username-with-programmatic-access&gt;
</code></pre>
<p>The <code>functions</code> property is used to declare the serverless functions, you can declare multiple functions under this property. The above example declares a function called <code>hello</code> present in the <code>handler.py</code> file. Browse over to the <code>handler.py</code> file and you will find something like this:</p>
<pre><code class="lang-bash">import json
def hello(event, context):    
    body = {
        <span class="hljs-string">"message"</span>: <span class="hljs-string">"Go Serverless v1.0! Your function executed      successfully!"</span>,        
        <span class="hljs-string">"input"</span>: event    
    }
    response = {
        <span class="hljs-string">"statusCode"</span>: 200,        
        <span class="hljs-string">"body"</span>: json.dumps(body)    
    }
    <span class="hljs-built_in">return</span> response
</code></pre>
<hr>
<h3 id="heading-the-serverless-app">The Serverless App</h3>
<p>Our Serverless solution makes use of AWS infrastructure, it consists of API Gateway, Lambda Functions, DynamoDB and Simple Email Service(SES). To achieve this end result we will use the previously introduced Serverless Framework.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Be-VuMqQEg6Ifh60bFtDcQ.png" alt="Image" width="2560" height="1440" loading="lazy"></p>
<p><em>Architecture of the Serverless app</em></p>
<ul>
<li><p><strong>Static Website —</strong> Amazon S3 provides a robust and simple web server. All of the static HTML, CSS and JS files for your application can be served from S3. The contact form on our static website is submitted using AJAX.</p>
</li>
<li><p><strong>API Gateway —</strong> The API Gateway is the event source for the application, it acts as a bridge between our contact form and serverless lambda function. It routes the request from the contact form to the lambda function. The API Gateway also performs tasks such as access control, monitoring, API version control and traffic management.</p>
</li>
<li><p><strong>AWS Lambda —</strong> AWS Lambda is the place where the action takes place. Lambda functions run in stateless compute containers that are event-triggered, managed and ephemeral. In our example, we use a lambda function to send email using SES and store the request contents in DynamoDB NoSQL database.</p>
</li>
<li><p><strong>Simple Email Service (SES) —</strong> The cloud-based email sending service from Amazon. Scalable email service, you can send marketing and transactional emails using SES. In our example, we use SES to send emails using a verified email address.</p>
</li>
<li><p><strong>DynamoDB —</strong> DynamoDB provides a scalable, consistent, fully managed and non-relational database from Amazon. In our example, we use DynamoDB to store and retrieve the messages received from the static contact form.</p>
</li>
</ul>
<p>You can find the source code for the demo application here. Go ahead and clone it!</p>
<p><a target="_blank" href="https://github.com/faizanbashir/python-ses-dynamodb-contactform"><strong>faizanbashir/python-ses-dynamodb-contactform</strong></a><br><a target="_blank" href="https://github.com/faizanbashir/python-ses-dynamodb-contactform">_python-ses-dynamodb-contactform - Serverless Framework SES and DynamoDB Contact Form_github.com</a></p>
<hr>
<h3 id="heading-application-walkthrough">Application Walkthrough</h3>
<p>Let’s have a stroll through the demo application before we actually deploy it on AWS.</p>
<h4 id="heading-1-demystifying-the-serverlessyml-file">1. Demystifying the serverless.yml file</h4>
<p>The <code>serverless.yml</code> file defines the services the application needs to use and interact with. The resources and the actions the Serverless functions can perform are listed under the <code>**iamRoleStatements**</code> property. It lists the actions and resources.</p>
<pre><code class="lang-bash">iamRoleStatements:
  - Effect: <span class="hljs-string">"Allow"</span>
    Action:
      - ses:SendEmail      
      - ses:SendRawEmail    
    Resource: <span class="hljs-string">"*"</span>  
  - Effect: <span class="hljs-string">"Allow"</span>    
    Action:      
      - dynamodb:Scan      
      - dynamodb:PutItem    
    Resource: <span class="hljs-string">"arn:aws:dynamodb:<span class="hljs-variable">${opt:region, self:provider.region}</span>:*:table/<span class="hljs-variable">${self:provider.environment.DYNAMODB_TABLE}</span>"</span>
</code></pre>
<p>In the <code>serverless.yml</code> we are allowing the Serverless functions to use <code>ses:SendEmail</code> and <code>dynamoDB:PutItem</code> actions among many others defined above.</p>
<p>Since Lambda runs serverless functions in the cloud, we need to define the functions somewhere. Functions are defined using the <code>**functions**</code> property. In our example application we have defined events attached to them.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">functions</span>:  
  sendMail:    
    handler: handler.sendMail
    description: Send Email using AWS SES Service
    events:
      - http:          
        path: sendMail          
        method: post          
        integration: lambda          
        cors: <span class="hljs-literal">true</span>          
        response:            
          headers:              
            <span class="hljs-string">"Access-Control-Allow_Origin"</span>: <span class="hljs-string">"'*'"</span>  

  list:    
    handler: handler.list    
    description: List all the contact form submissions    
    events:      
      - http:          
        path: list          
        method: get          
        integration: lambda          
        cors: <span class="hljs-literal">true</span>          
        response:            
          headers:              
            <span class="hljs-string">"Access-Control-Allow_Origin"</span>: <span class="hljs-string">"'*'"</span>
</code></pre>
<p>Another great feature of Serverless Framework is that it will create an API in the AWS API Gateway and link it with relevant Lambda function. This is done using the <code>http</code> property defined in the <code>events</code> property.</p>
<h4 id="heading-2-creating-resources">2. Creating Resources</h4>
<p>With Serverless Framework you create resources like a DynamoDB table as we have done here. This snippet of code is responsible for creating a DynamoDB table with the given configuration.</p>
<pre><code class="lang-bash">resources:  
  Resources:    
    ContactFormDynamoDbTable:      
      Type: <span class="hljs-string">'AWS::DynamoDB::Table'</span>      
      DeletionPolicy: Retain      
      Properties:        
        AttributeDefinitions:          
          -            
            AttributeName: id            
            AttributeType: S        
        KeySchema:          
          -            
            AttributeName: id            
            KeyType: HASH        
        ProvisionedThroughput:          
          ReadCapacityUnits: 1          
          WriteCapacityUnits: 1        
        TableName: <span class="hljs-variable">${self:provider.environment.DYNAMODB_TABLE}</span>
</code></pre>
<h4 id="heading-3-peek-into-the-serverless-functions">3. Peek into the Serverless functions</h4>
<p>The demo application is written in <strong>python</strong>, it uses <a target="_blank" href="https://github.com/boto/boto3"><strong>boto3</strong></a> AWS SDK to send emails using SES and for performing read/write operations on DynamoDB.</p>
<p>The <code>sendMail</code> function is triggered when a <code>POST</code> request is received from the contact form on the <code>/sendMail</code> path. The <code>list</code> function is triggered by a <code>GET</code> request to <code>/list</code> path defined in the <code>serverless.yml</code> file.</p>
<hr>
<h3 id="heading-building-the-application">Building the Application</h3>
<p>Now that you have set up and configured the Serverless Framework in your machine, it’s time to get things rolling.</p>
<h4 id="heading-1-clone-the-application">1. Clone the application</h4>
<p>Let’s start by cloning the application from Github.</p>
<pre><code class="lang-bash">$ git <span class="hljs-built_in">clone</span> https://github.com/faizanbashir/python-ses-dynamodb-contactform
$ <span class="hljs-built_in">cd</span> python-ses-dynamodb-contactform
</code></pre>
<h4 id="heading-2-verify-e-mail-address-with-ses">2. Verify e-mail address with SES</h4>
<p>Fast-forward to verifying the email you intend to send email from SES. All you need to do is add an email address, AWS will send you a verification with a link to verify the email address.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*f_Y1mmdgKjtvxjBZL8zdIw.png" alt="Image" width="1080" height="430" loading="lazy"></p>
<p>After verifying the email address, the “Verification Status” for the email will show up as “verified”.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*IqxxKMYybvn0PlSPWgWgew.png" alt="Image" width="1600" height="746" loading="lazy"></p>
<h4 id="heading-3-configuring-the-application">3. Configuring the application</h4>
<p>You need to configure the <code>serverless.yml</code> with your account specific details to make it work. Replace the <code>region</code>, <code>profile</code> and <code>SENDER_EMAIL</code> properties in <code>serverless.yml</code> as seen here:</p>
<pre><code class="lang-bash">provider:  
  name: aws  
  runtime: python2.7  
  region: &lt;aws-region&gt;  
  profile: &lt;aws-user&gt;  
  ...
environment:  
  SENDER_EMAIL: &lt;verified-email-address&gt;
</code></pre>
<p>Awesome! with the configuration done you can turn your attention to deploying the application.</p>
<h4 id="heading-4-deploying-to-aws">4. Deploying to AWS</h4>
<p>Everything in place now you can deploy application with a single command, ain’t that super cool.</p>
<pre><code class="lang-bash">$ sls deploy -v
</code></pre>
<p>It will take a minute or two to execute if you religiously followed this tutorial, at the end it will provide you a list of endpoints to use for calling our functions. It will look something like this:</p>
<pre><code class="lang-bash">endpoints:
POST - https://xxx.execute-api.xx.amazonaws.com/development/sendMail
GET - https://xxxx.execute-api.xx.amazonaws.com/development/list
</code></pre>
<h4 id="heading-5-testing-the-endpoints">5. Testing the endpoints</h4>
<p>Now that we have the endpoints let’s test application to see if it’s working or not. The <code>/sendMail</code> endpoint expects input in JSON format.</p>
<pre><code class="lang-bash">$ curl --header <span class="hljs-string">"Content-Type: application/json"</span> \--request POST \--data <span class="hljs-string">'{"firstname": "John", "lastname": "Doe", "email": "john@doe.com", "message": "Hi there"}'</span>\https://xxx.execute-api.xx.amazonaws.com/development/sendMail
</code></pre>
<p>If the email is sent and the entry written to DynamoDB the request will exit with a response like this.</p>
<pre><code class="lang-bash">&gt; <span class="hljs-string">"Email Sent!"</span>
</code></pre>
<p>Now, let’s test the <code>/list</code> endpoint in the same manner with the <code>GET</code> endpoint you got after deploying the application.</p>
<pre><code class="lang-bash">$ curl https://xxxx.execute-api.xx.amazonaws.com/development/list
</code></pre>
<p>The <code>/list</code> endpoint response will look something like this:</p>
<pre><code class="lang-bash">&gt; {<span class="hljs-string">"body"</span>: [{<span class="hljs-string">"firstname"</span>: <span class="hljs-string">"John"</span>, <span class="hljs-string">"lastname"</span>: <span class="hljs-string">"Doe"</span>, <span class="hljs-string">"email"</span>: <span class="hljs-string">"john@doe.com"</span>, <span class="hljs-string">"updatedAt"</span>: 1529425349731, <span class="hljs-string">"message"</span>: <span class="hljs-string">"Hi there"</span>, <span class="hljs-string">"id"</span>: <span class="hljs-string">"f651c404-73dc-11e8-bf3e-be54be0b5d22"</span>, <span class="hljs-string">"createdAt"</span>: 1529425349731}], <span class="hljs-string">"statusCode"</span>: 200}
</code></pre>
<h4 id="heading-6-the-contact-form">6. The Contact Form</h4>
<p>With the Serverless functions working properly we can go ahead and integrate it into our static contact form. The static form code is in the <code>public</code> folder.</p>
<p>Open the <code>index.html</code> file in your favourite IDE and update the <code>URL</code> variable with the <code>/sendMail</code> endpoint and you are good to go.</p>
<pre><code class="lang-bash">//Insert your lambda <span class="hljs-keyword">function</span> URL here
var URL = <span class="hljs-string">"https://xxx.execute-api.xx.amazonaws.com/development/sendMail"</span>;
</code></pre>
<p>Navigate to the page using the <code>file:///&lt;path&gt;/&lt;to&gt;/&lt;folder&gt;/index.html</code> in the browser or upload it to S3 bucket and enable static hosting.</p>
<pre><code class="lang-bash">$ aws s3 sync public s3://your-bucket-name
</code></pre>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*G6Q3XRI6tADC38nbcJohkQ.png" alt="Image" width="1600" height="747" loading="lazy"></p>
<p><em>Serverless Contact Form</em></p>
<p>Treat yourself with a Cappuccino, Latte or . You just implemented a cool way to keep your website on static hosting with handling your forms, thanks to Serverless.</p>
<hr>
<h3 id="heading-afterthoughts">Afterthoughts</h3>
<p>Serverless is definitely the way forward, not just for the worlds static contact forms. Serverless has opened a universe of opportunities for you, the contact form was just to get started with. How about using Serverless for your website analytics, a visitor counter or maybe click tracking?</p>
<p>Endless opportunities are waiting for you. Get started for your next project in Serverless, it’ll be an awesome journey.</p>
<hr>
<p><em>Originally published at</em> <a target="_blank" href="https://www.serverlessops.io/blog/serverless-contact-form-for-static-websites"><em>www.serverlessops.io</em></a><em>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
