I can't download a file with nodejs backend and react frontend - javascript

I am trying to download a requested file from the frontend to a backend server with nodejs. To do it in react I use axios and in nodejs I am using express. The problem is that with my implementation I get the following error:
Error [ERR_STREAM_CANNOT_PIPE]: Cannot pipe, not readable
at new NodeError (node:internal/errors:371:5)
at ServerResponse.pipe (node:_http_outgoing:987:22)
at file:///home/marc/Disco/Projects/GymApp/server/routes/trainer.js:249:9
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Emitted 'error' event on ServerResponse instance at:
at ServerResponse.pipe (node:_http_outgoing:987:8)
at file:///home/marc/Disco/Projects/GymApp/server/routes/trainer.js:249:9
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
code: 'ERR_STREAM_CANNOT_PIPE'
Here is the axios request:
const downloadTraining = async (id) => {
const JWT = new ClassJWT();
const axiosReq = axios.create();
await JWT.checkJWT();
axiosReq
.post(`${serverPath}/download-training`, {
trainer_id: await JWT.getId(),
training_id: id,
token: JWT.getToken(),
})
.then((res) => {
console.log(res);
})
.catch((err) => console.log(err));
}
Here I don't know what to do for the browser to download the file, if anyone know it, let me know.
Here is the nodejs code with express that gives me the error that I mentioned above:
import express from "express";
import { verifyJWT } from "../services/tokens/verifyJWT.js";
import Training from "../models/Training.js";
import fs from "fs";
const path = "/home/marc/Disco/Projects/AtlasFitness/";
const dirname = "uploads/trainings/";
routerTrainer.post("/download-training", verifyJWT, async (req, res) => {
const { trainer_id, training_id } = req.body;
let training = await Training.findOne({
where: { id: training_id, trainer_id },
});
if (training) {
const filePath = fs.createWriteStream(`${path}${dirname}${training.file_id}`);
res.pipe(filePath);
filePath.on('finish',() => {
filePath.close();
console.log('Download Completed');
})
}
})
Here is when I get the error, if anyone know how to fix it to get the file downloaded to the user's computer, please let me know it.

It should be a read stream on the API, not a write stream.
const filePath = fs.createReadStream(`${path}${dirname}${training.file_id}`);
res.pipe(filePath);
filePath.on('finish',() => {
filePath.close();
console.log('Download Completed');
})

Related

multiple url variable async/await axios

Probably this is a very stupid question, i'm new in Node.js and javascript, so please forgive me if the question is not properly explained or the answer is simple...
I'm trying to send 2 variables thru a url... When i send only 1 variable (artist=${term}) work all good, but I'm really stuck with about how to send 2 variables thru the url (&artist=${term1}&album=${term2})
I've work on this code so far which for 1 variable is working well... but i have no idea how to add a second or a third variable to the request:
File 1: "./services/albumInfo.js"
import { BRV_API } from '../../config';
import axios from 'axios';
import dotenv from 'dotenv';
const ALBUM_INFO = 'method=album.getinfo';
dotenv.config();
const doRequest = async (url) => {
return await axios.get(`${BRV_API}/${url}&api_key=${process.env.API_KEY}&format=json`);
};
export const infoAlbum = async (term) => {
return await doRequest(`?${ALBUM_INFO}&artist=${term}`);
};
File 2: "./repositories/albumInfo.js"
import { infoAlbum } from '../repositories/albumInfo';
import status from 'http-status';
export const albumInfo = async (req, res, next) => {
try {
const { query } = req;
const { data } = await infoAlbum(query.name);
const response = data;
res.status(status.OK).send(response);
} catch (error) {
next(error);
}
};
I know that my problem is in this part of the code (I guess)
export const infoAlbum = async (term) => {
return await doRequest(`?${ALBUM_INFO}&artist=${term1}&album=${term2}`);
};
I've been searching, and i've seen some solution, like this one, but i just don't understand those solutions or how to apply on my code (sorry for that, im a very new on this)
Any good soul who can help this newbie? (if can explain the why of the solution as well, for understand, will be amazing!!)
Thanks in advance!!
Axios provides parameters that can be added custom as the following
const your_url = process.env.URL
const infoAlbum = await axios.get(your_url,{
params: {
artist: term,
album: term2,
api_key: process.env.API_KEY,
format:'json'
}
})
console.log(infoAlbum.data.args)
note: your_url without any more parameters.
So,
I've found a solution, which is pretty ugly, but so far is working, if someone have a better option, will be amazing to know:
File 1: repositories/albumInfo.js, I've just add the console (as per #Alex028502 suggestion), to know what the code was returning:
import { BRV_API } from '../../config';
import axios from 'axios';
import dotenv from 'dotenv';
const ALBUM_INFO = 'method=album.getinfo';
dotenv.config();
const doRequest = async (url) => {
const fullurl = `${BRV_API}/?${ALBUM_INFO}${url}&api_key=${process.env.API_KEY}&format=json`;
console.log('full url is', fullurl);
return await axios.get(fullurl);
};
export const infoAlbum = async (term) => {
return await doRequest(`&${term}`);
};
File 1: services/albumInfo.js: I change the behaviour of 'infoAlbum' to make the request from his side:
import { infoAlbum } from '../repositories/albumInfo';
import status from 'http-status';
export const albumInfo = async (req, res, next) => {
try {
const { query } = req;
console.log(query);
const { data } = await infoAlbum('artist=' + query.artist + '&album=' + query.album);
const response = data;
res.status(status.OK).send(response);
} catch (error) {
next(error);
}
};
I know that probably this is not the very best way to walk away from the problem, but so far is what i have.... any other better option about how to capture the second or third parameter of the url request and then add them to the final url?
Best!

