Node.js and express.js how to read 'get' endpoint? - javascript

I am trying to pass simple data from my server to a javascript file called on another html page. I am testing sending a single string from the server, but am not sure how to receive it. Server below:
const express = require('express')
const app = express()
const port = 3000
app.use(express.static("./assets"));
app.get('/', (req, res) => {
//res.send('Hello World!')
res.sendFile('./main.html', { root: __dirname });
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
app.get('/get-test', async (_req, res) => {
try {
const test_string = "get-test request string";
return res.send(test_string);
} catch (e) { throw e; }
});
And in another javascript file I have the following:
async function testing() {
const response = await fetch('/get-test');
console.log(response);
}
testing();
The console.log gives me a whole object, and clicking through it I can't seem to find my test_string anywhere:
So I believe the get request worked, but how do I actually access the data inside that I want?

You need to call await response.text() before console.loging it.

So in this functio:
async function testing() {
const response = await fetch('/get-test');
console.log(response);
}
testing();
You will need to put await inside the console.log
so like this:
async function testing() {
const response = await fetch('/get-test');
console.log(await response);
}
testing();
Why ?
Since javascript is asynchronous when retrieving data there will be a delay. That is why they implemented async / await and promises. So when you trying to make a get request with fetch you will need need to await the response. But the response is also a promise, which mean you will need to await the response aswell. This makes more sense, when you try to process the response to lets say json.
A little tip
When the console.log returns no error, but no data either. It might because of a promise issue

Related

Res.send sends an empty object, even though console.log shows it isn't empty?

I am trying to use the google-sheets api with express and don't have much experience with javascript. I'm attempting to use pass a json object from express to react, but it seems that whenever I finally send the object, it just renders as empty on the frontend?
I've tried using res.body/res.data, but the object doesn't seem to have either. I've also tried to put as many awaits as I can everywhere to make sure the object is loaded in before sending, but nothing seems to do the trick. If I use res.json or res.send with just the response object, I get a circular structure converting to JSON error. Here is the code I'm working with.
async function docShit() {
// Initialize the sheet - doc ID is the long id in the sheets URL
const doc = new GoogleSpreadsheet(
"--SPREADSHEET ID--"
);
// Initialize Auth - see https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication
await doc.useServiceAccountAuth({
// env var values are copied from service account credentials generated by google
// see "Authentication" section in docs for more info
client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
private_key: process.env.GOOGLE_PRIVATE_KEY,
});
await doc.loadInfo(); // loads document properties and worksheets
const sheet = doc.sheetsByTitle[--WORKSHEET TITLE--];
const rows = await sheet.getRows(); // can pass in { limit, offset }
return rows;
}
app.get("/home", async (req, res) => {
try {
await docShit()
.then((response) => {
res.send(Promise.resolve(response)); //console log shows the object, but res.send just sends nothing??
})
.catch((err) => console.log(err));
} catch (err) {
console.error(err.message);
}
});
There is no res.send at all in your code. Also, you use await and .then together, but I consider them alternatives. Try the following:
app.get("/home", async (req, res, next) => {
try {
var response = await docShit();
console.log(response);
/* If response is circular, decide which parts of it you want to send.
The following is just an example. */
res.json(response.map(function(row) {
return {id: row.id, cells: row.cells.map(function(cell) {
return {id: cell.id, value: cell.value};
};
})};
} catch (err) {
console.error(err.message);
next(err);
}
});

Clearner way to wait for function to complete before continuing

First I fetch a random quote from a public api. Only AFTER I actually have a quote I want to render the page including that quote.
I am a JavaScript newbie so please bear with me if this is a stupid question.
I was struggling with waiting for the api call to return BEFORE continuing with rendering the page.
I wanted to do the following, but this doesn't work because res.render will be called before I have a quote. (note: I am using Express and Axios)
async function getRandomQuote() {
try {
const res = await axios.get("https://api.quotable.io/random?tags=famous-quotes")
console.log(`${res.data.content} - ${res.data.author}`) //This prints fine
return res.data
} catch(e) {
console.log('error', e)
}
}
app.get('/', (req, res) => {
const quote = getRandomQuote()
console.log(`${quote.content} - ${quote.author}`) //This prints 'undefined' because 'getRandomQuote' isn't finished yet
res.render('home', { quote })
})
The only way I figured out to do it is as follows, but I find this really messy.
Is there a cleaner way to do this? Or do I always need to put all the lines of code that I want to wait for each other in an async function?
async function getRandomQuote() {
try {
const res = await axios.get("https://api.quotable.io/random?tags=famous-quotes")
console.log(`${res.data.content} - ${res.data.author}`) //This prints fine
return res.data
} catch(e) {
console.log('error', e)
}
}
app.get('/', (req, res) => {
const getQuoteAndRender = async() => {
const quote = await getRandomQuote()
console.log(`${quote.content} - ${quote.author}`) //This prints only if I wrap everything in yet another aync function, otherwise it will not wait for 'getRandomQuote' to complete
res.render('home', { quote })
}
getQuoteAndRender()
})
(Note: I realize rendering the page after I successfully get a quote is not ideal either because this means I will not get a page at all if the quote api (for some reason) doesn't work. But for now I just want to know how to do this with the waiting.)
Heres what you need to do. Make your controller async by doing this app.get('/', async (req, res)
app.get('/', async (req, res) => {
const quote = await getRandomQuote()
console.log(`${quote.content} - ${quote.author}`) //This prints 'undefined' because 'getRandomQuote' isn't finished yet
res.render('home', { quote })
})
Try this:
getRandomQuote()
.then(quote => {
console.log(`${quote.content} - ${quote.author}`)
res.render('home', { quote })
});

Why is my catch statement triggering even though the .then is resolved?

This is the code I have currently in my server folder. I was just wondering for some reason the catch console log is still sending even though the .then statement actively sends a response though res.send works and sends a response to the user?
const express = require("express");
const router = express.Router();
const { API_KEY, API_URL } = process.env
const axios = require("axios")
router.get("/", async (req, res) => {
console.log(API_URL + API_KEY)
await axios.get(API_URL + API_KEY + "&count=9")
.then((response) => {
res.send(response.data)
})
.catch(
console.log("error")
)
})
module.exports = router
Thank you!
.catch accepts a callback - right now, you're invoking console.log immediately and passing the result to .catch. This:
// ...
.catch(console.log("error"))
is equivalent to
const consoleResult = console.log("error");
// ...
.catch(consoleResult)
You need to change to
.catch(
() => {
console.log("error")
}
)
Or, even better, examine the argument to see what the error was:
.catch(
(error) => {
console.log("error:", error.message)
}
)

How do I call Airtable rest API from inside an AWS Lambda?

I am trying to call my rest api endpoint in AIRTABLE from inside an AWS Lambda with no success. I get no errors, no outputs.
If I call the same code using node - it works.
I am able to use Axios in my code.
Pure airtable code (works)
var Airtable = require('airtable');
var base = new Airtable({apiKey: 'keyoMYSECRETKEY'}).base('Mybaseid');
base('MyBase').select({maxRecords: 3,view: "MyView"}).eachPage(function page(records, fetchNextPage) {
// This function (`page`) will get called for each page of records.
records.forEach(function(record) {
console.log('Retrieved',JSON.stringify(record.get('Session Information')));
});
fetchNextPage();
}, function done(err) {
if (err) { console.error(err); return; }
});
If I put it inside a Lambda handler - I get nothing.
const axios = require('axios')
const url = 'https://checkip.amazonaws.com/';
var Airtable = require('airtable');
var base = new Airtable({apiKey: 'keySECRETKEY'}).base('MYBASEID');
let response;
exports.lambdaHandler = async (event, context) => {
try {
base('MyBase').select({maxRecords: 3,view: "MyView"}).eachPage(function page(records, fetchNextPage) {
records.forEach(function(record) { //HERE - NOTHING HAPPENS
console.log('Retrieved',JSON.stringify(record.get('Session Information')));
});
fetchNextPage();
}, function done(err) {
if (err) { console.error(err); return; }
});
const ret = await axios(url); //THIS WORKS
response = {
'statusCode': 200,
'body': JSON.stringify({
message: 'hello world - boo',
location: ret.data.trim()
})
}
} catch (err) {
console.log(err);
return err;
}
return response
};
What am I missing so I can call Airtable API from inside an AWS Lambda?
It seems that your lambda terminates before the API call execution your trying to perform.
I believe this will be solved using a synchronous lambda or with a correct usage of promises with await calls.
Best way to troubleshoot this is to go back to the basics.
See if you can at least get a meaningful console log by wrapping a simpler fetch request into a lambda handler:
const baseId = 'exampleAppId123';
const tableName = 'Table 1';
const api_key = 'keyExample123';
const url = `https://api.airtable.com/v0/${baseId}/${tableName}?api_key=${api_key}`;
exports.lambdaHandler = async () => {
const res = await fetch(url)
.then(res => res.json())
.then(data=>console.log(data))
.then(() => {
//do more stuff
})
}
Then report back if you can't. Or better yet, report back either way as that's bound to help more people in the future.
Worst case? The above code still doesn't do anything. If that happens, I suggest going with #Shoty's first instinct and turning this code into a synchronous fetch request by removing the async/await syntax and returning chained thenables. Not that blocking behavior of this sort is acceptable from a UX perspective, but it should at least help with debugging.

SyntaxError: Unexpected end of JSON input - why is that?

This is the code I have written using express and node.js
const express = require("express");
const https = require("https");
const app = express();
app.get("/", function(req, res) {
// Url to my api key
const url = "https://api.spoonacular.com/recipes/random?apiKey=...&number=1";
https.get(url, function(response) {
response.on("data", function (data) {
console.log(JSON.parse(data));
// const theRecipe = JSON.parse(data);
console.log(data);
});
});
res.send("The server is up and running");
});
app.listen(3000, function () {
console.log("Server started at port 3000");
});
When I refresh my webpage on localhost, on console I get the following error:
quote
SyntaxError: Unexpected end of JSON input
at JSON.parse ()
at IncomingMessage. (C:\Users\ArunBohra\Desktop\FoodRecipes\app.js:12:33)
quote
Can anyone find what is the problem with my code.
The data event fires when a chunk of data from the response has arrived. You are trying to parse the first chunk as if it were the complete JSON text.
You need to collect the pieces from each data event but wait until the end event fires before joining them together into the complete string of JSON that you can parse.
There is an example of fetching and parsing JSON in the documentation.
You might want to look at modules like axios and node-fetch which take care of that (and the JSON parsing) for you while providing modern Promise based APIs.
If you have a new enough version of Node, you can use the native Fetch API.
If you use a package like node-fetch you can get the whole thing in one go instead of what you have now which is chunking the data
const fetch = require('node-fetch');
const url = "https://api.spoonacular.com/recipes/random?apiKey=...&number=1";
fetch(url)
.then(response => response.json())
.then(data => console.log(data));
In addition to other answers, you can do it without another package.
https.get(url, function (response) {
let result = "";
response.on("data", function (data) {
result += chunk;
});
res.on('end', () => {
// now you have the combined result
console.log(result);
});
});

Categories