With the introduction of binary support in API Gateway (APIG) you can now send, for example, image binary blobs through API gateway.  I wanted to have the src attribute on an HTML img tag be a URI to an API gateway endpoint, backed by AWS Lambda.  I was unable to find clear documentation on how to do this.  The walkthrough blog post by AWS is a good start, however it adds un-necessary complexity.

Here is my attempt to make it crystal clear, using a sample image resize lambda.

Step 1: Setup API Gateway

Create a new API Gateway instance with a single GET resource.  Choose Lambda Function integration type and Use Lambda Proxy integration:
Screen Shot 2017-10-17 at 3.39.30 PM

A Lambda Proxy integration takes all the incoming HTTP request data (headers, query string, body etc) and transforms them into a lambda event object which it then reverse proxies to your Lambda function.

New Lambda Request/Response Interface

As you can imagine when your Lambda code gets control, the event object shape is different.  You can see the full input format here, however here is a snippet:

{
  "resource": "Resource path",
  "path": "Path parameter",
  "httpMethod": "Incoming request's method name"
  "headers": {Incoming request headers}
  "queryStringParameters": {query string parameters }
  "pathParameters": {path parameters}
  "stageVariables": {Applicable stage variables}
  "requestContext": {Request context, including authorizer-returned key-value pairs}
  "body": "A JSON string of the request payload."
  <span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
}

In this post, the only important attribute is queryStringParameters

You are then also responsible for responding with a specifically shaped JSON object from your Lambda that tells the APIG how to translate to an HTTP response.

The output format docs can be found here, however here is a snippet:

{
  "isBase64Encoded": true|false,
  "statusCode": httpStatusCode,
  "headers": { 'Content-Type': 'image/png', ... },
  "body": "..."
}

Important things here are:

  • isBase64Encoded if true your body must be a base64 encoded string.  I’m always sending image data, so I hardcode this to true
  • statusCode I’m going to use 200,400 and 401

Enable Binary Support

You need to tell APIG what incoming Accept headers will warrant a binary response.  In my case, my service is an image resizer that I want to use on a src attribute.  This comes from a browser, so I have no control over what your browser sends as an Accept header.  Therefore I can tell APIG that every content type should be treated as a binary response.  I do this by specifying */* in the Binary Support section of my APIG instance:
Screen Shot 2017-10-17 at 3.58.35 PM

If you know the exact Accept header the client will send (ex: server-2-server cURL) then you can hard code these.  A simple example of serving PDFs would be application/pdf​ (a simple PDF binary lambda can be found here).

Hit save and make sure to deploy your APIG to a new stage.

Step 2: Setup Lambda function

The code below is pretty straightforward, but it essentially takes an image url (u query string param) and resizes it to a new width (w query string param).  It resizes the image on disk, then returns the binary data base64 encoded (for APIG).  I also add a super simple API key (k) check.

You will then use the APIG endpoint like this in your HTML

<img src="https://blah.execute-api.us-east-1.amazonaws.com/prod?k=myKey&u=https://s3.amazonaws.com/static.me.com/images/app1234/branding/appIcon.png&w=512"><span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>

 

The Lambda code


This code could be cleaner, especially if Lambda supported non LTS NodeJs versions (>7.4  has async/await), but that is a fight for a different day.  Also the fs.unlink() are not needed, since I’m only supporting PNG and I always use the same file path.

Thanks

Hopefully this helps save someone some time.  I don’t check the comments on my blog, so hit me up on twitter if you found this useful.

Advertisements