TypeError: ping is not a function

Im trying to make a discord bot that shows my minecraft server stats and stuff. It is almost done but when i do the command it comes up with this in the terminal: TypeError: ping is not a function. Here is my code:
const {Client, RichEmbed } = require('discord.js')
const bot = new Client()
const ping = require('minecraft-server-util')
const token = 'not gunna tell u my token'
const ip = 'or ip'
const PREFIX = '!'
bot.on('ready', () =>{
console.log('Bot has come online.')
})
bot.on('message', message =>{
let args = message.content.substring(PREFIX.length).split(' ')
switch(args[0]){
case 'mc':
ping(ip, parseInt(25565), (error, reponse) =>{
if(error) throw error
const Embed = new RichEmbed()
.setTitle('Server Status')
.addField('Server IP', reponse.host)
.addField('Server Version', reponse.version)
.addField('Online Players', reponse.onlinePlayers)
.addField('Max Players', reponse.maxPlayers)
message.channel.send(Embed)
})
break
}
})
bot.login(token)
As the docs say, ping is a property of that module's exports: it's not the whole export:
const util = require('minecraft-server-util');
util.ping('play.hypixel.net', { port: 25565 })
For your code, either destructure out the ping property on import:
const { ping } = require('minecraft-server-util')
Or give it some other name, then call .ping on the imported object:
const util = require('minecraft-server-util');
// ...
util.ping(ip, /* etc */
Also, if you want the port to be the default 25565, there's no need to pass it at all. The module also returns a Promise, which you should use instead of the callback form:
ping(ip)
.then((response) => {
// handle response
})
.catch((error) => {
// handle errors
});

TypeError: resolver is not a function

I am attempting to create a basic post on click in my NextJS app to a MongoDB database. The issue i am getting is TypeError: resolver is not a function. I understand it might be a syncronicity issue but for the life of me I cannot figure out where.
Stack used: NextJS, Axios, Mongoose.
Component code snippet calling axios:
i know the states are updating so i am putting only the snippet that handles the request
handleSubmit = async (e: any) => {
e.preventDefault();
await axios
.post('/api/roomSession', {
roomName: this.state.roomName,
teamName: this.state.teamName
})
.then((response: any) => console.log('axios call reached', response))
.catch((error: any) => console.log('---- error! ----', error));
};
[...]
<button onClick={this.handleSubmit}>Create</button>
[...]
NextJS API file:
import { newSession } from '../../packages/backend/mongo/connection';
const apiNewSession = async (roomName, teamName) => {
await newSession(teamName, roomName);
};
export default apiNewSession();
Mongoose file:
const mongoose = require('mongoose');
mongoose
.connect(
'mongodbconnection',
{ useNewUrlParser: true, useUnifiedTopology: true }
)
.then(() => {
console.log('connected to mongoDB');
})
.catch(err => console.error('Connection failed: ', err));
const sessionSchema = new mongoose.Schema({
teamName: String,
roomName: String
});
const Session = mongoose.model.tests || mongoose.model('tests', sessionSchema);
export const newSession = async (teamName, roomName) => {
const session = new Session({
teamName,
roomName
});
const result = await session.save();
mongoose.connection.close();
};
Some extra info on the strange behaviour: When first time called, the code throws the aformentioned error but manages to reach the mongoDB connection and creates an EMPTY entry inside the collection (only _id, and _v).
Upon second attempt, only the error is thrown.
I was exporting the function incorrectly from the NextJS API file.
Correct method:
export default apiNewSession;
Not sure why it still happened when when i was exporting the function by default.
My problem was that I was defining a middleware function incorrectly...
This is okay
/* pages/api/endpoint.js */
export default MiddleWare(handler)
But middleware functions shouldn't be async...
async function MiddleWare(handler) {
// ^ remove this!
return async function (req, res) {
// some async stuff
next(req, res);
}
}

How to make call to external api from nodejs

Hi all I have to develop a utility which makes a call to external API with different parameters, for example, I have an array val which has 100 value val= ['we23','22ww', 'gh22'....n] and URL: www.google.com so one by one I have to append value from val to the URL, first api= www.google.com/we23, second api= www.google.com/22ww and make an External API hit and then store the response in database. so what is the most efficient way to do it? and links to working examples would be helpful.
A very simple example express app using the Fetch API:
const express = require('express')
const fetch = require('node-fetch')
const app = express()
// This sets up a route to localhost:3000/random and goes off and hits
// cat-fact.herokuapp.com/facts/random
app.get('/:apiRoute', async (req, res) => {
try {
const { apiRoute } = req.params
const apiResponse = await fetch(
'https://cat-fact.herokuapp.com/facts/' + apiRoute
)
const apiResponseJson = await apiResponse.json()
// await db.collection('collection').insertOne(apiResponseJson)
console.log(apiResponseJson)
res.send('Done – check console log')
} catch (err) {
console.log(err)
res.status(500).send('Something went wrong')
}
})
app.listen(3000, () => console.log(`Example app listening on port 3000!`))
Visit http://localhost:3000/random
With the following code you can make concurrent API calls within an endpoint using Node.js + Express:
const [
LoMasNuevo, LoMasVisto, TeRecomendamos, Categorias,
] = await Promise.all([
numerosController.getLoMasNuevo(),
numerosController.getLoMasVisto(),
numerosController.getRecomendaciones(),
categoriasController.getCategorias(),
]);
Inside every get function you can make an axios request like this:
const params = {
method: 'GET',
url: 'https://development.api.yodlee.com/ysl/transactions',
headers: {
'Api-Version': '1.1',
Authorization: `Bearer ${tokenuser}`,
},
};
const data = await axios(params);
return data;
In 2022
In Node.js:
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) =>
fetch(...args));
app.get('/checkDobleAPI', async (req, res) => {
try {
const apiResponse = await fetch(
'https://jsonplaceholder.typicode.com/posts'
)
const apiResponseJson = await apiResponse.json()
console.log(apiResponseJson)
res.send('Running 🏃')
} catch (err) {
console.log(err)
res.status(500).send('Something went wrong')
}
})
You can use Express to build a API as your idea
Then you can call api by using axios package.
In addition, you can build link to receive request and send response by using Router of ExpressJS

