Using AWS Lambda and API Gateway, we’re going to build a very simple API and webpage for displaying the current temperature in Venice, Italy.

Prerequisites

You’ll need a free account with AWS. We’ll also be using the Serverless Framework for managing and deploying our Lambda function, so you’ll need to install and configure it to work with your AWS account. It’s a pain, but worth the effort.

Finally, you’ll need an account with OpenWeatherMap - we’ll be making use of their weather API service.

The Beginnings

With Serverless we get to define our infrastructure in YAML, so let’s begin there:

# create a directories for the service and webpage.
mkdir -p weather_in_venice/{service,webpage}

# navigate into the service directory
cd weather_in_venice/service

# create the serverless.yml file. This is where we'll declare the
# parts that make up our service.
touch serverless.yml

# Open the directory in atom (or editor of your choice!)
atom .

Describing our Infrastructure

Opening up serverless.yml, we can begin describing the parts we’ll need for our webpage to work, namely, a function for telling us the current temperature for Venice, and an HTTP endpoint to invoke our function when requested.

# Name of our service (the service being the function and integrated API endpoint)
service: WeatherInVenice

provider:
# Define *provider* specific details.

  name: aws
  # Serverless framework supports multiple cloud providers.
  # We're using AWS...

  runtime: nodejs6.10
  # AWS Lambda supports a small number of runtimes.
  # We'll use node in this tutorial

  region: us-east-1
  # See the end of this post for info on regions, if you're
  # not already familiar with them.

  functions:
  # This is where it gets exciting! We're describing our
  # function and an event for invoking the function.

    getWeather:
    # Name of our function.

      handler: function.getWeather
      # Location of our function's code, *function* is the file, and
      # *getWeather* is the name of the exported module.

      events:
      # Define events here. Multiple AWS services trigger
      # events that you can tap into. Here, we
      # only need to integrate with an API Gateway event.
        - http:
            path: weather
            # Path of the API endpoint.

            method: get
            # The HTTP method. We only need to support
            # a GET request.

            cors: true
            # We'll be calling our API endpoint from a
            # different domain, so we need to enable cors.
            # If you're not sure what cors is see end of
            # post for more info.

Without the inline comments, serverless.yml is just 13 lines long:

service: WeatherInVenice

provider:
  name: aws
  runtime: nodejs6.10
  region: us-east-1

functions:
  weatherInVenice:
    handler: function.getWeather
    events:
      - http:
          path: weather
          method: get
          cors: true

We’re getting very close to knowing the current temperature in Venice and being able to display it on a webpage.

But let’s not get ahead of ourselves. We still need to create the beating heart of our service, our Lambda Function!

Staying in the service directoy, we’ll create a file called function.js:

module.exports.getWeather = (event, context, callback) => {
/*
In serverless.yml we said that our function would be exported as
`getWeather`.
*/

  const data = { temperature: 19.2 };
  /* Define some dummy data, for now. In the next post
     we'll replace this with a live call to the OpenWeatherMap API. */


  const response = {
    statusCode: 200,
    headers: {
      "Access-Control-Allow-Origin" : "*",
    },
    body: JSON.stringify(data)
    // JSON.stringify converts the JavaScript response object
    // into a JSON string, which is what our client is expecting.
  };
  /* Build a response object. Since our function
     is responding to an HTTP request, we should
     at least define the response status code, `statusCode`,
     and the response body, `body`. Because we'll
     be depending on CORS we also need to set
     the `Access-Control-Allow-Origin` header. */


  callback(null, response);
  /* We terminate the request by invoking the `callback` function
     given to us by Lambda. We're passing `null` as the first argument
     as our function has successfully executed. If our function
     fails to execute, then we'd pass a description of the
     error as our first argument. */
};

I’ve deliberately kept the number of moving parts in our function to a minimum - I want to focus first on getting something deployed and working with our webpage.

Before we deploy let’s use the Serverless Framework to invoke our function locally, to see if there are any obvious errors we’ve missed. On the command line:

