I'm trying to paginate on a call to callback function but getting an error over second call
my function does:
let content = ''
let size = 100
let from = 1
function result(size, from, callback) {
api.performRequest('/myContents', 'GET', {
pageSize: size,
startFrom: from,
filter:'NOT contents:[* TO *]',
}, function (data) {
content += JSON.stringify(data)
callback()
})
}
function logContent() {
const parsed = JSON.parse(content)
console.log('content length: ', parsed.body.length)
if (parsed.body.length === size) {
calculate(size, from + size)
}
}
function calculate(size, from) {
result(size, from, logContent)
}
calculate(size, from)
on the first call the console returns
content length: 100
on the second call I get all my json chunk on the logs and an error
Unexpected token { in JSON at position 32847
I think it has to do with the callback and something happening before the function finishes, but I'm unable to see what I'm doing wrong here
the function performRequest just does an http get and returns a json chunk
export function performRequest(endpoint, method, data, callback) {
const headers = {
Authorization: auth,
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Expose-Headers': 'Location,ETag',
}
let options = {
host: host,
port: port,
path: endpoint,
headers: headers,
}
if (method === 'GET') {
endpoint += '?' + queryString.stringify(data)
const additionalOptions = {
path: endpoint,
}
options = _.merge(options, additionalOptions)
}
return http.get(options, function (response) {
// Continuously update stream with data
let body = ''
response.on('data', function (d) {
body += d
})
response.on('end', function () {
// Data reception is done
const parsed = JSON.parse(body)
callback({
totalCount: parsed.totalCount,
body: parsed.content,
})
})
})
}
Your first call is going to be fine, because your content is empty at first. So, during the first call:
content = '' --> content = '{...}'
And your second call :
content = '{...}' --> content = '{...}{...}'
Thus the error :
Unexpected token { in JSON at position 32847
You need to put every objects in an array, or even in another object if you want it to work. You can create an array and push every element into it at every call.
Related
I am working on a little project to learn how to work with APIs by making GET requests to the Twitter API v2 via a node server.
For the get requests I am using Node's built in https package.
I made a basic GET request that returns a list of the last 10 tweets from a user.
I think in order to increase the amount of tweets I can get I have to make a separate parameter object, which I then implement in the get request.
Right now my function looks like this:
function getTweets() {
const options = {
host: "api.twitter.com",
path: `/2/users/${userId}/tweets`,
headers: {
authorization:
`Bearer ${bearerToken}`,
},
};
https
.get(options, (response) => {
let data = "";
response.on("data", (chunk) => {
data += chunk;
});
response.on("end", () => {
let jsonObject = JSON.parse(data);
tweetObjects = jsonObject.data;
tweetObjects.map((item) => {
let tweetWords = "";
tweetWords += item.text;
userTweets.push(tweetWords);
});
const result = userTweets.flatMap((str) => str.split(" "));
console.log(result);
});
})
.on("error", (error) => {
console.log(error);
});
}
Right now I only have the options object with host, path, and headers in the request.
This is what I am trying to do:
function getTweets() {
const options = {
host: "api.twitter.com",
path: `/2/users/${userId}/tweets`,
headers: {
authorization:
`Bearer ${bearerToken}`,
},
};
let params = {
max_results: 100,
};
https
.get(params, options, (response) => {
let data = "";
response.on("data", (chunk) => {
data += chunk;
});
response.on("end", () => {
let jsonObject = JSON.parse(data);
tweetObjects = jsonObject.data;
tweetObjects.map((item) => {
let tweetWords = "";
tweetWords += item.text;
userTweets.push(tweetWords);
});
const result = userTweets.flatMap((str) => str.split(" "));
console.log(result);
});
})
.on("error", (error) => {
console.log(error);
});
}
But I get
throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener);
^
TypeError [ERR_INVALID_ARG_TYPE]: The "listener" argument must be of type function. Received an instance of Object
at checkListener (events.js:131:11)
at ClientRequest.once (events.js:496:3)
at new ClientRequest (_http_client.js:215:10)
at request (https.js:326:10)
at Object.get (https.js:330:15)
at IncomingMessage.emit (events.js:388:22)
at endReadableNT (internal/streams/readable.js:1336:12)
at processTicksAndRejections (internal/process/task_queues.js:82:21) {
code: 'ERR_INVALID_ARG_TYPE'
You can either only pass the URL or an object containing options as stated in the docs:
https://nodejs.org/api/https.html#https_https_get_url_options_callback
So you might want to try something like this:
function getTweets() {
let params = {
max_results: 100,
};
const options = {
host: "api.twitter.com",
path: `/2/users/${userId}/tweets?max_results=${params.max_results}`,
headers: {
authorization:
`Bearer ${bearerToken}`,
},
};
https
.get(options, (response) => {
let data = "";
...
}
I am not sure to understand what you are calling parameters that you want to give to the get request.
Anyway a get request does not take some body or parameter. If you want to add some data to the core of your request you have to use the post method and not the get one.
Can you show us the import of https library just to check the functions that it propose?
Best Regards,
Hugo Delatte
I am trying to use the fetch method in javascript to post data to the server.
Here is what I have done
var formData = {};// data
var url = ''; // url
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
}).then(async response => {
addClientFormAlert.innerHTML = '';
addClientFormAlert.classList.add('d-none');
if (response.status === 200) {
return response.json();
}
var data = await response.json();
Object.values(data.errors).forEach(function (items) {
if (items.errorMessage) {
addClientFormAlert.innerHTML += '<p>' + items.errorMessage + '</p>';
} else {
items.forEach(function (item) {
addClientFormAlert.innerHTML += '<p>' + item + '</p>';
});
}
});
addClientFormAlert.classList.remove('d-none');
}).then(client => function () {
// This should only be called when the request returns HTTP code 200
console.log('Request completed and here is the new client...', client);
});
But the second then never gets called even when the server return HTTP code 200.
How can I correctly use the fetch function to post data and capture HTTP code 200 and/or 400?
The issue was with the syntax.
The line }).then(client => function () { should be }).then(client => () { now that works
I have the following function:
export function request(
searchKey,
apiEndpoint,
path,
params,
{ additionalHeaders } = {}
) {
const method = "POST";
return _request(method, searchKey, apiEndpoint, path, params, {
additionalHeaders
}).then(response => {
return response
.json()
.then(json => {
var my_json = update(params)
const result = { response: response, json: json };
return result;
})
});
}
I want to export the variable my_json to another .js file. I already tried with export { my_json }, but it only works if I do that on the top of the document, which doesn't work in my case. Does anyone have an idea?
You can't export a variable which is inside a function but you can definitely get the value stored in my_json by the help of a callback function written in another javascript file.
Try using:
export function request(
searchKey,
apiEndpoint,
path,
params,
{ additionalHeaders } = {},
callback
) {
const method = "POST";
return _request(method, searchKey, apiEndpoint, path, params, {
additionalHeaders
}).then(response => {
return response
.json()
.then(json => {
var my_json = update(params);
callback(my_json);
const result = { response: response, json: json };
return result;
})
});
}
and in the other file define a function callback like:
function callback(data){
// assign this data to another variable so that one can use it
console.log(data)
}
and while calling the request function add one more argument as callback.
Hope this helps.
These results are for GET and POST requests response through API gateway and lambda. used same lambda function, but when i used post method of API gateway, response just shows me JSON. what should i have to do?
when i used post req
when i used get req
it's my lambda function
exports.handler = (event, context, callback) => {
const response = {
statusCode: 301,
headers: {
Location: 'https://google.com',
}
};
return callback(null, response);
}
thank you.
I usually use the new async/await pattern when defining my aws lambdas :
exports.handler = async (event) => {
// do stuff...
}
you usually don't need the context, unless you want to use some aws-related info regarding your lambda.
I have a helper function that is re-used a lot inside the code base
function proxyResponse(inBody, inStatusCode = null, headers = {}, event = null) {
if (!isApiGateway(event)) {
if (inBody instanceof Error) {
throw inBody;
}
return inBody;
}
let statusCode = inStatusCode;
let body;
if (inBody instanceof Error) {
statusCode = statusCode || INTERNAL_SERVER_ERROR;
body = JSON.stringify({
message: inBody.message,
code: statusCode,
});
} else if (inBody instanceof Object) {
body = JSON.stringify(inBody);
} else {
body = inBody;
}
const [origin] = event ? caseHandler(event.headers, 'origin') : [];
return {
statusCode: statusCode || 200,
body,
headers: Object.assign({
'Access-Control-Allow-Headers': 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token,x-api-key,Authorization',
'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Methods': 'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT',
}, headers),
};
}
and another one :
function caseHandler(mixedCaseObject, inputKey) {
if (!mixedCaseObject || !inputKey) {
return [];
}
return Object.keys(mixedCaseObject)
.filter((key => key.toLowerCase() === inputKey.toLowerCase()))
.map(key => mixedCaseObject[key]);
}
and this one :
function isApiGateway(event) {
return (!!event.httpMethod && !!event.headers && !!event.requestContext);
}
so inside the lambda whenever I want to return something I use this helper functions :
module.exports.handler = async (event) => {
try{
// do stuff...
return proxyResponse(YOUR_CUSTOM_BODY_OBJECT, Api.HTTP.OK, {}, event);
} catch(error) {
return proxyResponse(error, Api.HTTP.INTERNAL_SERVER_ERROR, {}, event);
}
}
The GET request will return any content provided in the response. In this case, it is the JSON object response. For POST requests the same response object is being returned back to the API Gateway which is then returning it back as-is to the client.
Perhaps the documentation here could be of help https://docs.aws.amazon.com/en_pv/apigateway/latest/developerguide/api-gateway-integration-settings-integration-response.html
I'm trying to send multiple HTTP requests from my node.js server (using the 'request' package) in a for loop.
The idea is to basically have an array of URLs, send a HTTP get for each one, and store the response in an array called 'responseArray'.
When I try this, I get 'undefined', but I know the requests are working if I log them to the console inside the for loop.
function apiCalls() {
var URLs = [‘URL1’, ‘URL2’, ‘URL3’];
var responseArray = []
for (var i = 0; i < URLs.length; i++) {
request({
url: URLs[i],
method: 'GET',
headers: {
'Connection': 'close'
},
}, function(error, response, body) {
if (error) {
console.log(error);
} else {
responseArray.push(String(response.body));
console.log(responseArray) // this is showing me all the data in the array correctly
return responseArray;
}
}) //end request function
} //end for loop
console.log(responseArray)
} //end apiCalls function
apiCalls()
So after looking at a few different solutions here on stack overflow and elsehwere I tried using promises. I've never used them before and based this off the google example
Promise:
var apiCalls = new Promise(function(resolve, reject) {
var URLs = [‘URL1’, ‘URL2’, ‘URL3’];
var responseArray = [];
for (var i = 0; i < URLs.length; i++) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
request({
url: URLs[i],
method: 'GET',
headers: {
'Connection': 'close'
},
}, function(error, response, body) {
if (error) {
console.log(error);
} else {
resolve(responseArray.push(String(response.body))
}
}) //end request
} //end for loop
})
apiCalls.then(function(result) {
console.log('this is calling the promise')
console.log(result)
}, function(err) {
console.log(err)
});
I always get an empty array when I try to log the responseArray after the for loop. Alternatively - I get 'undefined' if I try to assign the returned array to a variable like so:
var gimmeAllResponses = apiCalls();
console.log(gimmeAllResponses); //returns 'undefined'
Can anyone show me where I'm going wrong? how do I updated the 'responseArray' data after the for loop has finished?
This is a little bit off, since this requires the alternative package, request-promise.
You're resolving many times. Since you're using Node.js, it's very likely that ES6 features are available. Use Array.prototype.map() and Promise.all():
var rp = require('request-promise');
var URLs = [‘URL1’, ‘URL2’, ‘URL3’];
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var responseArray = Promise.all(URLs.map((url) => rp({
uri: url,
method: 'GET',
headers: {
'Connection': 'close'
}
}).then((error, response, body) => {
if (error) {
console.log(error);
} else {
return String(response.body);
}
})));