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.
Related
While creating matrixClient by using matrix-js-sdk, I am getting an error of TypeError: this.opts.request is not a function.
The error is at line no. 19. but don't understand the reason.
const express = require('express');
const sdk = require('matrix-js-sdk');
const app = express();
const port = 3000;
require('dotenv').config();
const BASE_URL = process.env.BASE_URL;
const ACCESS_TOKEN = process.env.ACCESS_TOKEN;
const USER_ID = process.env.USER_ID;
const PASSWORD = process.env.PASSWORD;
const matrixClient = sdk.createClient({
baseUrl: BASE_URL,
accessToken: ACCESS_TOKEN,
userId: `#${USER_ID}:matrix.org`,
});
app.get('/', async (req, res) => {
await matrixClient.startClient(); // error
matrixClient.once('sync', function (state, prevState, res) {
console.log(state); // state will be 'PREPARED' when the client is ready to use
});
res.send('hello');
});
// getAccessToken(USER_ID, PASSWORD);
function getAccessToken(userId, password) {
const client = sdk.createClient('https://matrix.org');
client
.login('m.login.password', { user: userId, password: password })
.then((response) => {
console.log(response.access_token);
})
.catch((err) => {
console.log('access_token error :', err);
});
}
app.listen(port, () => {
console.log(`app is listening at http://localhost:${port}`);
});
ERROR :
app is listening at http://localhost:3000
Getting saved sync token...
Getting push rules...
Attempting to send queued to-device messages
Got saved sync token
Got reply from saved sync, exists? false
All queued to-device messages sent
Getting push rules failed TypeError: this.opts.request is not a function
at MatrixHttpApi.doRequest (A:\matrix\matrix_node\node_modules\matrix-js-sdk\lib\http-api.js:741:23)
at MatrixHttpApi.requestOtherUrl (A:\matrix\matrix_node\node_modules\matrix-js-sdk\lib\http-api.js:620:17)
at MatrixHttpApi.request (A:\matrix\matrix_node\node_modules\matrix-js-sdk\lib\http-api.js:576:17)
at MatrixHttpApi.authedRequest (A:\matrix\matrix_node\node_modules\matrix-js-sdk\lib\http-api.js:524:33)
ix-js-sdk\lib\client.js:7283:22) at SyncApi.getPushRules (A:\matrix\matrix_node\node_modules\matrix-js-sdk\lib\sync.js:155:42) at SyncApi.sync (A:\matrix\matrix_node\node_modules\matrix-js-sdk\lib\sync.js:674:16)
at MatrixClient.startClient (A:\matrix\matrix_node\node_modules\matrix-js-sdk\lib\client.js:497:18) at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async A:\matrix\matrix_node\index.js:19:3
Waiting for saved sync before retrying push rules...
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?
Faced an issue with Mongoose.
import express from 'express';
import Countries from '../models/countries.mjs';
const router = new express.Router();
router.get('/countries-data', async (req, res) => {
try {
let countries =
await Countries.find({})
.select(
'-_id specimenDate dailyLabConfirmedCases changeInDailyCases')
.sort('specimenDate');
if (!countries) return res.status(500).send();
res.json(countries);
} catch (err) {
res.status(500).send();
}
});
This code works as expected but I decided to remove the async/await and use find().then() instead:
import express from 'express';
import Countries from '../models/countries.mjs';
const router = new express.Router();
router.get('/countries-data', (_, res) => {
Countries.find({})
.select('-_id specimenDate dailyLabConfirmedCases changeInDailyCases')
.sort('specimenDate')
.then((countries) => {
if (!countries) throw Error('no data');
res.json(countries);
})
.catch(res.status(500).send());
});
This one rise an exception while trying to send the json data:
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I really don't know what I'm doing wrong. And why the catch isn't getting the promise exception? Any suggestion?
I think the problem arises from the response you sent in the catch block since the catch takes in a callback function which returns an error if there is any.
Try this:
import express from 'express';
import Countries from '../models/countries.mjs';
const router = new express.Router();
router.get('/countries-data', (_, res) => {
Countries.find({})
.select('-_id specimenDate dailyLabConfirmedCases changeInDailyCases')
.sort('specimenDate')
.then((countries) => {
if (!countries) throw Error('no data');
res.json(countries);
})
// refactor your code like this
.catch((err)=>{
res.status(500).send(err)
});
});
I'm new to node and react and I am trying to fetch some data and show it on my react page. It's pretty simple. I have an express server running on localhost:3001 and my react app is on localhost:3000.
I'm attempting to fetch data and then set that data to a state via a hook. I can't seem to get the data on the react page or in the web developer console. Is there a way I can see the data that is being fetched in the console?
Here is my React component:
import React, { useState } from "react";
function App() {
const [weatherData, setWeatherData] = useState("");
console.log(weatherData);
React.useEffect(() => {
const fetchData = async () => {
const result = await fetch(
"http://localhost:3001"
);
const data = await result.json();
console.log("data", data);
setWeatherData(data);
};
fetchData();
})
return (
<div>
<h1>The temprature is {weatherData}</h1>
</div>
);
}
export default App;
Here is my node server:
const express = require('express');
const bodyParser = require('body-parser');
const https = require("https");
const cors = require('cors');
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'jsx')
app.use(express.static("public"));
app.get("/", function (req, res) {
const query = Chicago;
const apiKey = "a valid key";
const unit = "imperial";
const url = "https://api.openweathermap.org/data/2.5/weather?appid=" + apiKey + "&q=" + query + "&units=" + unit;
https.get(url, (response) => {
console.log("statusCode", res.statusCode);
response.on("data", (d) => {
const weatherData = (JSON.parse(d));
console.log(weatherData);
res.send(weatherData);
});
}).on("error", (e) => {
console.error(e);
})
});
const port = process.env.PORT || 3001;
app.listen(port, () => console.log(`Listening on port ${port}`));
The result I get is no data and these 2 errors in chrome dev tools console.
index.js:1 Uncaught SyntaxError: Unexpected token '<'
App.jsx:19 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
Any help is greatly appreciated!!
You need to specify the Content-Type of data returned from the server.
Either you can use res.setHeader before sending response or res.json() to send json response
https.get(url, (response) => {
console.log("statusCode", res.statusCode);
response.on("data", (d) => {
const weatherData = JSON.parse(d);
console.log(weatherData);
res.json(weatherData); // use json response
});
}).on("error", (e) => {
console.error(e);
})
It seems there maybe an error occur in result.json().
Probably the request return html rather then json.
You can use postman or other tools to get the real response by 'localhost:3001' to clear where goes wrong.
const data = await result.json();
this line will occur a problem if result does not have valid json.
that's why you have encountered an error. catch that error and see the response
I'm trying to update multiple collections in my Firestore and RTDB by triggering Google cloud function through an HTTP request, and I've done some several times of testing, but the batch commit works from time to time. I've also tried running test excluding RTDB update from the code, but nothing changes much.
If something goes wrong, I get the following error message with status code 500.
Error: Cannot modify a WriteBatch that has been committed.
Here's the example code:
Server-side code
'use strict';
module.exports = ({ admin, cors, express, functions }) => {
const app = express();
const fireStore = admin.firestore();
const rtdb = admin.database();
const apps = fireStore.collection('apps');
const users = fireStore.collection('users');
const batch = admin.firestore().batch();
app.use(cors({ origin: true }));
...
app.post('/', (req, res) => {
const uid = req.user.user_id;
const data = req.body;
const appsRef = apps.doc(uid);
const usersRef = users.doc(uid);
const activityState = {
currentActiveStatus: data.activityState.currentActiveStatus,
usingApp: data.activityState.usingApp
};
const appState = {
emailVerified: data.user.emailVerified
};
const userState = {
displayName: data.user.displayName,
photoURL: data.user.photoURL,
currentActiveStatus: data.user.currentActiveStatus,
lastLoginAt: admin.firestore.FieldValue.serverTimestamp()
};
batch.update(appsRef, appState);
batch.update(usersRef, userState);
return batch.commit().then(() => {
console.log('Batch commit finished!');
return admin.database().ref(`status/${uid}`).update(activityState).then(() => {
res.status(201).send({ message: 'Successfully Initialize Default State' });
});
}).catch(err => console.log('Err:', err));
});
return functions.https.onRequest(app);
};
Client-side code
const data = {
activityState: {
currentActiveStatus: "online",
usingApp: "true"
},
user: {
displayName: this.displayName,
photoURL: this.photoURL,
currentActiveStatus: "online",
emailVerified: "true"
}
};
this.userService.updateUserProfile(this.displayName, this.photoURL).then((accessToken) => {
const url = 'https://us-central1/dbname/cloudfunctions.net/functionname';
this.http.post(url, JSON.stringify(data), {
headers: {'Authorization': accessToken, 'Content-Type': 'application/json; charset=utf-8'}
}).subscribe((res) => {
// Worked well
}, (err) => {
// Went wrong
});
});
Error message in details
Error: Cannot modify a WriteBatch that has been committed.
at WriteBatch.verifyNotCommitted (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/write-batch.js:148:13)
at WriteBatch.update (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/write-batch.js:333:10)
at app.post (/user_code/exports/auth/user/startapp/initDefaultState.f.js:54:11)
at Layer.handle [as handle_request] (/user_code/node_modules/express/lib/router/layer.js:95:5)
at next (/user_code/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/user_code/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/user_code/node_modules/express/lib/router/layer.js:95:5)
at /user_code/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/user_code/node_modules/express/lib/router/index.js:335:12)
at next (/user_code/node_modules/express/lib/router/index.js:275:10)
Perhaps I'm missing something out in my code?
I resolved my problem. It was a silly mistake that I've made.
I had to declare batch inside app.post().
app.post('/', (req, res) => {
const batch = admin.firestore().batch();
});
Instead
module.exports = ({ admin, cors, express, functions }) => {
const app = express();
const fireStore = admin.firestore();
const rtdb = admin.database();
const apps = fireStore.collection('apps');
const users = fireStore.collection('users');
const batch = admin.firestore().batch();
};