Unable to receive/send response from a Promise in Lambda function - javascript

I am having a Lambda function, in which I am taking a parameter from Query String and using it to call a downstream service that will inturn return a promise. I am unable to send back the response received by the Promise.
This is my Lambda function:
export const handler = async (event) => {
let sessionId = event.queryStringParameters.sessionId;
const testClient = new Client.WebStoreClient(config);
const sessionResponse = getSession(testClient, sessionId);
sessionResponse.then(function (result) {
console.log(result);
});
const response = {
statusCode: 200,
body: JSON.stringify(sessionResponse),
};
return response;
};
async function getSession(testClient, sessionId) {
let data = await testClient.getSession(sessionId, headers)
.then((response) => response.body);
return data;
}
When I am executing this, I am getting a null response: { statusCode: 200, body: '{}' } from the Lambda function output. I tried to console log the value that I have gotten and was receiving this: Promise { <pending> }
I am suspecting this to be a Javascript/Typescript Promise issue which I am slightly neglecting. Any help is appreciated here. Thanks in advance.

In your response, you are trying to stringify the promise instead of the promise value and it gives you an empty object as a String.
You can try it:
JSON.stringify(Promise.resolve(1)) // output is "{}"
You can only access the value of a Promise from inside the callback you pass to then.
Think of it this way: inside of then is the future, the surrounding code is the present. You can't reference future data in the present.
You probably wanted to write
return sessionResponse.then(function (result) {
return {
statusCode: 200,
body: JSON.stringify(result),
}
});
The above returns a promise containing your response.
You could have also just written getSession this way
function getSession(testClient, sessionId) {
return testClient.getSession(sessionId, headers)
.then((response) => response.body);
}
I suggest that you practice using promises without async/await

An alternative to geoffrey's answer, note that both of your functions are async, meaning you can use the same await keyword you used in getSession().
export const handler = async (event) => {
// ...
const sessionResponse = await getSession(testClient, sessionId);
// ^
console.log(sessionResponse);
const response = {
statusCode: 200,
body: JSON.stringify(sessionResponse),
};
return response;
};
async function getSession(testClient, sessionId) {
let data = await testClient.getSession(sessionId, headers)
.then((response) => response.body);
return data;
}
This is roughly the equivalent of...
export const handler = (event) => {
// ...
return getSession(testClient, sessionId).then((data) => {
console.log(data);
const response = {
statusCode: 200,
body: JSON.stringify(sessionResponse),
};
return response;
});
};
function getSession(testClient, sessionId) {
return testClient
.getSession(sessionId, headers)
.then((response) => response.body);
}
Note that your getSession() function is returning a Promise by virtue of async (and in the translated example above). Your handler() function is likewise returning a Promise, so whatever is calling it will need to use a .then() or be async itself to leverage an await. As you can see this asynchronous requirement cascades.

Related

Sinon stub an axios post, the return value is a undefined and can't be resolved

function to be tested
export const get_drawing_data = (url, request_obj) => {
axios
.post(url, JSON.stringify(request_obj), {
headers: {
// Overwrite Axios's automatically set Content-Type
"Content-Type": "application/json",
},
})
.then((response) => {
return response;
})
.catch((error) => {
console.log(error);
});
};
Sinon stub is used to intercept the Axios post call. It simulates the axios.post return value which is a Promise. The resolve of the Promise takes the API response data as a value. So the following line use sinon.stub resolves function created a promise as axios post function return value.
describe("test axios get_drawing_data", () => {
it("test input and output", () => {
const requestData = {a: "hello world"};
const responseData = "hellow world";
sinon.stub(axios, "post").resolves(responseData);
const response = get_drawing_data("http://127.0.0.1/xxx", requestData);
expect(response).toEqual(["hello world"]);
});
});
The expectation is response.data should be equal to the constructed value in Promise. But actually it's a undefined.
expect(received).toEqual(expected) // deep equality
Expected: ["hello world"]
Received: undefined
There are a few problems here...
Your get_drawing_data function doesn't return anything
The Axios defaults are already perfect for posting JSON without custom headers or payload transformation
Don't catch promise rejections and transform the promise into a resolved one, especially by resolving with an undefined value. Your consumers will not handle that response correctly.
You'd probably be more interested in the Axios response data than the entire response
You should await the result of calling get_drawing_data()
Not sure why you'd expect an array when you've already told it what to return
With that in mind, try the following
export const get_drawing_data = async (url, request_obj) => {
try {
const { data } = await axios.post(url, request_obj);
return data;
} catch (err) {
console.error("get_drawing_data", err.toJSON());
throw err; // keep the promise chain rejected
}
};
or if you don't like async / await...
// note the arrow function has no `{...}`, ie an implicit return
export const get_drawing_data = (url, request_obj) =>
axios
.post(url, request_obj)
.then(({ data }) => data)
.catch((err) => {
console.error("get_drawing_data", err.toJSON());
return Promise.reject(err); // keep the promise chain rejected
});
and in your test
describe("test axios get_drawing_data", () => {
it("test input and output", async () => { // async function here
const url = "http://127.0.0.1/xxx";
const requestData = { a: "hello world" };
const responseData = "hellow world";
// resolve with something closer to the Axios response schema
sinon.stub(axios, "post").resolves({ data: responseData });
// await the result
const response = await get_drawing_data(url, requestData);
// verify the stub was called as expected
sinon.assert.calledWith(axios.post, url, requestData);
// compare the response with what you actually set up
expect(response).toEqual(responseData);
});
});

