Practical Serverless: How to Email Yourself Chuck Norris Jokes

Serverless code on a laptop

The Case for Serverless Architecture

The serverless architecture pattern has grown exponentially in adoption over the past few years. According to CloudSkills, serverless usage increased 600% from 2020 to 2021 among AWS customers. Clearly it has moved beyond early stages to gain strong mainstream momentum.

But what exactly makes serverless so appealing?

Benefits of serverless include:

  • No server management: All infrastructure is handled by the cloud provider allowing teams to solely focus on application code. No more configuring clusters, scaling, or managing servers.

  • Pay per use pricing: Never pay for idle capacity again. Only pay for the actual compute resources used down to the millisecond when functions execute.

  • Auto-scaling: Serverless apps seamlessly scale up and down to any volume without capacity planning. No more worrying about traffic spikes causing downtime.

  • Faster time to market: Get from idea to production faster by removing infrastructure tasks. Less configuration and lower operational burden.

  • Increased productivity: Spend less time on infraops and more time building features that deliver direct business value. Serverless translates to higher velocity for engineering teams.

As a result, serverless adoption grew from 23% of organizations in 2019 to 47% by 2020 according to Datadog. The top benefits realized were faster time to market, productivity gains, and cost savings. Serverless delivers on the promise of focusing engineering resources more on creating differentiated value rather than commodity infrastructure.

AWS Lambda Overview

The most widely used serverless platform today is AWS Lambda. Lambda functions abstract all infrastructure away making the service infinitely scalable. Just package code and dependencies into a deployable bundle and Lambda handles everything required to run and scale.

Some common use cases well suited for Lambda include:

  • Data processing: Stream processing, ETL pipelines, file processing
  • Integration & orchestration: Gluing services together in workflows
  • Web apps: Processing requests from front-end apps
  • Mobile apps: Processing events from mobile apps
  • Chatbots: Language processing and dialog management
  • Reactions to events: Trigger functions in response to changes

Key capabilities provided by Lambda include automated scaling, concurrency controls, versioning, environment variables, logging, and more.

Pricing is based purely on execution duration plus requests. There is no charge when functions are idle making the service very cost efficient.

Alternative services offering serverless functionality include Google Cloud Functions, Azure Functions, Cloudflare Workers, and more. The major cloud platforms all offer compelling serverless solutions – Lambda just has the most extensive capabilities currently.

Serverless Framework Overview

While AWS Lambda provides the core infrastructure, the Serverless Framework radically simplifies building on top of it.

Using the Serverless Framework abstracts away all the intricacies of stitching together Lambda, networks, storage systems, credentials, access controls, and more.

The framework handles all this complexity through simple YAML configuration files. This allows engineers to focus on writing business logic in the form of functions.

Some key aspects of the Serverless Framework:

Function Definition: Define functions, their triggers, access controls, and more in easy config files

Services & Resources: Group functions into services with shared permissions and resources

Secrets Management: Securely store sensitive credentials and tokens

Custom Plugins: Extend functionality by installing pre-built plugins

Tooling: CLI for streamlined deploying, invoking, and debugging

Vendor Agnostic: Supports AWS, Azure, GCP, Cloudflare, etc.

By abstracting the nitty gritty infrastructure details, the Serverless Framework gives developers a simplified way to tap into the full power of serverless platforms like AWS Lambda.

Creating a Serverless Service

Enough background – let‘s dive into actually building something! Our goal is to create a serverless application using the Serverless Framework that emails us a Chuck Norris joke every morning.

We‘ll use AWS Lambda and Node.js, but similar principles apply across providers and languages.

Initialization

First, scaffold a new service:

serverless create --template aws-nodejs --path chuck-jokes
cd chuck-jokes

The template generates a starter config with a single function that we can begin extending.

Take a quick look at serverless.yml which defines the service:

# serverless.yml
service: chuck-jokes

provider:
  name: aws
  runtime: nodejs12.x

functions:
  hello:
    handler: handler.hello

plugins:
  - serverless-offline # For testing locally

Let‘s install plugins and test locally:

npm install
serverless offline # Starts local Lambda emulator  
serverless invoke local -f hello -d ‘{}‘ # Invokes hello function

We should see successful output from the test event! Our environment is set up for local development.

Fetching Jokes via API

We need a way to source jokes. ChuckNorris.io provides a free JSON API we can leverage.

Define a new function in serverless.yml:

functions:

  getJoke: 
    handler: handler.getJoke
    events:
      - http:  
          path: joke
          method: get

This exposes an API Gateway HTTP endpoint to trigger the function.

Next, fetch a random joke using axios and return it:

// handler.js
const axios = require(‘axios‘); 

module.exports.getJoke = async () => {

  try {
    const url = ‘https://api.chucknorris.io/jokes/random‘;
    const response = await axios.get(url);

    return {
      statusCode: 200,
      body: JSON.stringify({ 
        joke: response.data.value
      })
    };

  } catch (error) {
    // Implement error handling  
    return {
      statusCode: 500,
      body: ‘Failed to fetch joke‘  
    }
  }

}