serverless invoke local --function weatherInVenice

You should see…

{
  "statusCode": 200,
  "headers": {
      "Access-Control-Allow-Origin": "*"
  },
  "body": "{\"temperature\":19.2}"
}

OK, Let’s Deploy!

This couldn’t be easier. Again, on the command line:

serverless deploy

That’s it!

The Serverless Framework will now do what it must to gracefully hand over our function and infrastructure requirements to AWS. An important part of this job is creating a CloudFormation template from our serverless.yml file. CloudFormation is another AWS service that allows you to describe your AWS resources, like S3 buckets, Lambda Functions, EC2 instances, and so on, and then have AWS build and configure them for you. Really, what we’ve been writing to serverless.yml is just a slightly higher abstraction of that. CloudFormation is an incredibly powerful service (and idea), but one we sadly haven’t the time to explore further in this post.

After running deploy, you’ll see output similar to this:

Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (239.62 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............
Serverless: Stack update finished...
Service Information
service: WeatherInVenice
stage: dev
region: us-east-1
stack: WeatherInVenice-dev
api keys:
  None
endpoints:
  GET - https://ciusxge266.execute-api.us-east-1.amazonaws.com/dev/weather
functions:
  weatherInVenice: WeatherInVenice-dev-weatherInVenice

We’re particularly interested in the output under endpoints. There you’ll see the API Gateway generated URL for invoking our function. Notice its path, /dev/weather? It contains a trailing /weather, as specified in our yaml file, but it also has /dev/ in there. Serverless allows us to deploy to different environments, [dev]elopment being the default. We could have a two stage deployment, where we test out a new feature in dev, and when we’re happy we deploy to production. This would be easily done on the command line with the --stage flag:

serverless deploy --stage prod

Anyway, back to our endpoint. Copy it and give it a try in a browser, or on the command line:

curl https://zb2a2lox1a.execute-api.us-east-1.amazonaws.com/dev/weather
{"temperature":19.2}

There’s the response body we’re expecting. Lovely! Now let’s incorporate our API into a webpage.

Back on the command navigate to the webpage directory we created:

pwd
-> /Users/omar/weather-in-venice/service
# Check where I am

cd ../webpage
# Navigate back one directory and forward into webpage

touch index.html app.js
# Create two files - one for our html markup and the other
# for our JavaScript

atom .
# Open up the directory with Atom (or editor of your choice)

Let’s get some basic markup together for index.html:


<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>What is the temperature in Venice?</title>
    <style>
      h1, h2 {
        text-align: center;
        font-family: sans-serif;
        color: #333;
      }
    </style>
  </head>
  <body>
    <h1>What is the temperature in Venice, Italy?</h1>
    <h2 id='temperature'>Checking...</h2>

    <script src="app.js" charset="utf-8"></script>
  </body>
</html>

And some behaviour for app.js:

function renderTemp(data) {
  var el = document.getElementById('temperature');
  el.innerHTML = data.temperature + " °C";
}

// replace with the endpoint created in your deployment.
var endpoint = 'https://replace-me.execute-api.us-east-1.amazonaws.com/dev/weather';

fetch(endpoint, {mode: 'cors'})
  .then(function(resp){
    return resp.json();
  })
  .then(renderTemp);

The above code is using the browser’s Fetch API for fetching data from our API Gateway resource. Notice we’ve set mode to cors. On a successful request we pass the returned JSON to renderTemp, where we assign the temperature to the innerHTML property of the h2 DOM element we created in our HTML doc.

Give it a spin on your browser. If things have gone to plan, you should have a working MVP!

In the next post we’ll work on replacing our stub data with live weather data using OpenWeatherMap’s current weather API. This’ll allow us to explore how we can set and use environment variables with AWS Lambda with help from the Serverless Framework. See you there!


The completed code for this app can be found here.

git clone git@github.com:osahyoun/code_examples.git