How to use the data after finding object by find() node.js/mongoDB - URL shortener

I got this far but I am stuck again…
under “find url” section, I want to use the data that I got (like let redirectUrl = data.original_url) but it returns undefined when I check with console.log.
Is there any way that I can get the redirectURL by the number that is entered in the URL?

'use strict';

var express = require('express');
var mongo = require('mongodb');
var mongoose = require('mongoose');
var cors = require('cors');

var app = express();

// Basic Configuration 
var port = process.env.PORT || 3000;

/** this project needs a db !! **/ 
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true }, (error, client) => {
	console.log("Successfully connected to MongoDB");
})
// var MongoClient = require('mongodb').MongoClient;
// var url = process.env.MONGO_URI;
// MongoClient.connect(url, function(err, db) {
//   if (err) throw err;
//   console.log("Database created!");
//   db.close();
// });
app.use(cors());

/** this project needs to parse POST bodies **/
// you should mount the body-parser here

const bodyParser = require('body-parser');
app.use('/public', express.static(process.cwd() + '/public'));
app.get('/', function(req, res){
  res.sendFile(process.cwd() + '/views/index.html');
});
app.use(bodyParser.urlencoded({ extended: false }))
  
//crete model
const Schema = mongoose.Schema;
const urlSchema = new Schema({
  original_url:{type:String,required:true},
  shortid: { type: Number},
});
let Url = mongoose.model('Url', urlSchema);


app.post('/api/shorturl/new', (req, res) => {
  
let originalUrl = req.body.url;
let number = Math.floor(Math.random()*10000).toString();

  //create new url
let newUrl = new Url({
  original_url:originalUrl,
  shortid: parseInt(number),
  }    
);
  
//  //save new url
newUrl.save(function(err, data){
    if (err)
      throw err
  });
console.log("newurl:"+newUrl)
res.send({"original_url":newUrl.original_url,"short_url":newUrl.shortid});
})


app.get('/api/shorturl/new/:number?', function(req, res){
  let number = req.params.number  
  console.log("number:" + number)

//find url
  let findUrl = function(n, done) {
  Url.findOne( {shortid: n}, function (err, data) {
    if (err) {
            throw err
          } else {
            console.log("data:"+data);
            let redirectUrl = data.original_url
            console.log(redirectUrl)
          }   
    });
};
  findUrl(number)
  
  res.redirect(redirectUrl);
});


app.listen(port, function () {
  console.log('Node.js listening ...');
});

Try using JSON.parse();

Its possible data is still in the JSON string format and needs to be rolled in an object. If this line works correctly then that is most likely the issue.

console.log("data:"+data); // If this outputs your object then its still a string

So try this

const dataObject = JSON.parse(data);
let redirectUrl = dataObject.original_url
console.log(redirectUrl);

Thank you, @shadowfox476!

console.log("data:"+data); does output my object as below but JSON.parse(data) gave me an error.

data:{ _id: 5d36459a72512a5fbbfa68d2,

  original_url: 'https://www.amazon.com/',

  shortid: 6412,

  __v: 0 }

I’m gonna look into it more or try to figure out different way to get the url to redirect.

What error does it give you when you use JSON.parse()?

SyntaxError: Unexpected token _ in JSON at position 2

    at JSON.parse (<anonymous>)

at /app/server.js:91:37

    at /rbd/pnpm-volume/ae72812f-2414-4216-b09b-536f7472e1a1/node_modules/.registry.npmjs.org/mongoose/5.6.6/node_modules/mongoose/lib/model.js:4567:16

    at /rbd/pnpm-volume/ae72812f-2414-4216-b09b-536f7472e1a1/node_modules/.registry.npmjs.org/mongoose/5.6.6/node_modules/mongoose/lib/query.js:4297:12

    at process.nextTick (/rbd/pnpm-volume/ae72812f-2414-4216-b09b-536f7472e1a1/node_modules/.registry.npmjs.org/mongoose/5.6.6/node_modules/mongoose/lib/query.js:2790:28)

    at process._tickCallback (internal/process/next_tick.js:61:11)

Emitted 'error' event at:

    at /rbd/pnpm-volume/ae72812f-2414-4216-b09b-536f7472e1a1/node_modules/.registry.npmjs.org/mongoose/5.6.6/node_modules/mongoose/lib/model.js:4569:13

    at /rbd/pnpm-volume/ae72812f-2414-4216-b09b-536f7472e1a1/node_modules/.registry.npmjs.org/mongoose/5.6.6/node_modules/mongoose/lib/query.js:4297:12

    at process.nextTick (/rbd/pnpm-volume/ae72812f-2414-4216-b09b-536f7472e1a1/node_modules/.registry.npmjs.org/mongoose/5.6.6/node_modules/mongoose/lib/query.js:2790:28)

    at process._tickCallback (internal/process/next_tick.js:61:11)

For some reasons, my original code just worked now. weired!

            let redirectUrl = data.original_url
            res.redirect(redirectUrl);

But thank you very much for your advise. I didn’t know JSON.parse(), so it was definitely good to know!!

The following findUrl function is asynchronous.

//find url
  let findUrl = function(n, done) {
  Url.findOne( {shortid: n}, function (err, data) {
    if (err) {
            throw err
          } else {
            console.log("data:"+data);
            let redirectUrl = data.original_url
            console.log(redirectUrl)
          }   
    });
};

And the problem is that you’re redirecting the user without waiting for that function to finish. On top of that,
let redirectUrl = data.original_url

redirectUrl is block scoped and you can’t access it outside.

My suggestion would be to simply use Url.findOne directly, without wrapping it in a function to make it easier. Like this:

app.get('/api/shorturl/new/:number?', function(req, res){
  let number = req.params.number  
  console.log("number:" + number)

//find url
  Url.findOne( {shortid: n}, function (err, data) {
    if (err) {
            throw err
          } else {
            console.log("data:"+data);
            let redirectUrl = data.original_url
            console.log(redirectUrl)
            // Redirect here, inside callback
            res.redirect(redirectUrl);
          }   
    });
 
});

This should work. And if you really want to use a custom wrapper function around Url.findOne, either you’d need to use a promise or pass a callback function like this:

//find url
let findUrl = function(n, callback) {
  Url.findOne( {shortid: n}, function (err, data) {
    if (err) {
            throw err
          } else {
            console.log("data:"+data);
            let redirectUrl = data.original_url
            console.log(redirectUrl);
            // pass the data to the callback here
            callback(redirectUrl)
          }   
    });
};

And you’d use the above function like this:

const number = 1234; // whatever the number you want to find
findUrl(number, function(redirectUrl) {
  // We have redirect url here!
  res.redirect(redirectUrl)
});

PS: Sorry, I posted this reply for Eriko87, not shadowfox746. It’s a mistake.

1 Like

That’s a good catch! Hope this works for you

@husseyexplores
Thank you!

redirectUrl is block scoped and you can’t access it outside.

Yes, that was also annoying me. I don’t know why I decided to wrap with function (I was probably testing a lot of different way). I also saw error message or something about using promise but I couldn’t figure out how to use it.
I decided to go with your first suggestion and it works perfectly!

Thank you very much for helping on this @shadowfox476 and @husseyexplores :blush: I really appreciate it!

1 Like