URL Shortener Microservice -- Null 'original_url'

Hey everyone!

I am currently working on the URL Shortener project and I am facing some issues.

The code is this:

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

const app = express();
const port = process.env.PORT || 3000;

mongoose.connect('mongodb+srv://nezumi:<password>@freecodecamp.qyx3zqg.mongodb.net/url_project?retryWrites=true&w=majority',
  { useNewUrlParser: true, useUnifiedTopology: true, serverSelectionTimeoutMS: 5000 })
  .catch(error => console.log(error));
mongoose.connection.on('error', console.error.bind(console, 'connection error:'));
mongoose.connection.once('open', () => {
  console.log('Database connection is established.');
})

const Schema = mongoose.Schema;

const urls_schema = new Schema({
  original_url: String,
  short_url: String,
});

const ShortURL = mongoose.model('Url', urls_schema)

app.use(cors());
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use('/public', express.static(`${process.cwd()}/public`));

// URL validity function
function checkHttpUrl(string) {
  let url;
  try {
    url = new URL(string);
  } catch (error) {
    console.log('URL object error');
    return false;
  }
  return url.protocol == "http:" || url.protocol == 'https:'
};

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

app.get('/api/shorturl/:short_url', async (req, res) => {
  try {
    const urlParam = await ShortURL.findOne({
      short_url: req.params.short_url
    });
    if (urlParam) {
      res.redirect(urlParam.original_url);
    } else {
      res.status(404).json('URL not found.');
    }
  } catch (err) {
    console.log(err);
    res.status(500).json('Server Error -- GET');
  }
});

app.post('/api/shorturl', async (req, res) => {
  const url = req.body.url;
  if (!checkHttpUrl(url)) {
    // Bad Request - client error
    res.status(400).json({
      error: 'invalid url'
    });
  }
  else {
    try {
      let findOne = await ShortURL.findOne({
        original_url: url
      });
      if (!findOne) {
        const urlShort = shortId.generate();
        let findOne = new ShortURL({
          original_url: url,
          short_url: urlShort
        });
        findOne.save((err, doc) => {
          if (err) console.log(err);
          console.log('Document was uploaded successfully!', findOne);
        });
      };
      res.json({
        original_url: findOne.original_url,
        short_url: findOne.short_url
      });
    } catch (err) {
      console.log(err);
      res.status(500).json('Server Error -- Post');
    }
  }
});

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

Even though I successfully write in my database, I get the following error:

TypeError: Cannot read properties of null (reading 'original_url')

in the POST method, after I write in the database, when I send the JSON response:

res.json({
        original_url: findOne.original_url,
        short_url: findOne.short_url
      });

Because I can write in the database, if I try to give the same url again I will get the correct result, and also the redirections work just fine.

Does anyone have an idea on why this is happening?



Link to the challenge:

Make sure you understand that error. It is saying “whatever thing that you think is an object with the property of original_url is really null” Trying to read a property or method of null or undefined is a sin in JS.

So, in your example code, findOne is null. But I’d check anywhere you try to access that property.

One thing that catches my eye is that you have three different things called findOne. There is the method and two variables called that. One here:

      let findOne = await ShortURL.findOne({
        original_url: url
      });

and one here:

        let findOne = new ShortURL({
          original_url: url,
          short_url: urlShort
        });

Those last two are “ghosting” - they are in overlapping scopes, generally a bad thing. When you use:

      res.json({
        original_url: findOne.original_url,
        short_url: findOne.short_url
      });

Which one do you think it is accessing?

1 Like

Thank you, you are right, I hadn’t noticed I was declaring the second one as a new variable.

I would consider renaming those - variable naming is one of the most important things and often gets ignored.