Cloud Functions: Promises must be handled appropriately - node.js

Since I upgraded Cloud Functions for Firebase to Node8 and ES17 a TsLint error shows up when trying to update a function. It throws Promises must be handled appropriately for this chunk of code:
app.get('*', (req, res) => {
const isBot = detectBot(req.headers['user-agent']);
if (isBot) {
const botUrl = generateUrl(req);
// If bot, fetch url via rendertron
fetch(`https://foo.com/render/${botUrl}`)
.then(rendertronRes => rendertronRes.text())
.then(body => {
res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
res.set('Vary', 'User-Agent');
res.send(body.toString());
});
} else {
// Not a bot, fetch the regular Angular app
fetch('https://bar.com/')
.then(regularRes => regularRes.text())
.then(body => {
res.send(body.toString());
})
.catch(err => res.send(err));
}
});
The strangest part is that it complains about the second fetch, but not the first.

Try to write catch block for each then

Related

didn't get response when using res.write() inside of the fetch function

I am using this <res.write()> ==> https://nodejs.org/api/http.html#responsewritechunk-encoding-callback (in nodejs)
and using this fetch ==> https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
My situation is that I didn't see any response when using the res.write() function inside of the fetch function. for example, in the below backend code, I tried to put res.write("456") inside of the first then function below fetch, but I only see 123 returned in the frontend, no 456.
res.write("123");
fetch('http://example.com/movies.json')
.then((response) => {response.json(), res.write("456")})
.then((data) => console.log(data));
I have searched Google for a while, but didn't see anything related. My guess is that this could be because of async usage.
appreciate if someone can give suggestions.
===== update =====
res is express's res obj
async sendText(res: Response){
res.write("123")
fetch('http://example.com/movies.json')
.then((response) => {return response.json()})
.then((data) => {
console.log(data);
res.write('456');
res.end();
});
}
seeing behavior: only see 123 in the frontend.
VS
async sendText(res: Response){
res.write("123")
await fetch('http://example.com/movies.json')
.then((response) => {return response.json()})
.then((data) => {
console.log(data);
res.write('456');
res.end();
});
}
seeing behavior: can see both 123 and 456 in the frontend.
I never use await and .then together before, not fully understand the difference. searching the web rn.
You aren't checking for any errors or an unsuccessful response so response.json() may be failing, preventing anything after it from executing.
Something like this should work better for you
async sendText (res: Response) {
res.write("123");
const response = await fetch("http://example.com/movies.json");
// check for an unsuccessful response
if (!response.ok) {
const error = new Error(`${response.status} ${response.statusText}`);
error.text = await response.text();
throw error;
}
const data = await response.json();
res.write("456");
res.end(); // finalise the HTTP response
console.log(data); // log data for some reason ¯\_(ツ)_/¯
};
This will return a promise that resolves with undefined or rejects with either a networking error, HTTP error or JSON parsing error.
When calling it, you should handle any rejections accordingly
app.get("/some/route", async (res, res, next) => {
try {
await sendText(res);
} catch (err) {
console.error(err, err.text);
next(err); // or perhaps `res.status(500).send(err)`
}
});
I never use await and .then together before
Nor should you. It only leads to confusing code.

Error in firebase function that generates custom token

I'm trying to implement a Firebase function that generates a custom token for my app. But I keep getting the following error message :
Error: could not handle the request
Or it ends up in timeout.
Do you have any idea of what could be wrong with my code hereafter ? I'm trying it with a 'test' uid.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const serviceAccount = require('./serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
exports.customFunction = functions.https.onRequest((data, context) => {
return admin.auth().createCustomToken('test')
.then(customToken => {
console.log(`The customToken is: ${customToken}`);
return {
status: 'success',
customToken: customToken
};
})
.catch(error => {
console.error(`Something happened buddy: ${error}`)
return {
status: 'error'
};
});
});
Your Cloud Function is an HTTPS one. In order to terminate it you need to call res.redirect(), res.send(), or res.end() as explained in the doc.
In your code you actually return the Promises chain: this is the correct way to terminate Cloud function triggered by background events (which is not the case of an HTTPS Cloud Function which is triggered by a call to the URL it exposes).
So, the following changes should do the trick (untested):
exports.customFunction = functions.https.onRequest((req, res)(data, context) => {
admin.auth().createCustomToken('test') // No need to return
.then(customToken => {
console.log(`The customToken is: ${customToken}`);
response.status(200).send({
status: 'success',
customToken: customToken
});
})
.catch(error => {
console.error(`Something happened buddy: ${error}`)
response.status(500).send(error);
});
});
Note that with an HTTPS Cloud Function, the objects you pass to the handler are not the Firebase data and context objects but the Express.js request and response objects.
So it is more clear to write
exports.customFunction = functions.https.onRequest((req, res) => {...});
instead of
exports.customFunction = functions.https.onRequest((data, context) => {...});

