getting photos from one endpoint and saving in another one - nodejs - node.js

Basically what I want to do is get photos from another endpoint in spacex API. The photos are on endpoint rockets/rocket_id, and im trying to get them but always gets an empty values.
spaceX api is someone want to see it : https://docs.spacexdata.com/
api = require("./api")
// ...
server.get('/rockets', async (req, res) => {
try {
const { data } = await api.get('/v3/launches');
var lista = [];
for (var i = 0; i < data.length; i++) {
const img = api.get('/v3/rockets/' + data[i].rocket['rocket_id']);
lista.push({
name_rocket: data[i].rocket['rocket_name'],
first_stage: data[i].rocket['first_stage']['cores'][0]['core_serial'],
second_stage: data[i].rocket['second_stage']['payloads'][0]['payload_mass_kg'],
link_patch: data[i]['links']['mission_patch'],
link_video: data[i]['links']['video_link'],
details: data[i]['details'],
launch_site: data[i]['launch_site']['site_name'],
img: img
});
}
return res.send(lista);
} catch (error) {
res.send({ error: error.message });
}
});
server.get('/link', async (req, res) => {
try {
const { data } = await api.get('/v3/rockets/falcon9');
var lista = [];
lista.push({
imagem: data['flickr_images']
});
return res.send(lista);
} catch (error) {
res.send({ error: error.message });
}
});
api.js:
const axios = require("axios");
const api = axios.create({ baseURL: 'api.spacexdata.com', });
module.exports = api;