Polly.js + selenium-webdriver: How to stub/intercept XHR requests

I'm running selenium tests for a react app, and I'm trying to stub my axios (XHR) requests. Ithink I'm close but seems like there's something missing. I'm running polly l - listening on localhost:3000 - and then my test looks like this:
import { Polly } from '#pollyjs/core'
import XHRAdapter from '#pollyjs/adapter-xhr'
import LocalStoragePersister from '#pollyjs/persister-local-storage';
import webdriver from 'selenium-webdriver'
const { By, Key, until } = webdriver
Polly.register(XHRAdapter)
Polly.register(LocalStoragePersister);
describe('Loggin in', () => {
const polly = new Polly('Sign In', {
adapters: ['xhr'],
persister: 'local-storage'
});
const { server } = polly;
polly.configure({
persisterOptions: {
'local-storage': {
key: '__pollyjs__'
}
}
});
server.get("http://localhost:3000/dashboard").passthrough()
server.get('http://localhost:3000/api/users/me').intercept((req, res) => {
res.status(200);
res.json({});
});
server.put('http://localhost:3000/api/users/login').intercept((req, res) => {
res.status(200);
res.json({});
});
it('renders correctly', async() => {
var chromeCapabilities = webdriver.Capabilities.chrome();
var chromeOptions = {
//'args': ['--headless']
'args': ['--auto-open-devtools-for-tabs']
};
chromeCapabilities.set('chromeOptions', chromeOptions);
const driver = new webdriver.Builder().withCapabilities(chromeCapabilities).build();
jasmine.Ajax.stubRequest('/dashboard').andReturn({
"status": 200
});
await driver.get('http://localhost:3000/dashboard')
await driver.getCurrentUrl().then((url) => {
expect(url).toEqual("http://localhost:3000/dashboard")
})
await polly.stop();
driver.quit()
})
})
So the idea here is that "/dashboard" should be passed through and not be intercepted at all, while the api requests (/api/users/meand /api/users/login) should be stubbed by polly. What happens is that when landing on /dashboard I get a 404 back and polly seem to not pass it through at all. Have I got it completely wrong here?
Cannot GET /dashboard

Categories