How to Build a Serverless Web Application

ritikbanger

Ritik Banger

Posted on March 12, 2024

How to Build a Serverless Web Application

Ah, serverless computing - the buzzword that's been taking the tech world by storm. But what exactly is it, and why should you care? Simply put, serverless computing allows you to build and run applications without worrying about provisioning or managing servers. It's like having an army of invisible servers at your beck and call, ready to handle your application's logic whenever it's needed.

One of the coolest use cases for serverless technology is building web applications. Imagine a world where you can create a website without having to worry about scaling, patching, or maintaining servers. Sounds too good to be true, right? Well, buckle up, because that's exactly what we're going to do in this article.

Serverless is a cloud computing execution model where the cloud provider dynamically manages the allocation and provisioning of servers. With serverless, you don't have to worry about provisioning, scaling, or managing servers yourself. Instead, you deploy your code, and the cloud provider handles the server management and execution for you.

In a serverless architecture, your application's code runs in stateless compute containers that are event-triggered, ephemeral (they only run when needed and shut down automatically after use), and fully managed by the cloud provider. These containers, called "functions" in the serverless world, are typically triggered by events such as HTTP requests, database events, queue services, monitoring alerts, or other custom triggers.

The key characteristics of serverless computing include:

  1. No Server Management: You don't have to provision, scale, or manage any servers. The cloud provider handles all the infrastructure management for you.

  2. Event-Driven: Functions are triggered by specific events or requests, rather than running continuously.

  3. Automatic Scaling: The cloud provider automatically allocates the necessary resources to handle the incoming workload, scaling out seamlessly as needed.

  4. Pay-per-Use Billing: You only pay for the compute time your functions consume when they are running, plus any other cloud services used. This can result in significant cost savings compared to maintaining always-on servers.

  5. High Availability and Fault Tolerance: Cloud providers ensure high availability and fault tolerance for serverless functions across multiple availability zones.

Popular serverless compute services include AWS Lambda, Google Cloud Functions, Azure Functions, and IBM Cloud Functions. These services allow you to deploy and run your code without provisioning or managing servers, enabling you to focus on writing application logic instead of infrastructure management.

Serverless architectures are well-suited for various workloads, including web applications, APIs, data processing, IoT backends, and more. They can help reduce operational overhead, increase scalability, and potentially lower costs for certain types of applications.

Setting the Stage

Before we dive into the code, let's set up our development environment. You'll need an AWS account and the AWS CLI installed on your machine. We'll also be using Node.js and the Serverless Framework, a popular open-source tool that makes building and deploying serverless applications a breeze.

First, install the Serverless Framework globally:

npm install -g serverless
Enter fullscreen mode Exit fullscreen mode

Next, create a new directory for your project and navigate to it:

mkdir serverless-web-app
cd serverless-web-app
Enter fullscreen mode Exit fullscreen mode

Initialize a new Serverless project:

serverless create --template aws-nodejs --path my-service
Enter fullscreen mode Exit fullscreen mode

This will create a new directory my-service with a basic Lambda function and other boilerplate files.

Building the Web Application

Our serverless web application will consist of two main components: a static website hosted on AWS S3, and a Lambda function to handle server-side logic. Let's start with the static website.

Create a new directory called website in your project's root directory, and add an index.html file with some basic HTML:

<!-- website/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Serverless Web App</title>
  </head>
  <body>
    <h1>Welcome to my Serverless Web App!</h1>
    <p>This is a static website hosted on AWS S3.</p>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now, let's configure our Serverless project to deploy this website to an S3 bucket. Open serverless.yml and add the following resources:

# serverless.yml
service: serverless-web-app

provider:
  name: aws
  runtime: nodejs18.x

resources:
  Resources:
    WebsiteBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:service}-${opt:stage, self:provider.stage}
        WebsiteConfiguration:
          IndexDocument: index.html

# ... (existing Lambda function configuration)
Enter fullscreen mode Exit fullscreen mode

This will create an S3 bucket with the name serverless-web-app-[stage], where [stage] is the deployment stage (e.g., dev, prod). The bucket will be configured to serve the index.html file as the website index.

To deploy the website, run:

serverless deploy
Enter fullscreen mode Exit fullscreen mode

This will create the S3 bucket and upload the index.html file. The CLI output will include the website URL, which you can visit in your browser to see the static website.

Adding Server-Side Logic with Lambda

Now that we have our static website set up, let's add some server-side logic using AWS Lambda. We'll build a simple "contact form" functionality, where users can submit their name and email, and we'll save the data to a DynamoDB table.

First, we need to create the DynamoDB table. Add the following resource to serverless.yml:

# serverless.yml
resources:
  Resources:
    # ... (existing resources)
    ContactsTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:service}-contacts-${opt:stage, self:provider.stage}
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        BillingMode: PAY_PER_REQUEST
Enter fullscreen mode Exit fullscreen mode