Nodejs wait till async function completes and print the results

I want to wait on the HTTP POST request to complete and then return response to the caller function. I am getting Undefined when I print the received results.
I have defined post method as below:
// httpFile.js
const axios = require('axios');
module.exports = {
getPostResult: async function(params) {
console.log("getPostResult async called...");
var result = await axios.post("https://post.some.url", params)
.then ((response) => {
console.log("getPostResult async success");
return {response.data.param};
})
.catch ((error) => {
console.log("getPostResult async failed");
return {error.response.data.param};
});
}
}
And I am calling it in this way:
// someFile.js
const httpFile = require('./httpFile');
// Called on some ext. event
async function getPostResult() {
var params = {var: 1};
var result = await httpFile.getPostResult(params);
// Getting Undefined
console.log("Done Result: " + JSON.stringify(result));
}
I don't want to handle the .then and .catch in the calling function as I want to return the different values based on the POST result.
How should I wait for the response and get the return results.
In the above code I am getting the log statements as expected and "Done Result" get printed at the very end after the 'getPostResult' returns.
you are using both await & .then thats why it returns undefined.
this is how it should look
// httpFile.js
const axios = require('axios')
module.exports = {
getPostResult: async function (params) {
try {
const res = await axios.post('https://post.some.url', params)
return res.data
} catch (error) {
// I wouldn't recommend catching error,
// since there is no way to distinguish between response & error
return error.response.data
}
},
}
if you want to catch error outside of this function then this is way to go.
getPostResult: async function (params) {
const res = await axios.post('https://post.some.url', params)
return res.data
},

Wait for response from request before returning

I am trying to create a function with a GET request that returns a portion of the data from the GET request. However, it keeps returning before the data is retrieved, so I keep getting "undefined". How can I set this up so it actually waits for the data to be set before returning?
let getInfo = async () => {
const request = net.request({
url: URL
})
return new Promise((resolve, reject) => { // Promise being here DOES work
request.on('response', (response) => {
response.on('data', (chunk) => {
//return new Promise((resolve, reject) => { //Promise being here does NOT work
let body = JSON.parse(chunk)
let info = body.data
if (info){
resolve(info);
}
reject();
//})
});
});
request.write('')
request.end()
}).then(data => {
console.log("From then: "+data)
return data
})
}
getInfo().then(data => {
console.log("From outside: "+data)
})
Edit: This is the updated version that still does not work. I am trying to use the native electron method and I don't see why this doesn't work. The "From then:" part displays the info correctly. But when run "From outside:" it prints undefined. Does the issue have anything to do with the response.on being nested inside the request.on?
Solution: As #NidhinDavid showed in his answer, the issue was that the promise was inside the 'response' listener. Moving the 'GET' request from start to finish inside the Promise fixed it to giving the correct output. I have updated my code to reflect that for future individuals.
let getInfo = () => {
let info;
const request = net.request({
url: URL
})
return new Promise((resolve, reject) => {
request.on('response', (response) => {
response.on('data', (chunk) => {
request.write('')
request.end()
let body = JSON.parse(chunk)
info = body.data
if (info) {
resolve(info)
} else {
reject('Something went wrong');
}
});
});
})
}
getInfo()
.then(data => {
// this will be your info object
console.log(data)
})
.catch(err => {
// this will log 'Something went wrong' in case of any error
console.log(err)
})
You need to return inside your, on type event handler. Read more about asynchronous code and synchronous code here
I couldn't find the net module and the one which is included with Nodejs do not have request method. So to get the similar concept of event emiters and promise I am using http module and doing a http request to fetch json and parse it
'use strict'
var https = require('https');
const getInfo = async () => {
// create a new promise chain
// remember it is a chain, if one return is omitted
// then the chain is broken
return new Promise((resolve, reject) => {
var options = {
host: 'support.oneskyapp.com',
path: '/hc/en-us/article_attachments/202761727/example_2.json'
};
// start the request
https.request(options, function (response) {
var str = '';
// data arrives in chunks
// chunks needs to be stitched together before parsing
response.on('data', function (chunk) {
str += chunk;
});
// response body obtained
// resolve (aka return) the result
// or parse it, or do whatever you want with it
response.on('end', function () {
resolve(str)
});
// errors are another event
// listen for errors and reject when they are encountered
response.on('error', function (err) {
reject(err)
})
}).end()
})
}
//*********************************************
// using async await
//*********************************************
// if this is the entry point into app
// then top-level async approach required
(async ()=>{
try{
let data = await getInfo()
console.log("From ASYNC AWAIT ")
console.log(JSON.stringify(JSON.parse(data)))
}
catch (err) {
console.log("operation failed, error: ", err)
}
})();
//************************************************
// using promise chains
//************************************************
getInfo()
.then((data)=>{
console.log("FROM PROMISE CHAIN ")
console.log(JSON.stringify(JSON.parse(data)))
})
.catch((err)=>{
console.log("operation failed, error: ", err)
})
Tyr this, it might works for you,
let info;
const getInfo = async (_url)=>{
const response = await fetch(_url);
const data = await response.json();
info = data;
} ;
const url = "some url";
getInfo(url);
console.log(info);
Async function always returns a promise, so either consume that promise or internally await the data and assign it to some variable.
Check for the valid data required in info by logging it to the console.