How to resolve Node.js mssql promise

I am trying to use the response from a SQL Server stored procedure and pass it into another stored procedure to log the response data.
However, I'm not sure how to chain them together as it appears they each need their own connection pool.
Attempt 1
// mssql#3.3.0
exports.chain = (req, res) => {
sql.connect(config.properties).then(pool => {
return pool.request()
.execute("chain")
.then(response => {
return pool.request()
.input("param", sql.NVarChar(300), result[0][0]["response"])
.execute("chain2")
.then(result => res.send(result))
.catch(err => res.send(err))
})
.catch(err => res.send(err))
})
}
// returns {}
Attempt 2
exports.chain = (req, res) => {
sql.connect(config)
.then(pool => {
return pool.request()
.execute("chain")
}).then(result => {
return pool.request()
.input("param", sql.NVarChar(300), result[0][0]["response"])
.execute("chain2")
}).then(result => {
res.send(result)
}).catch(err => {
// ... error checks
})
sql.on('error', err => {
// ... error handler
})
}
// Throws error HTTP Status: 500, HTTP subStatus: 1013
Attempt 3
sql.connect(config.properties).then(pool => {
return pool.request()
.execute("chain")
}).then(response => {
pool.request()
.execute("chain2")
.input('param', sql.NVarChar(300), response[0][0]['response'])
.then(response => res.send(response))
.catch(err => res.send(err))
})
// Throws error HTTP Status: 500, HTTP subStatus: 1013
Timeouts might be related to
DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
How would I take the response from the first stored procedure and pass it into a second one for logging?
A connection pool is a an instance consisting of multiple TDS connections, so you should be able to connect several procedures to the same pool, as a new request only accuires one of the TDS connections. Maybe it's a flaw in your config probs.
In your third example. Your input comes after execute, so that's probably causing the error.
As a request returns a promise, you can chain them with then, as you are already doing, but variables in each then are not in the same scope, so you can't access a user, received from the user record, when you are in the then for a different request.
Alternatively, you can also nest them via callback, if you need to access variables across responses:
sql.connect(config.properties).then(pool => {
pool.request()
.execute("chain", (error, response) => {
const row = response.recordset;
pool.request()
.input('param', sql.NVarChar(300), row.foo)
.execute("chain2", (err, res) => {
const row_ = res.recordset;
// send response to client
});
});
}).catch(err => {
// error handling here
});
Best solution, would probably be to return your stored procedure as JSON, and you would only need one request anyway. Downside is, you probably need more specialized procedures.

Async Request to API return undefined in Jest

I am quiet new to testing, and specifically to Jest.
I am following several tutorials in which they handle asynchronous code in the manner I am attempting. My code seems to work when I am making a custom Promise that resolves with dummy data. But when I try to use axios to fetch from an external API, Jest gets as a response undefined.
// functions2.js
const axios = require("axios")
const fetch = () => {
axios.get("https://jsonplaceholder.typicode.com/users")
.then(res => res.data)
.catch(err => err);
}
module.exports = fetch;
// functions2.test.js
describe("async operation", ()=>{
it("should be defined", ()=>{
expect(fetch).toBeDefined()
}); // Passed
it("should fetch", async () => {
expect.assertions(1);
const data = await fetch();
expect(data).toBeTruthy();
}) // Did not pass, data is undefined
it("should fetch, using promises", () => {
expect.assertions(1);
return fetch().then(data => {
expect(data).toBeTruthy();
}) // Did not pass, got 0 assertions
})
})
In one tutorial I encountered that this has something to do with Jest running through Node.JS, but I don't know how to handle it because I don't know node.js.
Also, I followed a tutorial by Traversy Media, cloned his Git repo (https://github.com/bradtraversy/jest_testing_basics) and had the same problem (though in the video it worked)
The problem is because you are not returning the promise from fetch.
Update your functions2.js to something like:
const fetch = async () => {
return axios
.get("https://jsonplaceholder.typicode.com/users")
.then(res => res.data)
.catch(err => err);
};

Using Node and Express, How to Call remote API from inside server.get(..)

Because of CORS problems, I want to call an external REST API from inside my node express server. That is, I have code like this that obviously does not work because it does not return.
How can I make this work and return the results of my external call?
const server = express();
server.put('/callme',(req,res) => {
axios
('http://weather.com/restapi', 'put', { zip: 10530 })
.then((resp: any) => {
console.log(' success' + resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
Axios returns a Promise which is resolved in the .then(). In order to get the response data back to the client you need to return it with res.send().
const server = express();
server.get('/callme', (req, res) => {
axios
.get('http://weather.com/restapi?zip=10530')
.then((resp: any) => {
res.send(resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
It would be a good idea to cache the weather API response for a period of time and serve the cached response for subsequent requests.

Resources