/ Programming

Dynamic Links Using AWS API Gateway and Lambda

Back when I was a little more active on this blog of mine, I had an idea. I wanted to make it so that the main photo for my blog would update based on my latest photo from Instagram. I am sure there are services out there that provide such a thing. But I like to needlessly write my own. I wanted to know for myself. AND! I wanted to know how I would do it with AWS.

I had never done anything with AWS let alone Lambda. I understood the basic concept of what lambda could provide. But let's be honest here... Like I knew what I was getting myself into...

Something... something... serverless

Actually, this is a great basic project to show the usefulness of Lambda and API gateway. I didn't want to create a Hapi server with a single endpoint and have it run for hours on end only to be called maybe 1 or 2 times a week. It just made sense to use Amazon's services.

I was trying to figure out how I wanted to send the client the image. First I thought I would take a Lambda and have it download the image and forward the binary onto the client telling it that the payload is an image. This would make it so there are no redirects. I found that issuing a redirect is a lot easier and accomplishes my original goal but I would like to one day forward the actual image. Maybe even cache it for faster response times.

Getting into things

You will need a pretty basic serverless.yml.

service:
  name: instagram
provider:
  name: aws
  runtime: nodejs6.10
environment:
  IG_TOKEN: '' # You will need to populate this later
functions:
  getLatestPhoto:
    handler: handler.getLatestPhoto
    description: grabs the latest instagram photo for a given api token user

That's it. Now you need to create your lambda function in your root index.js. Like so:

const fetch = require('node-fetch');
const get = require('lodash.get');

const url = `https://api.instagram.com/v1/users/self/media/recent/?access_token=${ process.env.IG_TOKEN }`;

module.exports = {
  getLatestPhoto(event, context, callback){
    return fetch(url, { headers: { 'Accept': 'application/json' } })
      .then((res) => res.json())
      .then((json) => {
        const result = { 
          url: get(json, 'data[0].images.standard_resolution.url') 
        };
        callback(null, result);
      })
      .catch(console.error);
  }
}

Given that Instagram's API doesn't change very often, and using Babel to compile down to Lambda's latest node version (v6.10) this code should work out of the box. You will need to get an instagram key, which I am not going to go over. (I have faith in your googling powers)

API Gateway

My api gateway route is pretty basic. I created a resource called /instagram/latest-photo. The most important things for this resource is that it should be a GET request. It needs to redirect with a 302 and we need to grab the url out of the response body from the lambda that is accessing the api.

Integration Response

Be sure to add a header of Location and a mapping of integration.response.body.url. This will set the url we returned from our lambda to the Location header.

integration-response-details-1

Method Response

The method response should return a 302 with the header Location. Response body does not need to be application/json and can be text.

method-response

Deployment

After all those steps are in place you will need to deploy a stage, which I called mine dev. Once that has been done, you can find the invoke url under that stage. You can take the url plus your resource route to finally invoke your newly created link.

The result:

The resulting image below is the latest image from instagram. You were able to load it without needing to use an api key or try to copy their image urls. I can give this url out to anyone and have them use it if I so desired.

My Current Instagram Photo