Make axios call from another axios promise and save returned data

I am making an axios call from promise of another axios call, the code looks like below, the call is basically from the first method to the second method.
UPDATED CODE BASED ON SUGGESTION
It simply says: Can not use keyword 'await' outside an async function then I tried
var data = async () => {
await this.checkParentLoggerLevel();
};
still did not work
async updateLevel(logger, level, index) {
alert(index);
axios
.post(
'url'
)
.then(() => {
var data = await this.checkParentLoggerLevel();
alert('waiting');
alert(
'This will be executed before the second methods returns HEllo'
);
alert(data);
});
},
Second method:
async checkParentLoggerLevel() {
alert('inside checkParentLoggerLevel ');
return await axios
.get('url')
.then(() => {
alert('returning hello');
return 'hello';
});
},
My aim to to save the returned hello to data variable in first method.This is not working. The other problem is after the this.checkParentLoggerLevel() methods call code execution continues and does not wait for the retuned value.
This happens because inside checkParentLoggerLevel you are not waiting for the axios promise to complete. You can do it like:
async checkParentLoggerLevel() {
alert('inside checkParentLoggerLevel ');
return await axios
.get('url')
.then((res) => {
return 'hello';
});
}
Also, you need to await inside updateLevel like:
async updateLevel() {
axios
.post(url)
.then(async (res) => {
var data = await this.checkParentLoggerLevel();
alert("This will be executed before the second methods returns HEllo");
alert(data);
});
}
You should chain the promises, so:
updateLevel() {
axios
.post(url)
.then(res => {
return this.checkParentLoggerLevel.then(data => ([res, data]);
})
.then(([res, data]) => {
// here
});
}
Or simply with async await:
async updateLevel() {
const res = await axios.post(url);
const data = await this.checkParentLoggerLevel();
// Do whatever you want
}

Typescript: how to structure a fetch API call inside a method that returns a Promise response

Maybe a trivial one, but I am new with Typescript and fetch API.
In an exported class I have a public method remoteFetchSomething like:
export class className {
remoteFetchSomething = (url : string) : Promise<Response> => {
return fetch(url)
.then(
(r) => r.json()
)
.catch((e) => {
console.log("API errore fetching " + objectType);
});
}
}
export const classInstance = new className();
The method queries a remote JSON API service, and in the code, I am using it like:
import { classInstance } from ...
classInstance.remoteFetchSomething('https://example.url')
.then((json) => {
console.log(json);
}
)
The console.log is actually showing the results, but the remoteFetchSomething returns a Promise and I am unable to parse and access the JSON object values.
I would like to wait for the response before executing the remaining code, but how do I unwrap content from promise? Should I again put another .then? What am I missing?
Thank you.
By now I resolved the problem defining the return type of the remoteFetch as any:
remoteFetchSomething = (url : string) : any => {
return fetch(url)
.then(
(r) => r.json()
)
.catch((e) => {
console.log("API errore fetching " + objectType);
});
}
And now I can access JSON values like data below:
classInstance.remoteFetchSomething('https://example.url').then(
(json) => {
console.dump(json.data);
}
)
[sincerely still not clear why I cant' use the Promise<Response> type]
You can't synchronously block while waiting for a request in javascript, it would lock up the user's interface!
In regular javascript, and most versions of TypeScript, you should be / must be returning a promise.
function doRequestNormal(): Promise<any> {
return fetch(...).then(...);
}
function someOtherMethodNormal() {
// do some code here
doRequestNormal.then(() => {
// continue your execution here after the request
});
}
In newer versions of typescript, there's async/await keyword support - so instead it might look like this:
async function doRequestAsync() {
var result = await fetch(...);
// do something with request;
return result;
}
async function someOtherMethodAsync() {
// do some code here
var json = await doRequestAsync();
// continue some code here
}
Keep in mind, doRequestAsync still returns a Promise under the hood - but when you call it, you can use await to pretend that you're blocking on it instead of needing to use the .then( callback. If you call an async method from a non-async method, you'll still need to use the callbacks as normal.
this is how I do it:
type Payload = {
id: number
}
type ReturnType = number
export const functionThatHasNumberType = async (
payload: Payload
): Promise<ReturnType> => {
let url = `/api/${payload.id}`
return await axios.get(url)
}

Categories

Resources