Url Shortener get route not redirecting

My API routes appear to work in the browser but when I run the tests I get : TypeError: Cannot read property 'original_url' of null. Please can you help me fix this.

My code:

require('dotenv').config();
const express = require('express');
const cors = require('cors');
const app = express();
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const mongodb = require('mongodb');
const dns = require('dns');
const shortid = require('shortid');

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

app.use(cors());

app.use('/public', express.static(`${process.cwd()}/public`));

app.use(bodyParser.urlencoded({extended: false}));

// connect to the database
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });

app.get('/', function(req, res) {
  res.sendFile(process.cwd() + '/views/index.html');
});

// Your first API endpoint
app.get('/api/hello', function(req, res) {
  res.json({ greeting: 'hello API' });
});

const Schema = mongoose.Schema;

const urlSchema = new Schema({
  "original_url": String,
  "short_url": {type: String, default: shortid.generate}
});

const Url = mongoose.model('Url', urlSchema);

const options = {
  family: 6
};

app.post("/api/shorturl", (req, res, next) => {
  const url = new URL(req.body.url);
  if (url) {
    dns.lookup(url.hostname, options, (err, address, family) => {
      if (err) {
        return res.json({error:'invalid url'});
      } else {
        next();
      }
    })
    } else {
      return res.json({error: 'invalid url'})
    }
  }, (req, res) => {
    Url.exists({original_url: req.body.url}, (err, result) => {
      if (err) {
        return res.json({error: 'invalid url'});
      } else if (result) {
         Url.findOne({original_url: req.body.url}, (err, data) => {
              if (err) {
                return console.log(err)
                };
              return res.json({original_url: data.original_url, short_url: data.short_url});
            });
      } else {
          Url.create({original_url: req.body.url}, (err, data) => {
              if (err) return console.log(err);
              return res.json({original_url: data.original_url, short_url: data.short_url});
        })
      }
    })
    });
    
  
// short url for redirection
app.get('/api/shorturl/:shorturl', (req, res) => {
  Url.findOne({ short_url: req.params.shorturl }, (err, data) => {
    if (err) return res.json({ error: 'invalid url' });
    return res.redirect(data.original_url);
  })
})


app.listen(port, function() {
  console.log(`Listening on port ${port}`);
});

Hi,

First thing I noticed in your code is that you use the body-parser module. It’s depreciated.
I would swap this:

app.use(bodyParser.urlencoded({extended: false}));

for this:

app.use(express.urlencoded({extended: true})

When it comes to the redirect, I would console.log the data returned by findOne and go from there. I had a similar problem but can’t recall what it was exactly. To get around it I went for

Url.find({ short_url: req.params.shorturl }, (err, data)=>{...})

instead of Url.findOne.
Url.find will return an array, so you can just return the first item:

return res.redirect(302, data[0].original_url)

Cheers

Thank you for replying. I have tried to implement the suggestions you made but now I get TypeError: Cannot read property 'original_url' of undefined. The post route sends the JSON as expected but when redirecting I have this error.

Could I ask you to send me an invite to your project?
I assume you use replit.
Thanks

Yeah I use replit, here is the link.
Cheers

Hi,

I believe, I have this one figured out.
Let’s look at your redirect route:

// short url for redirection
app.get('/api/shorturl/:shorturl', (req, res) => {

  console.log("req params: ")
  console.log(req.params)

  Url.find({short_url: req.params.shorturl}, (err, data) => {
    if (err) return res.json({ error: 'invalid url' });
    return res.redirect(302, data[0].original_url);
  })
})

I’ve allowed myself to add 2 console logs to see what is passed to your app.get.
Then, I manually tried to go to ‘/api/shorturl/12345’ as such entry doesn’t exist in your db.
I got the following error:

req params:
{ shorturl: ‘12345’ }
events.js:291
throw er; // Unhandled ‘error’ event
^

TypeError: Cannot read property ‘original_url’ of undefined
at /home/runner/boilerplate-project-urlshortener/server.js:89:38
at /home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/model.js:5068:18
at processTicksAndRejections (internal/process/task_queues.js:79:11)
Emitted ‘error’ event on Function instance at:
at /home/runner/boilerplate-project-urlshortener/node_modules/mongoose/lib/model.js:5070:15
at processTicksAndRejections (internal/process/task_queues.js:79:11)
[nodemon] app crashed - waiting for file changes before starting…

Your expectations are to see {"error":"invalid url"}.
This will never happen due to how Url.find works. At the moment, your code tells node to return {"error":"invalid url"} IF an error happens while searching the database for req.params.url. However this operation is successful, whether it returns an empty object or not. In case of /api/shorturl/12345 it returns an empty object, and therefore
if (err) return res.json({ error: 'invalid url' });
is skipped. The error actually happens in your return statement because redirect is in fact trying to access an empty object.

To resolve this, you need to check whether or not Url.find() returned an empty object first. If the object is not empty, then you can return the redirect.
This also means, you can use Url.findOne(), rather than Url.find()

Good luck with the challenge

1 Like

Thank you for your feedback, I have added an if statement to check for the data object and the code now redirects the original url as expected. However for some reason I still can’t pass the second and third tests. Is it necessary for the short_url to be a number?

The whole point of these challenges is that you do them yourself, with somewhat limited help. I can’t do this for you.

Asking questions is ok, however, you should make your queries as specific as possible with as much info to work with.

I suggest you take the same trouble shooting steps I did for every test that you can’t pass. Look at the test requirements, see what part of your code should handle that, add console.logs to see that is happening, run some tests and look at the errors.

Yes you are right - sorry I should have been more specific with my previous post.

I did some troubleshooting as you suggested and discovered that I needed to validate the url , in addition to using the dns.lookup for validating the domain part of the url. I have finally managed to pass all the tests. Thank you for your help :grinning_face_with_smiling_eyes:

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.