Apigee to AWS Lambda

IMPORTANT

An updated version of the solution is available in this article.

https://www.googlecloudcommunity.com/gc/Cloud-Product-Articles/ApigeeX-and-AWS-Lambda/ta-p/427109

 

 

 

APIs on Apigee can talk to AWS Lambda functions as a backend. This article provides an outline of an approach to perform the connectivity using AWS Javascript SDK (without using a127).

AWS provides SDKs for several languages and we will be using the Javascript SDK 2.149.x in particular here in Apigee node.js. The AWS Java SDK using Apigee Java Callout is an alternative but the SDK performs Java calls that are blocked by Apigee, getEnv() in this case (This can change in the future). You can also connect to AWS Lambda directly using Apigee TargetServer and make direct REST calls to Lambda without using an SDK.

A sample node.js app that you can deploy to Apigee looks as follows:

var AWS = require('aws-sdk');
var apigee = require('apigee-access');
var http = require('http');
var lambda = null;

console.log('node.js application starting...');

var server = http.createServer(function (request, resp) {

    var accessKeyId = apigee.getVariable(request, 'aws.accessKeyId');
    var secretAccessKey = apigee.getVariable(request, 'aws.secretAccessKey');
    var region = apigee.getVariable(request, 'aws.region');

    var awsCredentials = new AWS.Credentials({accessKeyId: accessKeyId, secretAccessKey: secretAccessKey});
    AWS.config.region = region;

    if(!lambda){
        lambda = new AWS.Lambda();
    }


    AWS.config.credentials.get(function(error){
        if(error){
            console.log(error);
            resp.end('Error: ' + error);
        }
        else {
            var lambda = apigee.getVariable(request, 'aws.lambda');
            var requestPayload = apigee.getVariable(request, 'request.content');
            var params = {
                FunctionName: lambda,
                InvocationType: 'RequestResponse',
                LogType: 'Tail',
                Payload: requestPayload
            };


            // Invoke Lambda
            lambda.invoke(params, function (err, data) {
                if (err) {
                    resp.end('Error: ' + err);
                } else {
                    resp.end(data.Payload);
                }
            });
        }
    });
});


server.listen(9000, function () {
    console.log('Node HTTP server is listening');
});

package.json

{
  "name": "lambda-sample",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "aws-sdk": "^2.149.0"
  }
}

In the request flow add a KVM policy to fetch AWS credentials from a secure KVM.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations async="false" continueOnError="false" enabled="true" name="KVMFetchAWSConfig" mapIdentifier="aws-config">
    <ExpiryTimeInSecs>3600</ExpiryTimeInSecs>
    <Get assignTo="aws.accessKeyId">
        <Key>
            <Parameter>accessKeyId</Parameter>
        </Key>
    </Get>
    <Get assignTo="aws.secretAccessKey">
        <Key>
            <Parameter>secretAccessKey</Parameter>
        </Key>
    </Get>
    <Get assignTo="aws.region">
        <Key>
            <Parameter>region</Parameter>
        </Key>
    </Get>
    <Get assignTo="aws.lambda">
        <Key>
            <Parameter>region</Parameter>
        </Key>
    </Get>
    <Scope>environment</Scope>
</KeyValueMapOperations>

Populate the KVM aws-config with the necessary AWS config.

Some considerations to note:

  1. The AWS SDK takes care of fetching OAuth access tokens from AWS and refreshing it as required
  2. The node.js tier can remain thin and just take care of the SDK calls
  3. Add API security like spike arrest, quota, threat protection policies
  4. Add caching and any transformation of JSON response using Apigee policies

Benefits

  1. Apigee serves as the single API platform that can front-end modern cloud backend like AWS Lambda and also allowing you to front-end legacy backend while presenting a consistent API experience to your customers, partners and vendors
  2. Stay up to date with developments in AWS SDK by consuming it directly
  3. Leverage Apigee's Analytics, Security and App lifecycle management capability to simplify API lifecycle
Contributors
Comments
Not applicable

The error with your code is, you have declared the var server, but used it as ser in the last part of the code.

madhans
Staff

Thanks @Preethi Ravichandran Fixed it.

gargi_talukdar
New Member

@Madhan Sadasivam, Please let me know how we can call AWS lambda using target server and not AWS-SDK.

kurtkanaskie
Staff

Thanks for the great write up, this got me most of the way!

I used an encrypted KVM (best practice for credentials).

Perhaps due to a newer version of the aws-sdk (2.264.1) but I didn't use:

var awsCredentials = new AWS.Credentials...
AWS.config.credentials.get...

Instead I used 'AWS.config.update', here is my simple example:

var AWS = require('aws-sdk');
var apigee = require('apigee-access');
var http = require('http');
var lambda = null;

var server = http.createServer(function (request, resp) {
    if (!lambda) {
        var accessKeyId = apigee.getVariable(request, 'private.accessKeyId');
        var secretAccessKey = apigee.getVariable(request, 'private.secretAccessKey');
        var region = apigee.getVariable(request, 'private.region');
        AWS.config.update({accessKeyId: accessKeyId, secretAccessKey: secretAccessKey, region: region});
        lambda = new AWS.Lambda();
    }

    var requestPayload = apigee.getVariable(request, 'request.content');
    var params = {
        FunctionName: 'get-pingstatus-status',
        InvocationType: 'RequestResponse',
        LogType: 'Tail',
        Payload: requestPayload
    };

    lambda.invoke(params, function (err, data) {
        if (err) {
            resp.end('Error: ' + err);
        } else {
            resp.end(data.Payload);
        }
    });
});

server.listen(process.env.PORT || 9000, function() {
    console.log('Node HTTP server is listening');
});
mark_hammelman
Bronze 5
Bronze 5

In case somebody else ends up here from the search, its worth mentioning that Apigee Edge Cloud now has an 'extension' for connecting to an AWS Lambda that can be used instead of building a nodejs proxy. https://docs.apigee.com/api-platform/reference/extensions/aws-lambda/aws-lambda-extension-100

Version history
Last update:
‎05-31-2022 03:11 PM
Updated by: