Express Middleware Setting Header Error - express

I'm trying to implementation a fairly simple middleware function to my Express application that just adds a useCache value to the request object being passed to the main handler but for some reason, I'm getting a Can't set headers after they were sent error.
const cacheControl = (req, res, next) => {
if (lastPulled === null) lastPulled = Date().getDay()
req.useCache = Date().getDay() === lastPulled
next()
}
app.use(cacheControl)
app.get('/missions', (req, res) => {
if (req.useCache) res.status(200).json({ result: cache })
fetch(dumpUrl)
.then(data => data.text())
.then(result => {
cache = result
res.status(200).json({ result })
})
.catch(e => res.status(500).json({ result: e.message }))
})
I've read that most of the time if the error is produced by the middleware it is due to multiple next() calls, but that doesn't apply here, unless I'm missing something obvious.
When I remove the cacheControl middleware from the application, there is no longer an error, but I can't figure out what in the function is causing the error! Any pointers are helpful!

I'm guessing it's because res.json() is firing twice:
app.get('/missions', (req, res) => {
if (req.useCache) res.status(200).json({ result: cache })
fetch(dumpUrl)
.then(data => data.text())
.then(result => {
cache = result
res.status(200).json({ result })
})
.catch(e => res.status(500).json({ result: e.message }))
})
// if res.useCase is true, set headers and reply
if (req.useCache) res.status(200).json({ result: cache })
// then fetch and reply again (which generates the error)
fetch(dumpUrl)
.then(data => data.text())
.then(result => {
cache = result
res.status(200).json({ result })
Change it to this to utilize explicit return
app.get('/missions', (req, res) => {
if (req.useCache) return res.status(200).json({ result: cache })
return fetch(dumpUrl)
.then(data => data.text())
.then(result => {
cache = result
res.status(200).json({ result })
})
.catch(e => res.status(500).json({ result: e.message }))
})
The nature of the error is similar to when you do this:
problem
function problem() {
if (true === true) console.log('send problem')
console.log('send garbage by accident')
}
console.log(problem())
solution
function solution() {
if (true === true) return console.log('send solution')
return console.log('send nothing')
}
console.log(solution())
return is how you exit a function. Your issue is that your code was checking the if condition, but then continuing past it because it wasn't told to stop once it found that condition.
The old way or less terse way to write your function would be like:
app.get('/missions', (req, res) => {
if (req.useCache) {
res.status(200).json({ result: cache })
} else {
fetch(dumpUrl)
.then(data => data.text())
.then(result => {
cache = result
res.status(200).json({ result })
})
.catch(e => res.status(500).json({ result: e.message }))
}
})
Without the else in there, it executes every if statement it comes across until it reaches the end of the function, unless you use the return keyword as the cue to exit right there.
Keep in mind, using return inside a .then() function will resolve the promise, it won't exit from the upper scope if there are more .then()s chained on.

Related

returning sqlite3 query in Node

I'm trying to write a function that returns a query from a sqlite3 database (using Node and Express)
This is how (likely) the function is called
app.get('/example',(req,res)=>{
console.log(getThings(db_connection))
}
And this is the function per se
getThings(db){
let sql = 'SELECT * FROM Table'
let results[]
db.all(sql, (err, rows) => {
if(err){throw err}
let i
for(i=0;i<rows.length;i++){
res.push(rows[i])
}
console.log(res)
})
return res
}
I expected the rows being returned at the end, but it always returns res before populating it first, and just then it prints res with the correctly
I might have understood why it does so, but I have no idea how to fix it properly (I'm still new at JS)
Callbacks are asynchronous, so res will not be populated before the return.
You need to make your callback into a Promise or use async/await.
Promisify the callback:
getThings(db){
return new Promise((resolve, reject) => {
db.all('SELECT * FROM Table', (err, rows) => {
if (err) reject(err)
resolve(rows)
})
})
}
app.get('/example', (req, res) => {
getThings(db_connection)
.then(rows => res.send(result))
.catch(err => res.send({
error: err.message
}))
}
or
Use async/await:
Wrapped in try/catch to catch err, then because you're simply looping over the rows you don't need the for loop.
When you see the following structure object.method('param', (err, rows) => { you can almost guarantee its Promise compatible, else if not you can use util.promisify(original) to make it so.
Cleaner and more verbose then then().catch() chaining:
app.get('/example', async (req, res) => {
let result = {}
try {
result = await getThings(db_connection)
} catch (err) {
result = {
error: err.message
}
}
res.send(result)
}
async getThings(db) {
return db.all('SELECT * FROM Table')
}

Callback function 'next' is executed automatically without making a call to it

I have this POST method which uses FetchURL middleware to fetch data from the url submitted by the user.
router.post('/', FetchURL, (req, res) => {
console.info('data received');
...
})
Everything works with response.ok being true, but the contrary case doesn't quite work as expected.
I don't want next to be called when response.ok equals false.
But I get to see "data received" logged to the console which means the next function does get called on its own.
fetch_url.js
function FetchURL(req, res, next) {
fetch(req.body.input_url)
.then(response => {
if(response.ok)
return response.json();
// else render error message on the client machine
res.status(response.status)
.render('index', {
errStatus: [response.status, response.statusText]
});
/* Throwing an Error here is the only way I could prevent the next callback */
// throw new Error(`Request failed with status code ${response.status}.`);
})
.then(data => {
req.data = data;
next();
})
.catch(err => console.error(err));
}
I could not find anything relevant on the documentation of expressjs middleware. The only way I could prevent next from being called is by throwing an Error on the server.
What happens behind the scene here?
try making a second check before next is called like following
function FetchURL(req, res, next) {
fetch(req.body.input_url)
.then(response => {
if(response.ok) // wrap your response in a temporary object.
return { fail: false, data: response.json() } ;
// else render error message on the client machine
res.status(response.status)
.render('index', {
errStatus: [response.status, response.statusText]
});
/* Instead of throwing an Error, return something indicating error */
return { fail: true };
})
.then(data => {
// check if previous procedure has failed.
if(!data.fail) {
req.data = data.data;
next();
}
})
.catch(err => console.error(err));
}

Problem in reusable code to check internet availability in react native

I have made a function that checks for internet availability. whenever I call this function it gives me true every time whether the internet is ON or OFF. I want to have one function that contains code to check the internet and I can call it before fetching data from the internet . my code is below.
const [campusList, setCampusList]= React.useState([{label:'Select Campus', value:'select campus'}]);
const isConnected =()=>{
NetInfo.fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
if(state.isConnected)
return true;
else
return false;
});
}
const loadCampuses = async()=>{
if(isConnected)
{
await fetch(url)
.then((respons)=>respons.json())
.then((jsonResponse)=>{
jsonResponse.map((data)=>
setCampusList(campusList=> [...campusList, {label:data.Text, value:data.Value}])
);
})
.catch((error)=>console.log(error))
//.finally(()=>setLoading(false))
}
}
fetch Returns a Promise that resolves to a NetInfoState object. you need to wait promise to resolve
try this
const isConnected = sendRequest => {
NetInfo.fetch().then(state => {
if (state.isConnected) {
sendRequest();
}
});
};
const loadCampuses = () => {
isConnected(async () => {
await fetch(url)
.then(respons => respons.json())
.then(jsonResponse => {
jsonResponse.map(data =>
setCampusList(campusList => [
...campusList,
{ label: data.Text, value: data.Value },
]),
);
})
.catch(error => console.log(error));
});
};
oh right, it's a promise, not just a straight return. you need to await for it. You don't need a separate function:
if(await NetInfo.fetch().isConnected)

await is only valid in async function - if else function

I check if there is such data in the firestore with (if (docSnapshot.exists)). I need to call await function when it goes inside else, but in this way I get an error "await is only valid in async function". How can I use the await function inside if else?
drawRoad = async (userLatitude,userLongitude,BranchLocationLatitude,BranchLocationLongitude,minPrice,shippingPrice) => {
try {
let userid = (this.props.UserStore.UserId).toString()
await firestore().collection('Tracking').doc(userid).onSnapshot(docSnapshot => {
if (docSnapshot.exists){
...
}
else {
...
let rsp=await fetch(url)
rsp=await rsp.json() }
}, err => {
console.log(`Encountered error: ${err}`);
});
} catch (error) {
console.log(error)
console.log("hata_componentDidMount HomeMap")
}
}
Try something like this:
else {
...
await fetch(url)
.then(res => res.json())
.then(res => do what you want with the response like setState, console.log it or whatever)
.catch(err => console.log(err))
}
Maybe try to tell the function to be async like this.
async function drawRoad(){
await fetch(url)
.then(res => res.json())
.then(res => {

React Native fetch doesn't work in another fetch callback

If I call my api function from POINT 1, fetch method inside the api method works well. When I comment it out and call the function at POINT 2 fetch method inside the addAccount() doesn't work. There is no exception, no rejection, no request on Reactotron, even I can't find request over Charles Proxy. What is the difference and what I have to know to figure it out?
I tried with RN 0.55.2 and 0.57.5
// Auth.js typical react native component
import * as api from '../actions/api';
class Auth extends Component {
// first triggered function
loginAccount(){
// api.addAccount(); // POINT 1 - this line works well if I uncomment
fetch('https://domain-a.com/login/',{
method: 'POST',
credentials: "same-origin",
headers: {
'accept-language': 'en-US;q=1',
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
body: encodeURIComponent(bodyParameters)
}).then((response) => {
console.log(response);
return response.json()
}).then(({ status, invalid_credentials }) => {
if(status == "ok"){
CookieManager.get('https://domain-a.com')
.then((cookies) => {
this.fetchAccountData(cookies);
})
})
}
fetchAccountData(cookies){
fetch('https://domain-a.com/'+cookies.user_id+'/info/',{
method: 'GET',
headers: {
'cookie': cookies
}
}).then((response) => {
return response.json();
})
.then(({ user, status }) => {
api.addAccount(); // POINT 2 - this line doesn't work
});
}
}
// api.js
// I repleaced fetch code with document example just to be clearify
export const addAccount = () => {
console.log("fetch begin"); // always works
fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson); // won't works from point 2
})
.catch((error) =>{
console.error(error); // never runs
});
}
It looks like your first .then statement in the addAccount() function is missing a return statement. responseJson would be undefined without a proper a 'return response.json()' statement. Also adding brackets for better semantic formatting.
export const addAccount = () => {
console.log("fetch begin"); // always works
fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => {
console.log(response); //test this response
return response.json();
})
.then((responseJson) => {
console.log(responseJson); // won't works from point 2
})
.catch((error) =>{
console.error(error); // never runs
});
}