The issue is coming from the URL. You need "http://" or "https://".
axios.create({ baseURL: 'https://api.spacexdata.com'})
To debug this in the future, consider a try/catch and some logging. The console.log(err) below gave me a much more detailed error that I was able to google.
try{
const result = await api.get(...)
console.log(result)
catch (err) {
console.log(err)
}
Related: StackOverflow issue
In case these were also causing problems, it looks like you're missing an await and some object drilling here:
const img = api.get('/v3/rockets/' + data[i].rocket['rocket_id']);
Should be changed to this:
const { data } = await api.get('/v3/rockets/' + data[i].rocket['rocket_id']);
const imgs = data.flickr_images
You could probably use GET /rockets once instead of GET /rockets/:id in a loop in the first endpoint for performance.
v3 of this API is deprecated. Would suggest moving to v4.

Related

Async function to scrape subreddits using Cheerio returns undefined

The script by itself works great (entering the url manually, writing a json file using the fs module, node script_name.js) but within a Express get request it returns undefined.
So I've built a simple frontend to let the user enter the subreddit name to be scraped.
And here's where the problem is:
Express controller
const run = require("../run");
requestPosts: async (req, res) => {
try {
const { subreddit } = req.body;
const response = await run(subreddit);
//console.log(response);
res.json(response);
} catch (error) {
console.error(error);
}
},
Cheerio functions
const axios = require("axios");
const { load } = require("cheerio");
let posts = [];
async function getImage(postLink) {
const { data } = await axios(postLink);
const $ = load(data);
return $("a.post-link").attr("href");
}
async function run(url) {
try {
console.log(url);
const { data } = await axios(url);
const $ = load(data);
$(".thing.linkflair.link").map(async (i, e) => {
const title = $(e)
.find(".entry.unvoted .top-matter .title .title")
.text();
const user = $(e)
.find(".entry.unvoted .top-matter .tagline .author")
.text();
const profileLink = `https://old.reddit.com/user/${user}`;
const postLink = `https://old.reddit.com/${$(e).find("a").attr("href")}`;
// const thumbail = $(e).find("a img").attr("src");
const image = await getImage(postLink);
posts.push({
id: i + 1,
title,
postLink,
image,
user: { user, profileLink },
});
});
const nextPage = $(".next-button a").attr("href");
if (nextPage) {
await run(nextPage);
} else {
return posts;
}
} catch (error) {
console.error(error);
}
}
module.exports = run;
I've tried working with Promise((resolve, reject) => {}).
I think it's returning undefined because maybe the code its not synchronized.
(idk if it makes sense, i've just started programming)
.map() is not promise-aware and does not wait for your promises to finish. So, $(".thing.linkflair.link").map() finishes long before any of the asynchronous functions inside its callback do. Thus you try to return posts BEFORE it has been populated.
Passing an async callback to .map() will return an array of promises. You can use Promise.all() on those promises to know when they are done and once you're doing that, you may as well just return each post object rather that using a higher level scoped/shared object, thus making the code more self contained.
I would suggest this code:
async function run(url) {
try {
console.log(url);
const { data } = await axios(url);
const $ = load(data);
const posts = await Promise.all($(".thing.linkflair.link").map(async (i, e) => {
const title = $(e)
.find(".entry.unvoted .top-matter .title .title")
.text();
const user = $(e)
.find(".entry.unvoted .top-matter .tagline .author")
.text();
const profileLink = `https://old.reddit.com/user/${user}`;
const postLink = `https://old.reddit.com/${$(e).find("a").attr("href")}`;
// const thumbail = $(e).find("a img").attr("src");
const image = await getImage(postLink);
// return a post object
return {
id: i + 1,
title,
postLink,
image,
user: { user, profileLink },
};
}));
const nextPage = $(".next-button a").attr("href");
if (nextPage) {
const newPosts = await run(nextPage);
// add these posts to the ones we already have
posts.push(...newPosts);
}
return posts;
} catch (error) {
console.error(error);
}
}

Https Request Response arriving after my Express API Response

I am writing an API endpoint which searches Google Book's API to add book titles to the notebooks in my MongoDB Database based on their Id's. I have it working properly, but sometimes my endpoint's response array returns missing the last item. My console logs seem to indicate my endpoint response is being sent before the Https request has finished fetching all the data. I need to solve the endpoint sometimes returning an incomplete array. I think I may need to include an async await somewhere but I'm not sure where. Any help is much appreciated!
Here is my code:
//GET a user's books with notes
const getNotebooks = async (req, res) => {
...
const books = await Book.find({ userId }).sort({createdAt: -1}) //gets all notebooks as array
let results = []
for (let i = 0; i < books.length; i++){
console.log(i + " - " + books[i].gId)
const item = {
id: books[i].userId,
gId: books[i].gId,
title: '',
}
const request = https.request(apiUrl + books[i].gId, async (response) => {
let data = '';
response.on('data', (chunk) => {
data = data + chunk.toString();
});
response.on('end', () => {
const body = JSON.parse(data);
item.title = body.volumeInfo.title
results.push(item)
if (i == books.length - 1){
res.status(200).json(results)
}
});
})
request.on('error', (error) => {
console.log('An error', error);
res.status(400).json({error: error})
});
request.end()
}}
You can use async-await with Promises only, since Node's core https module does not have any build-in promise support, you will have to convert it first into the promise format, then you can use async-await with it, i am not familiar with the standard https module but i will make an example with your code
const getNotebooks = async (req, res) => {
//...
const books = await Book.find({ userId }).sort({createdAt: -1}) //gets all notebooks as array
let results = []
for (let i = 0; i < books.length; i++){
console.log(i + " - " + books[i].gId)
const item = {
id: books[i].userId,
gId: books[i].gId,
title: '',
}
const options = apiUrl + books[i].gId
// now you can use await to this function which return a promise
await makeRequest(options, res, item, results, books )
}
}
// Making this function into a promise outside of the scope from your getNotebooks function.
function makeRequest(options, res, item, results, books) {
return new Promise((resolve, rejects) => {
const request = https.request(options, (response) => {
let data = '';
response.on('data', (chunk) => {
data = data + chunk.toString();
});
response.on('end', () => {
const body = JSON.parse(data);
resolve(body);
item.title = body.volumeInfo.title
results.push(item)
if (i == books.length - 1){
res.status(200).json(results)
}
});
});
request.on('error', (error) => {
console.log('An error', error);
rejects(error);
res.status(400).json({error: error})
});
request.end()
});
};
Alternatively you can also use a more modern way to request data, for example with fetch which is now available in node, or even Axios library, if you don't want any dependency, fetch should be the way to go, an example if there is no mistake and assuming is a GET method request:
const getNotebooks = async (req, res) => {
//...
try {
const books = await Book.find({ userId }).sort({createdAt: -1}) //gets all notebooks as array
let results = []
for (let i = 0; i < books.length; i++) {
console.log(i + " - " + books[i].gId)
const item = {
id: books[i].userId,
gId: books[i].gId,
title: '',
}
// using fetch on node js is now possible without any package to install
const response = await fetch(apiUrl + books[i].gId)
const data = await response.json();
item.title = data.volumeInfo.title
results.push(item)
if (i == books.length - 1){
res.status(200).json(results)
}
}
} catch (error) {
res.status(400).json({error: error})
};
};
As you can see you would write 2 line code instead of 20+ line.

Express: res.send() returns an empty object

I have a setup that uses a MySQL server node and express, I have created an update statement on a SQL file and got it to work with the node via patch request on the postman.
It seems to work and it updates the server, however, when I try to get the recently updated object or even a success message, I get an empty object or nothing, even though the data has been updated.
Can anyone help me get some sort of response on postman?
update.sql
UPDATE [dbo].[RetailerThemes]
SET [Name] = #name,
[PrimaryColourPalette_main] = #primaryColourPalette_main
WHERE [UniqueThemedPageName] = #UniqueThemedPageName
router.js
router.patch("/update", internal, async (req, res) => {
try {
//do the update
const updateRetailer =
await retailerController.updateRetailerConfigByName(req, res)
console.log(`Update Retailer Routes: ${updateRetailer}`)
res.status(200).send(
{updateRetailer}
);
} catch (error) {
console.log(error);
}
});
controller.js
const updateRetailerConfigByName = async (req, res) => {
try {
// Credentials from Request
let retailername = req.retailername;
// Data from Repository
const thisRetailerConfig = await retailerRep.updateRetailerConfigDetails(
retailername
);
console.log( `thisRetailerConfig: ${thisRetailerConfig}`)
} catch (error) {
console.log(error)
}
};
repo.js
async function updateRetailerConfigDetails(retailername) {
try {
//RetailerTheme
const sqlcommanda = await tsql.loadSql(
"tsql",
//"GetRetailerThemeByThemedPageName.sql"
"UpdateRetailer.sql"
);
let pool = await sql.connect(sqlConfig);
const themes = await pool
.request()
.input("name", sql.VarChar(150), "b") // change 80700 to variable
.input("UniqueThemedPageName", sql.VarChar(150), retailername)
.input("primaryColourPalette_main", sql.VarChar(9), "#c00")
.query(sqlcommanda)
;
if (themes.rowsAffected != 1) {
console.log("Retailer not found for ", retailername, sqlcommanda);
return { isFound: false };
}
const theme = themes.recordset[0];
console.log(`The Theme: ${theme}`)
return theme;
} catch (error) {
console.log(error.message);
return {};
}
}
Here is a screenshot of what I get on the postman, the update works but the response is an empty object.

node.js Why data from post requests that is saved in a json file resets to initial empty array after restart server?

I am working with express node.js, and I am trying to save datas from post request in a json file. but for some reason when I restart the server, my data from post request was supposed to save in roomDB.json file doesnt remain instead it resets to initial empty array...
Could anyone please advice? thank you very much.
here is my code
//saving function
const fs = require("fs");
exports.save =(data, PATH) =>{
return new Promise((resolve, reject) => {
fs.writeFile(PATH, JSON.stringify(data), function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
// code in router file to make requests
const express = require("express");
const router = express.Router();
const fs = require("fs");
const rooms = ("./roomDB.json");
const { addRoom} = require("./rooms");
router.get("/", (req, res)=>{
fs.readFile("roomDB.json", (err, data)=>{
if(err) return res.status(400);
res.send(roomDB_PATH)
})
});
router.get("/:id", (req, res)=>{
res.send("connect to a room");
});
router.post("/", (req, res)=>{
let roomName = req.body;
if(!roomName.name){
res.status(404);
res.end();
return;
}
let room =addRoom(roomName.name);
res.status(201).send(room)
})
module.exports = router;
*/
const uuid = require("uuid");
let roomdatas;
const {save} = require("./save");
const roomDB_PATH = "roomDB.json";
try {
roomdatas = JSON.parse(fs.readFileSync(roomDB_PATH));
} catch (e) {
roomdatas = []
save(roomdatas, roomDB_PATH);
}
const addRoom = (roomName) => {
roomName = roomName.trim().toLowerCase();
const existingRoom = roomdatas.find((room) => room.name === roomName);
if (existingRoom) {
return { error: 'chatroom has existed' };
}
let room = {
name: roomName,
id: uuid.v4(),
messages: [],
users: [],
created: +new Date()
};
roomdatas.push(room);
save(roomdatas, roomDB_PATH);
return { room };
};
module.exports ={addRoom};
I'm assuming that you are encountering an error with the JSON.parse(fs.readFileSync(roomDB_PATH)); call. This code runs every time your server is started (when you import the file into your router file), and if it encounters an error it is resetting the file to an empty array. Try logging the error to see what is causing it. You're currently completely suppressing the error with no way to tell why it is failing.

passing json formatted with information from multiple json documents nodeJs

I have a function where I would like to return an arrays of JSON objects with the necessary information. to send directly to the front everything ready.
async listProcessByOffice(req, res, next) {
try {
const actualPlayer = await PlayerOffice.findById(req.userId);
const actualOffice = await Office.findById(actualPlayer.Office);
const listProcesses = await Processes.find({ '_id': { $in: actualOffice.processes } });
const infosTable = {
protocol: ''
};
for (let i in listProcesses) {
this.protocol = listProcesses[i].prc_protocol;
console.log(this.protocol)
}
return res.status(200).json({ infosTable });
} catch (err) {
return next(err);
}
Not sure what you are looking for but i am assuming that you want to response back with array list of objects. So simple answer will be,
const infosTable = [];
for (let i in listProcesses) {
this.protocol = listProcesses[i].prc_protocol;
infosTable.push({protocol:listProcesses[i].prc_protocol})
console.log(this.protocol)
}

Resources