Writing a webapp that uses async/await but got stuck where the line var r1 = await fetch(url).then((r) => r.text()) appears to be handing forever. My web server listening on port 80 didn't even receive the request.
const fetch = require ('fetch-node')
const express = require('express');
const app = express();
var savedResolve;
app.listen(8079, '127.0.0.1', function() {
console.log('listening on 8079')
})
app.get('/*', async function (req, res) {
console.log(req.path)
res.setHeader('Content-Type', 'text/html');
await task()
res.send('Done')
})
async function task() {
console.log("starting..")
var url = "http://localhost/prod/te.php";
var r1 = await fetch(url).then((r) => r.text())
console.log(r1)
return "done"
}
Any ideas? Thanks in advance!
Update1
Thanks to suggestion by #deryck, add try and catch around the line of fetch call, got the following error instead
TypeError: Cannot read property 'render' of undefined
at module.exports (/Users/jsmith/learn/node/node_modules/hooks-node/hooks-node.js:8:11)
at module.exports (/Users/jsmith/learn/node/node_modules/fetch-node/fetch-node.js:17:1)
at task (/Users/jsmith/learn/node/te4b.js:22:18)
at /Users/jsmith/learn/node/te4b.js:13:8
at Layer.handle [as handle_request] (/Users/jsmith/learn/node/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/jsmith/learn/node/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/jsmith/learn/node/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/jsmith/learn/node/node_modules/express/lib/router/layer.js:95:5)
at /Users/jsmith/learn/node/node_modules/express/lib/router/index.js:281:22
at param (/Users/jsmith/learn/node/node_modules/express/lib/router/index.js:354:14)
node-fetch and fetch-node are two differnt libraries.
And based on the shown code node-fetch is the library that should be used.
Related
I am trying to pass the client value to another file.
I am using express to create a web server that works with my discord bot, and want to do something on a post request to an endpoint.
When the endpoint receives a post request with a user id, the bot should send them a message.
// index.js
const client = new Client(etc etc);
const routes = require('./routes.js');
https.createServer(options, app).listen(PORT, function() {
console.log('Express server listening on port ' + PORT);
});
app.use(bodyParser.json(), routes, limiter);
client.login(token).then(() => {console.info('Logged in.');});
module.exports = { client };
In the index.js file, the server is created, along with various other command handling things that are unrelated.
// routes.js
const express = require('express');
const router = express.Router();
const { invite, topggAuth } = require('./config.json');
const client = require('./index.js');
const vote = require('./events/vote.js');
router.route('/api/tggwh').post((req, res) => {
if (req.header('authorization') === topggAuth) {
vote.execute(client, req.body);
res.status(200).end();
}
else {
console.log('Unauthorized vote request attempt.');
res.send('Unauthorized');
res.status(401).end();
}
});
Routing seems to work fine, it matches the auth header to the key in the config file, then moves on to the vote handler.
// vote.js
const { logUsage } = require('../functions.js');
module.exports = {
name: 'vote', // name, duh
async execute(client, data) { // stuff to do
try {
const targetUser = await client.users.cache.get(data.user);
await targetUser.send('Thank you for voting!');
logUsage({ name: 'vote' });
}
catch (error) {
console.error('Whoops! Something went wrong while thanking a vote.');
console.error(error);
}
},
};
This is where the error comes in.
TypeError: Cannot read properties of undefined (reading 'cache')
at Object.execute (/home/ubuntu/Harold/events/vote.js:7:42)
at /home/ubuntu/Harold/routes.js:43:8
at Layer.handle [as handle_request] (/home/ubuntu/Harold/node_modules/express/lib/router/layer.js:95:5)
at next (/home/ubuntu/Harold/node_modules/express/lib/router/route.js:144:13)
at Route.dispatch (/home/ubuntu/Harold/node_modules/express/lib/router/route.js:114:3)
at Layer.handle [as handle_request] (/home/ubuntu/Harold/node_modules/express/lib/router/layer.js:95:5)
at /home/ubuntu/Harold/node_modules/express/lib/router/index.js:284:15
at Function.process_params (/home/ubuntu/Harold/node_modules/express/lib/router/index.js:346:12)
at next (/home/ubuntu/Harold/node_modules/express/lib/router/index.js:280:10)
at Function.handle (/home/ubuntu/Harold/node_modules/express/lib/router/index.js:175:3)
I think this means that client is passed through, but client.users is not.
How can I pass client through to vote.js after its been called by the post request?
I'm learning express and having an issue sending json from my express server to my react app.
On my express server I do an API call to openweathermap API and then send the JSON to react where I pick it up using axios. The problem is, my react app will get the JSON but the data field will be blank, I tried manually sending a JSON using res.json({name:"blank"}) and that sent but the result from my API call won't.
The first code snippet is my Express server and the second snippet is my React app. The last snippet is the error I am getting.
const express = require('express');
const path = require('path');
const app = express();
const fetch = require('node-fetch');
app.get('/test', (req, res) =>
res.send('Welcome to backend this is from node')
);
const port = process.env.PORT || 3001;
app.listen(port);
console.log('App is listening on port ', port);
const apiCall = async () => {
try {
const KEY = fd3909829b4fbfcfcca7c595a56c7632;
const api_res = await fetch(
'api.openweathermap.org/data/2.5/weather?q=toronto&appid=${KEY}'
);
response = await api_res.json();
console.log(response);
return response;
} catch (error) {
console.log('error: ', error);
}
};
app.get('/weather', async (req, res) => {
const data = await apiCall();
res.json(data);
});
import React from 'react';
import './App.css';
import axios from 'axios';
import Weather from './components/weather';
const hitBackend = () => {
axios.get('/weather').then((res) => {
console.log(res);
});
};
function App() {
return (
<div className='App'>
<Weather />
<button onClick={hitBackend}>Send Request!</button>
</div>
);
}
export default App;
error: ReferenceError: fd3909829b4fbfcfcca7c595a56c7632 is not defined
[server] at apiCall (C:\Users\Jalal\Desktop\Coding\React\weather\server\index.js:21:15)
[server] at C:\Users\Jalal\Desktop\Coding\React\weather\server\index.js:34:21
[server] at Layer.handle [as handle_request] (C:\Users\Jalal\Desktop\Coding\React\weather\node_modules\express\lib\router\layer.js:95:5)
[server] at next (C:\Users\Jalal\Desktop\Coding\React\weather\node_modules\express\lib\router\route.js:137:13)
[server] at Route.dispatch (C:\Users\Jalal\Desktop\Coding\React\weather\node_modules\express\lib\router\route.js:112:3)
[server] at Layer.handle [as handle_request] (C:\Users\Jalal\Desktop\Coding\React\weather\node_modules\express\lib\router\layer.js:95:5)
[server] at C:\Users\Jalal\Desktop\Coding\React\weather\node_modules\express\lib\router\index.js:281:22
[server] at Function.process_params (C:\Users\Jalal\Desktop\Coding\React\weather\node_modules\express\lib\router\index.js:335:12)
[server] at next (C:\Users\Jalal\Desktop\Coding\React\weather\node_modules\express\lib\router\index.js:275:10)
[server] at expressInit (C:\Users\Jalal\Desktop\Coding\React\weather\node_modules\express\lib\middleware\init.js:40:5)
You need to await your apiCall since it's asynchronous.
app.get('/weather', async (req, res, next) => {
const data = await apiCall();
res.send(data);
});
Your API key variable isn't being set properly
const KEY = fd3909829b4fbfcfcca7c595a56c7632;
Should be
const KEY = "fd3909829b4fbfcfcca7c595a56c7632";
Next you are not handling errors correctly.
Because you are catching the error(s) in the callApi method,
when you are sending the response back to react, you have no way of knowing if the apiCall function succeeded or not.
Also in order to use the ${} notation in a string, you need to use `` instead of " ".
so
'api.openweathermap.org/data/2.5/weather?q=toronto&appid=${KEY}'
Becomes
`https://www.api.openweathermap.org/data/2.5/weather?q=toronto&appid=${KEY}`
This is how I would code it to properly catch errors and also let react know if the request failed.
app.get('/weather', async (req, res) => {
try {
const KEY = "fd3909829b4fbfcfcca7c595a56c7632";
const api_res = await fetch(
`https://www.api.openweathermap.org/data/2.5/weather?q=toronto&appid=${KEY}`
);
response = await api_res.json();
console.log(response);
return res.json(response);;
} catch (error) {
console.log('error: ', error);
return res.status(400).send('error: ' + error.toString());
}
});
The main problem on your express app is that you are not awaiting the apiCall method on your route. So, the function is executing but not awaiting the async code that you had over there.
So, you will need to await that, like this:
app.get("/weather", async (req, res, next) => {
const weather = await apiCall()
res.send(weather);
});
Also, I see that you are using fetch for getting the API response from weather, but not requiring any module. Fetch is a browser API. You can install node-fetch or use axios for this purpose.
I'm trying to use firebase for the first time with a web-applicaiton, but I get this error about .child is not a function. Can anyone please show the right way to connect to the firebase database collection, if what i am doing is wrong.
If I prefer to use mongoDb instead of firebase, how should I implement my post method in this case?
This is the error I get upon posting the form on the site.
TypeError: fbRef.child is not a function
at C:\Users\WorkoutApp_v1\routes\workouts.js:32:23
at Layer.handle [as handle_request] (C:\Users\WorkoutApp_v1\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\WorkoutApp_v1\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\Users\WorkoutApp_v1\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\WorkoutApp_v1\node_modules\express\lib\router\layer.js:95:5)
at C:\Users\WorkoutApp_v1\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\Users\WorkoutApp_v1\node_modules\express\lib\router\index.js:335:12)
at next (C:\Users\WorkoutApp_v1\node_modules\express\lib\router\index.js:275:10)
at Function.handle (C:\Users\WorkoutApp_v1\node_modules\express\lib\router\index.js:174:3)
at router (C:\Users\WorkoutApp_v1\node_modules\express\lib\router\index.js:47:12)
This is the post method in my router file.
var express = require('express');
var router = express.Router();
const Firebase = require('firebase');
const fbRef = 'https://console.firebase.google.com/project/workout-7f912/database/firestore /data~2Fexercise~2F2zCvky1fWQcD14tTCq8B';
router.post('/add', function(req,res,next) {
var workout = {
name: req.body.Name,
discription: req.body.Discription,
set: req.body.Set,
RepsTime: req.body.RepsTime
}
// console.log(workout);
// return;
var dbRef = fbRef.child('workouts');
dbRef.push().set(workout);
req.flash('success_msg', 'Workout saved');
res.redirect('/workouts');
});
fbRef is a string and does not contain a method called child. You need to first connect to firebase database then you can do the following:
const fbRef = firebase.database().ref();
var dbRef = fbRef.child('workouts');
For more information check the following:
https://firebase.google.com/docs/database/web/start
This seems like a straightforward google, but I can't seem to find the answer...
Can you pass in ES6 ES7 async functions to the Express router?
Example:
var express = require('express');
var app = express();
app.get('/', async function(req, res){
// some await stuff
res.send('hello world');
});
If not, can you point me in the right direction on how to handle this problem ES7 style? Or do I just have to use promises?
Thanks!
May be you didn't found results because async/await is an ES7 not ES6 feature, it is available in node >= 7.6.
Your code will work in node.
I have tested the following code
var express = require('express');
var app = express();
async function wait (ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms)
});
}
app.get('/', async function(req, res){
console.log('before wait', new Date());
await wait(5 * 1000);
console.log('after wait', new Date())
res.send('hello world');
});
app.listen(3000, err => console.log(err ? "Error listening" : "Listening"))
And voila
MacJamal:messialltimegoals dev$ node test.js
Listening undefined
before wait 2017-06-28T22:32:34.829Z
after wait 2017-06-28T22:32:39.852Z
^C
Basicaly you got it, you have to async a function in order to await on a promise inside its code.
This is not supported in node LTS v6, so may be use babel to transpile code.
Hope this helps.
Update
Since ExpressJs 5, async functions are supported, and throw errors as expected
Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error
source
In Express 4 or less, it sort of works, but not really
While it seems to work, it stops handling errors thrown inside the async function, and as a result, if an error is not handled, the server never responds and the client keeps waiting until it timeout.
The correct behavior should be to respond with a 500 status code.
Solutions
express-promise-router
const router = require('express-promise-router')();
// Use it like a normal router, it will handle async functions
express-asyncify
const asyncify = require('express-asyncify')
To fix routes set in the app object
Replace var app = express(); with
var app = asyncify(express());
To fix routes set in router objects
Replace var router = express.Router(); with
var router = asyncify(express.Router());
Note
You only need to apply the asyncify function in the objects where you set the routes directly
https://www.npmjs.com/package/express-asyncify
I think you can't do it directly because exceptions are not caught and the function won't return if one is thrown. This article explains how to create a wrapper function to make it work: http://thecodebarbarian.com/using-async-await-with-mocha-express-and-mongoose.html
I haven't tried it but was investigating this recently.
Use express-promise-router.
const express = require('express');
const Router = require('express-promise-router');
const router = new Router();
const mysql = require('mysql2');
const pool = mysql.createPool({
host: 'localhost',
user: 'myusername',
password: 'mypassword',
database: 'mydb',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
}).promise();
router.get('/some_path', async function(req, res, next) {
const [rows, ] = await pool.execute(
'SELECT * ' +
'FROM mytable ',
[]
);
res.json(rows);
});
module.exports = router;
(The above is an example of using mysql2's promise interface with express-promise-router.)
Express 5 will automatically handle async errors for you
https://expressjs.com/en/guide/error-handling.html currently says it clearly:
Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error. For example:
app.get('/user/:id', async function (req, res, next) {
var user = await getUserById(req.params.id)
res.send(user)
})
If getUserById throws an error or rejects, next will be called with either the thrown error or the rejected value. If no rejected value is provided, next will be called with a default Error object provided by the Express router.
We can test that as follows:
const assert = require('assert')
const http = require('http')
const express = require('express')
const app = express()
app.get('/error', async (req, res) => {
throw 'my error'
})
const server = app.listen(3000, () => {
// Test it.
function test(path, method, status, body) {
const options = {
hostname: 'localhost',
port: server.address().port,
path: path,
method: method,
}
http.request(options, res => {
console.error(res.statusCode);
assert(res.statusCode === status);
}).end()
}
test('/error', 'GET', 500)
})
The terminal output on express#5.0.0-alpha.8 is the expected:
500
Error: my error
at /home/ciro/test/express5/main.js:10:9
at Layer.handle [as handle_request] (/home/ciro/test/node_modules/router/lib/layer.js:102:15)
at next (/home/ciro/test/node_modules/router/lib/route.js:144:13)
at Route.dispatch (/home/ciro/test/node_modules/router/lib/route.js:109:3)
at handle (/home/ciro/test/node_modules/router/index.js:515:11)
at Layer.handle [as handle_request] (/home/ciro/test/node_modules/router/lib/layer.js:102:15)
at /home/ciro/test/node_modules/router/index.js:291:22
at Function.process_params (/home/ciro/test/node_modules/router/index.js:349:12)
at next (/home/ciro/test/node_modules/router/index.js:285:10)
at Function.handle (/home/ciro/test/node_modules/router/index.js:184:3)
If you visit it on the browser, you will see an HTML page that says my error.
If you run the exact same code on express#4.17.1, you see on the terminal only:
UnhandledPromiseRejectionWarning: my error
but not the 500 nor my error. This is because the request just hangs forever. If you try to open it on the browser, you will see it hang more clearly.
TODO: how to make it show the stack trace as well instead of just my error? Getting the stack trace in a custom error handler in Express?
Express 4 solution
The simplest solution for Express 4 is to just wrap every single route in a try/catch as follows:
app.get('/error', async (req, res, next) => {
try {
throw new Error('my error'
res.send('never returned')
} catch(error) {
next(error);
}
})
which produces the same correct behavior as Express 5.
You can also factor this out further with some of the methods discussed at: express.js async router and error handling
Tested on Node.js v14.16.0.
To handle async requests in express routes use a try catch, which helps you to try for any errors in function and catch them. try{await stuff} catch{err}
An other less intrusive option is using express-async-errors
This will patch express to correctly handle async/await.
import express from 'express'
import 'express-async-errors'
I am implementing this in Nodejs to upload files (photos, mp3, pdf) to amazon web services s3. Currently am trying to upload an mp3 file but I keep getting tis error: "TypeError: Cannot read property 'path' of undefined". I am using the Multipart middlemware.
var fs = require('fs');
//S3FS instance
var S3FS = require('s3fs')
//multiparty middleware (to upload our file)----> S3FS will read it's object and stream it to AWS
//nmp install connect-multiparty
var multiparty = require('connect-multiparty'),
multipartyMiddleware = multiparty();
var express = require('express');
var route = express.Router();
module.exports = route;
route.use(multipartyMiddleware);
//create new filesystem
var s3fsImpl = new S3FS('unasbeatz', {
accessKeyId: 'xxxxxxx',
secretAccessKey: 'xxxxx'
});
//create bucket ----> unasbeatz
s3fsImpl.create();
route.post('/', function (req, resp) {
//get the file from our request object (from multiparty object, it sets the request object)
var file = req.files.file;
//create new readstream
var stream = fs.createReadStream(file.path);
//write to AWS using stream
return s3fsImpl.writeFile(file.originalFileName, stream).then(function () {
fs.unlink(file, function (err) {
if (err)
console.error(err);
})
//else we write uploaded
console.log('uploaded');
})
})
route.get('/', function (req, resp) {
resp.json({ SecretData: 'abcde' })
});
var stream: grabs direct part of every uploaded file. But I don't know why the error comes up
error:
TypeError: Cannot read property 'path' of undefined
at /Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/app/routes/mp3_route.js:34:42
at Layer.handle [as handle_request] (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/route.js:131:13)
at Route.dispatch (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/layer.js:95:5)
at /Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:277:22
at Function.process_params (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:330:12)
at next (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:271:10)
at multipart (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/connect-multiparty/index.js:50:53)
at Layer.handle [as handle_request] (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:312:13)
at /Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:280:7
at Function.process_params (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:330:12)
at next (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:271:10)
at Function.handle (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:176:3)
at router (/Users/AzeezOlusegunOdumosu/Desktop/unasbeatz/node_modules/express/lib/router/index.js:46:12)
Use busboy to receive files in the route.
What I finally implemented was to updload my files in AWS and save their urls in MongoDB,I then reuest for these urls in my web application to show these files in the browser