We try/catch to handle errors gracefully. Now let‘s test:

serverless invoke local -f getJoke -l

It should return a random Chuck Norris gag! Our backend is fetching jokes successfully.

Tip: Using -l logs full output helpful for debugging what is returned.

Sending Automated Emails

Next we need to email ourselves the joke every morning using Nodemailer.

First install the package:

npm install nodemailer 

Then reference it in the new function:

// handler.js

const nodemailer = require(‘nodemailer‘);

module.exports.emailJoke = async () => {

  const joke = await getJoke(); // Fetch a joke

  // Configure Nodemailer transport 
  const transporter = nodemailer.createTransport({
    service: ‘Gmail‘,
    auth: { 
      user: process.env.EMAIL_USER,  
      pass: process.env.EMAIL_PASS  
    }
  });

  // Compose message
  const message = {
    from: ‘Chuck Norris Jokes <[email protected]>‘,
    to: ‘[email protected]‘,  
    subject: ‘Your Daily Chuck Norris Joke‘,
    text: joke
  };

  // Send email
  await transporter.sendMail(message);

  return {
    statusCode: 200,
    body: JSON.stringify({ msg: ‘Email sent!‘ })
  };

};

We configure a Gmail transport then construct a message that includes our fetched joke before sending it off.

Setting Environment Variables

Hard coding credentials in handler code creates security issues. Instead we should use environment variables.

The Serverless Framework provides multiple methods to manage this. One simple approach is using a secrets file:

  1. Create secrets.yml file:

    dev:
      EMAIL_USER: [email protected]
      EMAIL_PASS: topsecret 
  2. Reference in serverless.yml:

    provider:
      environment:
        EMAIL_USER: ${file(secrets.yml):dev.EMAIL_USER}  
        EMAIL_PASS: ${file(secrets.yml):dev.EMAIL_PASS}   

This injects the creds safely into our functions at runtime.

Other options like AWS Parameter Store and AWS Secrets Manager provide more advanced capabilities for secrets management. Evaluate based on your needs.

Local Invocation

With the full flow implemented, let‘s test locally:

serverless invoke local -f emailJoke

We should see the function fetch a joke, email it, and output the response.

Experiment with breaking parts of the function and using the logs to troubleshoot issues.

The invoke local workflow is much faster than continually deploying during development – taking full advantage cuts debug cycles significantly.

Infrastructure as Code

Our emailer service is fully defined in code via serverless.yml making the architecture highly portable. We could replicate the same setup across AWS accounts, regions, and cloud providers with minimal effort by deploying the same scripts.

Changes can be tracked in Git allowing us to easily evolve infrastructure right alongside code. The days of manually configuring servers or clicking around cloud consoles are over!

Infrastructure defined as code through Serverless revolutionizes how we build and operate backend systems.

Deploying to AWS

With our automated joke emailer tested locally, let‘s deploy it live to AWS.

serverless deploy

This bundles code, configures Lambda functions, API Gateway, IAM permissions, and synthesizes everything needed to run the app in AWS.

Serverless handles all provisioning and networking complexity behind the scenes allowing us to go from local to cloud with a single CLI command!

After deploying we can invoke the function remotely:

serverless invoke -f emailJoke 

We should now receive an email sent straight from our serverless application hosted in AWS.

Setting up Scheduled Invocation

Rather than manually invoking it, we likely want this to run automatically every morning.

Simple add a CloudWatch scheduled event as a trigger:

functions:

  emailJoke:
      handler: handler.emailJoke
      events:
        - schedule:
           rate: cron(0 8 * * ? *) 
           input:
             key: ‘value‘

This configures CloudWatch Events to invoke the emailJoke function daily at 8AM UTC.

We can pass the optional input payload to the function containing any data needed.

Now we‘ll reliably receive Chuck Norris gags every morning without any servers to manage!

Closing Thoughts

That wraps up this full journey of building a practical serverless application!

To quickly recap:

  • We leveraged AWS Lambda and Serverless Framework to avoid managing infrastructure
  • Used an API to fetch jokes then email them automatically
  • Configured environment variables securely
  • Tested the entire flow locally with sls invoke local
  • Deployed the backend to AWS and called it over HTTP
  • Added scheduled invocation to email daily jokes

Through this simple example, we explored key serverless concepts like event-driven architecture, functions as a service, and infrastructure as code.

As highlighted, Serverless Framework combined with AWS Lambda provides a robust platform for developing cloud-based applications rapidly. minimal overhead with only business logic to focus on.

There are many additional capabilities we could tap into like adding a database, user authentication, monitoring & alerts, and more. Serverless removes nearly all limits on building modern applications.

I highly recommend exploring serverless platforms combined with frameworks like SST or Serverless Framework as an alternative to traditional servers or container orchestrators. Give your developers superpowers by unleashing innovation velocity!

I hope this guide gave you a solid overview of developing practical serverless applications. Feel free to reach out via email if you have any other questions!

Cheers,

John Doe
Senior Cloud Architect
Awesome Company

Similar Posts