How to make an external api call within an AWS Lambda function

元气小坏坏 提交于 2020-06-11 05:20:30

问题


I'm having problem with a Lambda function which for some reason it goes on timeout.

I tried to add some console.log to understand what was going on and everything works properly until the external http request.

I use the npm module request

I can see the log before the request, but the one inside the callback never shows up, like if the endpoint doesn't respond.

Am I missing something? Thanks in advance!

var mysql = require('mysql'); 
var request = require('request');
var config = require('./config.json'); 

var pool = mysql.createPool({
  host: config.dbhost,
  user: config.dbuser,
  password: config.dbpassword,
  database: config.dbname
});

var openWeatherOptions = (device) => {
    let domain = 'https://api.openweathermap.org/data/2.5/';
    let apiEndpoint = 'weather?';
    let params = `lat=${device.lat}&lon=${device.lng}&APPID=${process.env.WEATHER_APP_ID}&units=metric`;
    let url = [domain, apiEndpoint, params].join('').toString();
    return {
      url: url,
      method: 'GET',
      headers: { 'Content-Type': 'application/json'},
    };
};

exports.handler = (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    // Connect to DB
    console.log('fallback connecting to DB')
    pool.getConnection( (errOnConnection, connection) => {
        if(errOnConnection) {
            callback({"err": errOnConnection });
        }
        console.log('fallback connected to DB')
        // Retrieve device position
        let queryOne = 'SELECT lat, lng FROM T03_DevicesPosition WHERE deviceCode = (?);';
        console.log('fallback retrieving device position')
        connection.query( queryOne, [event.device], (errOnQ1, results, fields) => { 
            if(errOnQ1) {
                connection.release();
                callback({"err": errOnQ1 });
            }
            console.log('fallback device position retrieved')

            // Call openweather 
            let device = results[0];
            let options = openWeatherOptions(device);
            console.log('fallback calling openWeather with following data: ', device, options);

            request( options, (errOpenWeather, response, body) => {
                console.log('fallback openweather response received');
                if(errOpenWeather || (response.statusCode !== 200 && response.statusCode !== 201) ) {
                    connection.release();
                    callback({"err": errOpenWeather });
                }
                let meteo = JSON.parse(body).main;
                meteo.date = new Date(event.time*1000);
                meteo.pressure = Math.floor( meteo.pressure );
                console.log('fallback storing data', meteo);
                let query = `INSERT INTO T02_DevicesTransmissions (deviceCode, transmissionDate, temperature, humidity, pressure, unixDate, rawData) VALUES ( ?, ?, ?, ?, ?, ?, ?);`;
                let queryValues = [ event.device, meteo.date, meteo.temp, meteo.humidity, meteo.pressure, event.time, 'fallback']; 
                connection.query( query, queryValues, (errInsert, results, fields) => { 
                    connection.release();
                    console.log('fallback completed with', errInsert ? '' : 'out');
                    if (errInsert) callback({"err": errInsert });
                    else callback();
                });
            });

        });
    });
}

回答1:


This is an old question but I spent a few hours banging my head figuring out how to set this up properly, so here's hoping someone else's time will be saved :)

If your lambdas are in a VPC they need to go through a NAT Gateway in order to reach external services.

One way to achieve this is:

  • configure your lambda to use a (or multiple) specific subnets (the Lambda console will suggest you associate at least 2 subnets in different availability zones to your lambda to ensure availability)
  • create a NAT Gateway in a different subnet
  • have the route table associated with your lambda's subnets send all outbound traffic (0.0.0.0/0) to the NAT Gateway you created (you do that by choosing the NAT in the target field)
  • have the route table in the NAT's subnet send all outbound traffic to an Internet Gateway

This will route traffic from your lambdas properly through the NAT and Internet Gateway so they can reach external services.

Note that if you're also using RDS from your lambdas AND you intend to also connect to RDS from outside (e.g. to manage the DB from your local machine), you can't put the RDS instance in the lambdas' subnet, or you won't be able to connect to it from outside (the NAT is outbound only). In that case you should make sure your RDS instance is associated with a subnet that is accessible, for instance the one the NAT is in (i.e. the one that sends outbound traffic to the Internet Gateway).




回答2:


This is your solution, please read.

https://forums.developer.amazon.com/questions/95692/invoking-a-rest-api-from-within-a-lambda-function.html

Further

This code sample shows how to call and receive external rest service data, within your skill Lambda code. https://github.com/robm26/SkillsDataAccess/blob/master/src/CallService/index.js




回答3:


You don't have a VPC set, right? And if you do, have to check that you have a NAT gateway attached if you're on a private subnet.



来源:https://stackoverflow.com/questions/50106048/how-to-make-an-external-api-call-within-an-aws-lambda-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!