skip to Main Content

I’m having to integrate a legacy system into using AWS API Gateway but first using Localstack to create a proof of concept.
I’m having to rapidly learn the underlying principles and concepts of everything related to the AWS system so apologies if I’m missing something "obvious".
I’m also having to write the lambda function in Node.js which I’m not particularly familiar with.

I believe I want the API Gateway to trigger a lambda function which will then pass the request onto the legacy system’s url, which will then send a response to the lambda function which will then pass it back through the gateway.
Or should I be using an HTTP integration?

Part of my issue is I can’t find any examples of this being done and searches only bring back links to the AWS documentation which is showing how to do it through the AWS API Gateway console and not through Localstack.

I’ve got a basic API Gateway example working using Localstack docker container and severless plugin which triggers a lambda function which returns a message string.
I’m using a serverless.yml to set up the gateway.

Now I’m trying to get the lambda function to make a get request to a test url for the legacy system.
My issue is it doesn’t seem to trigger the get request as none of the console.log in the http.get() are displaying in the Localstack docker container. The console.log outside of the http.get() is displaying though.
Right now I’m just trying to break it down and hit a Google url to confirm if the http.get() is running and if not, why not?

In this case it just returns the "Didn’t go through https.get?" message.
I’d expect to get errors relating to the response not being formatted correctly and/or the console.log inside the http.get() to display in the localstack docker container.

Here’s the lambda function I currently have:

'use strict';
const https = require('https');

module.exports.hello = async (event) => {
    console.log("in lambda function");
    let testURL = "https://www.google.com/";
    console.log(testURL);

    let testResponse = "";
    
    https.get(testURL, (resp) => {
        console.log("In http.get");
    let data = '';

        // A chunk of data has been received.
    resp.on('data', (chunk) => {
            console.log(("get chunk"));
            data += chunk;
    });

    // The whole response has been received. Print out the result.
    resp.on('end', () => {
            console.log("response end");
            console.log(JSON.parse(data).explanation);
            testResponse = JSON.stringify(data);
    });

    }).on("error", (err) => {
        console.log("Error: " + err.message);
        testResponse = "Error: " + err.message;
    });

    // This is to make sure something correctly formatted gets returned otherwise API Gateway
    // complains about missing values. If this returns it also implies the http.get() didn't trigger
    if(testResponse == ""){
        testResponse = {
            statusCode: 200,
            body: JSON.stringify(
                {
                    message: "Didn't go through https.get?",
                    input: event,
                },
                null,
                2
            ),
    };
    }

    console.log("about to return");
    return testResponse;
}

3

Answers


  1. Chosen as BEST ANSWER

    In the end I stumbled across an article that got it working for me.

    https://medium.com/intelliconnect-engineering/how-to-call-an-external-rest-api-from-aws-lambda-ce88e91165f6

    Be aware that I don't know if this is the "correct" way to achieve what I want. I just know that it works.

    Filippo Testini's answer also gave me a clue that the issue was to do with my misunderstanding and manipulation of async.

    This is how my handler for the lambda function ended up.

    'use strict';
    const http = require('http');
    
    
    const getStatus = (defaultOptions, path, payload) => new Promise((resolve, reject) => {
        const options = { ...defaultOptions, path, method: 'GET' };
        const req = http.request(options, res => {
            let buffer = "";
            res.on('data', chunk => buffer += chunk)
            res.on('end', () => resolve(JSON.parse(buffer)))
        });
        req.on('error', e => reject(e.message));
        req.write(JSON.stringify(payload));
        req.end();
    });
    
    
    module.exports.hello = async (event) => {
        let hostName = "jsonplaceholder.typicode.com";
        let pathName = "/posts";
    
        const defaultOptions = {
            host: hostName,
            headers: {
                'Content-Type': 'application/json',
                "accept": "application/json"
            }
        }
    
        var status_info = await getStatus(defaultOptions,pathName,'');
        
        const response = {
            statusCode: 200,
            body: JSON.stringify(status_info),
        };
        
        return response;
    };
    

  2. Just remove the "async" keyword on the handler.

    Login or Signup to reply.
  3. I’d recommend using an HTTP integration if you don’t need to enrich the request, which from the example above, you don’t.

    You can see this sample here, using Terraform as an example of an API REST Gateway calling http://httpbin.org/anything. The sample also includes integration with Lambda, but you can ignore it.

    Hope it helps!

    Sample: https://github.com/localstack/localstack-terraform-samples/blob/master/apigateway-http-proxy-authorizer/main.tf

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search