This will create a DynamoDB table called serverless-web-app-contacts-[stage] with a hash key id.

Next, let's create the Lambda function that will handle the contact form submissions. In the my-service directory, create a new file called handler.js with the following code:

// handler.js
import AWS from 'aws-sdk';

const dynamoDb = new AWS.DynamoDB.DocumentClient();

const TABLE_NAME = process.env.CONTACTS_TABLE;

export const handler = async (event) => {
  const { name, email } = JSON.parse(event.body);

  const params = {
    TableName: TABLE_NAME,
    Item: {
      id: `contact-${Date.now()}`,
      name,
      email,
    },
  };

  try {
    await dynamoDb.put(params).promise();
    return {
      statusCode: 200,
      body: JSON.stringify({ message: 'Contact form submitted successfully' }),
    };
  } catch (err) {
    console.error(err);
    return {
      statusCode: 500,
      body: JSON.stringify({ message: 'Error submitting contact form' }),
    };
  }
};
Enter fullscreen mode Exit fullscreen mode

This Lambda function expects a JSON payload with name and email properties in the event body. It generates a unique ID for the contact, and stores the contact data in the DynamoDB table.

Finally, we need to configure the Lambda function in serverless.yml:

# serverless.yml
functions:
  app:
    handler: handler.handler
    events:
      - http:
          path: submit-contact
          method: post
          cors: true
    environment:
      CONTACTS_TABLE: ${self:service}-contacts-${opt:stage, self:provider.stage}
Enter fullscreen mode Exit fullscreen mode

This sets up an HTTP endpoint (/submit-contact) that will trigger the Lambda function when a POST request is made. The function will have access to the DynamoDB table name through the CONTACTS_TABLE environment variable.

To deploy the Lambda function and DynamoDB table, run:

serverless deploy
Enter fullscreen mode Exit fullscreen mode

The complete severless.yml file looks like this:

service: serverless-web-app

provider:
  name: aws
  runtime: nodejs18.x

resources:
  Resources:
    WebsiteBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:service}-${opt:stage, self:provider.stage}
        WebsiteConfiguration:
          IndexDocument: index.html
    ContactsTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:service}-contacts-${opt:stage, self:provider.stage}
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        BillingMode: PAY_PER_REQUEST

functions:
  app:
    handler: handler.handler
    events:
      - http:
          path: submit-contact
          method: post
          cors: true
    environment:
      CONTACTS_TABLE: ${self:service}-contacts-${opt:stage, self:provider.stage}
Enter fullscreen mode Exit fullscreen mode

Connecting the Frontend

With the backend logic in place, let's update the frontend to allow users to submit the contact form. Open website/index.html and add a form:

<!-- website/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Serverless Web App</title>
  </head>
  <body>
    <h1>Welcome to my Serverless Web App!</h1>
    <p>This is a static website hosted on AWS S3.</p>

    <h2>Contact Us</h2>
    <form id="contact-form">
      <label for="name">Name:</label>
      <input type="text" id="name" name="name" required />
      <br />
      <label for="email">Email:</label>
      <input type="email" id="email" name="email" required />
      <br />
      <button type="submit">Submit</button>
    </form>

    <script>
      const form = document.getElementById('contact-form');
      form.addEventListener('submit', async (event) => {
        event.preventDefault();

        const name = document.getElementById('name').value;
        const email = document.getElementById('email').value;

        const response = await fetch('/submit-contact', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ name, email }),
        });

        const data = await response.json();
        alert(data.message);
        form.reset();
      });
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

This adds a simple contact form to the HTML, and includes a JavaScript script that sends a POST request to the /submit-contact endpoint (which is the Lambda function we deployed earlier) when the form is submitted.

To deploy the updated website, run:

serverless client deploy
Enter fullscreen mode Exit fullscreen mode

This will upload the index.html file to the S3 bucket again.

Now, if you visit the website URL and submit the contact form, you should see a success message. You can also check the DynamoDB table in the AWS console to see the submitted contact data.

Wrapping Up

And there you have it, folks! We've built a serverless web application using AWS Lambda, S3, and DynamoDB. We've covered setting up the development environment, deploying a static website, creating a Lambda function for server-side logic, and connecting the frontend to the backend.

Serverless architectures offer numerous benefits, including scalability, cost-efficiency, and reduced operational overhead. By leveraging services like AWS Lambda, you can focus on writing code and building features, without worrying about server management. It's like having a team of hardworking elves handling the tedious infrastructure tasks for you.

Of course, this is just a simple example, and real-world serverless applications can become much more complex. But hey, we all have to start somewhere, right? The principles and services demonstrated in this article provide a solid foundation for building more advanced serverless web applications.

So, what are you waiting for? Embrace the serverless revolution and start building your next big idea today!

💖 💪 🙅 🚩
ritikbanger
Ritik Banger

Posted on March 12, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related