Reputation: 27
I am writing a simple URL shortening web app to learn JavaScript, Node (focusing on Express), and Mongo. A user submits a URL to my web app and receives a shortened URL (if the submitted URL is a valid URL) or an error message (if the submitted URL is not a valid URL). I have implemented the following algorithm:
The JavaScript code is below. I believe it works (based on some manual testing), but I have the following questions:
app.post("/api/shorturl", handleShortenURL);
function handleShortenURL(req, res) {
console.log("Given URL: " + req.body.url);
// Check if the provided URL is valid and return otherwise. dns.lookup accepts hostnames without the protocol prefix,
// so if there is a protocol prefix in the URL, it needs to be removed
const REPLACE_REGEX = /^https?:\/\//i;
let url = req.body.url.replace(REPLACE_REGEX, '');
dns.lookup(url, function onLookup(err) {
if (err)
{
console.log("err.code = " + err.code);
res.json({"error":"invalid URL"});
}
else // The provided URL is a valid URL
{
// It the URL is already in the database, don't add it again
URLModel.find({ original: url }, function (err, docs) {
if (err)
return console.log(err);
if (Object.keys(docs).length > 0)
res.json({original_url: req.body.url, short_url: docs[0].shortened});
else
{
URLModel.find().exec(function (err, results) {
let count = results.length;
var urlDocument = new URLModel({original: url, shortened: count});
urlDocument.save(function(err, data) {
if (err)
console.error(err);
});
res.json({original_url: req.body.url, short_url: count});
});
}
});
}
});
}
This question is at a high-level similar to mine, but the specific approach for addressing it proposed by @OlivierKrull is somewhat different (it uses async/await along with Promises) and I find it easier to follow and, perhaps, a bit more elegant than the approach proposed at the above link.
Upvotes: 0
Views: 75
Reputation: 2081
In order to omit nested callbacks like this you can use async/await
.
mongoDB queries do return promise
s, so you can easily await
the queries to resolve.
Not sure if dns.lookup
returns a promise
but if it does you could also just use await
there.
I simplified your code and didn't include the error handling. But it should give you an idea of your possibilities.
app.post("/api/shorturl", handleShortenURL);
function handleShortenURL(req, res) {
const REPLACE_REGEX = /^https?:\/\//i;
const url = req.body.url.replace(REPLACE_REGEX, '');
dns.lookup(url, async function onLookup(err) {
if (err) {
console.log("err.code = " + err.code);
return res.json({"error":"invalid URL"});
}
const docs = await URLModel.find({ original: url });
if (Object.keys(docs).length > 0) {
return res.json({original_url: req.body.url, short_url: docs[0].shortened});
}
const results = await URLModel.find();
const count = results.length;
const urlDocument = new URLModel({original: url, shortened: count});
await urlDocument.save();
return res.json({original_url: req.body.url, short_url: count});
});
}
Upvotes: 1
Reputation: 2549
You can go async way and use Promise/await. Below is just an example, but you can adopt it for your program
// Use async
(async () => {
// Function 1
const fn1 = (val) => {
return new Promise((resolve, reject) => {
// Do some stuff here
val = val + 1;
// Resolve result.
// Can be resolved from any level
// of nested function!
function nested1() {
function nested2() {
resolve(val);
}
nested2();
}
nested1();
});
};
// Function 2
const fn2 = (val) => {
return new Promise((resolve, reject) => {
// Do some stuff here
val = val * 2;
// Resolve result
resolve(val);
});
};
// Function 3
const fn3 = (val) => {
return new Promise((resolve, reject) => {
// Do some stuff here
val = val + 1000;
// Resolve result
resolve(val);
});
};
// Sync code
let val = 5;
val = await fn1(val); // Wait until fn1 resolves
val = await fn2(val); // Wait until fn2 resolves
val = await fn3(val); // Wait until fn3 resolves
console.log(val);
})();
Upvotes: 0