URL Shortener Microservice - Please help

Hi campers,

I have an issue with this challenge.

Here is the code that I am working with

'use strict';

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

var cors = require('cors');
var bodyParser = require('body-parser');

var urlHandler = require('./controllers/urlHandler.js');

var app = express();

var mongoURL = process.env.MONGOLAB_URI;
var port = process.env.PORT || 3000;

mongoose.connect(mongoURL);

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

app.use('/public', express.static(process.cwd() + '/public'));

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

app.post('/api/shorturl/new', urlHandler.addUrl);
  
app.get('/api/shorturl/:shurl', urlHandler.processShortUrl);


app.use(function(req, res, next){
  res.status(404);
  res.type('txt').send('Not found');
});


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

… and in my .env I have following

MONGOLAB_URI='mongodb://admin:admin1@ds139950.mlab.com:39950/short-url'

Here is my package.json ; just in case if you need it.

{
  "name": "shorturl",
  "version": "0.0.1",
  "description": "API project for freeCodeCamp",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^5.0.0-alpha.2",
    "mongodb": "^3.0.8",
    "mongoose": "^5.1.3",
    "cors": "^2.8.4",
    "body-parser": "^1.18.3",
    "node": "^10.3.0"
  },
  "engines": {
    "node": "8.11.2"
  },
  "repository": {
    "type": "git",
    "url": "https://hyperdev.com/#!/project/welcome-project"
  },
  "keywords": [
    "node",
    "hyperdev",
    "express"
  ],
  "license": "MIT"
}

Can someone, please, help me get over this hurdle. I have been working on this project for DAYS and I can not figure out what am I doing wrong with following all the tutorials and everything that I was able to find online.

Thank you in advance for any help.

Hi,

First off, we need the file ./controllers/urlHandler.js.

Also, it would save us time if you told us what the problem is.

So anyway, I try to fire it up to see what I can see. It doesn’t like the node engine you unnecessarily specify in the package.json so I remove it.

The first error I get in the terminal is:

Node.js listening ...
(node:4452) UnhandledPromiseRejectionWarning: Error: URL malformed, cannot be parsed
    at module.exports (/home/ksjazzguitar/programming/fcc01/node_modules/mongodb/lib/url_parser.js:17:21)
    at connect (/home/ksjazzguitar/programming/fcc01/node_modules/mongodb/lib/mongo_client.js:880:3)
    at connectOp (/home/ksjazzguitar/programming/fcc01/node_modules/mongodb/lib/mongo_client.js:269:3)
    at executeOperation (/home/ksjazzguitar/programming/fcc01/node_modules/mongodb/lib/utils.js:420:24)
    at MongoClient.connect (/home/ksjazzguitar/programming/fcc01/node_modules/mongodb/lib/mongo_client.js:260:10)
    at Promise (/home/ksjazzguitar/programming/fcc01/node_modules/mongoose/lib/connection.js:427:12)
    at new Promise (<anonymous>)
    at NativeConnection.Connection.openUri (/home/ksjazzguitar/programming/fcc01/node_modules/mongoose/lib/connection.js:424:19)
    at Mongoose.connect (/home/ksjazzguitar/programming/fcc01/node_modules/mongoose/lib/index.js:208:15)
    at Object.<anonymous> (/home/ksjazzguitar/programming/fcc01/server.js:17:10)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
(node:4452) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:4452) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

It sounds to me like mLab is rejecting your URI.

OK, that is because of your .env file. Using a .env file is a great idea, but you need to intstall the dontenv package, like npm install dotenv --save. Then somewhere near the top of your server.js file you need require('dotenv').config(). Then node will look in that file for those constants. As it was, your mongodb url was undefined so you were crashing for that. I just cut and pasted your URL into the browser so I can test it quickly. Now the server starts up without an error.

If your issue is something else, please be specific and provide all of the code.

Don’t get frustrated. I don’t know about others, but I was a semi-experienced coder that sailed through everything else and then got to the backend and just stalled. It is a bit thing to wrap your head around. It’s very confusing. It’s a big logical leap from what we were doing before. But I got it. It was about 75% into the backend before it started to click. And it was a few more months after I finished the backend section before it completely clicked.

Just stick with it.

1 Like

Hi @kevinSmith ,

Thank you very much for your response.

I have tried what you have advised me but it is not working … here is where I have put the line that you have suggested in my server.js file

'use strict';

var express = require('express');
var mongo = require('mongodb');
var mongoose = require('mongoose');
var cors = require('cors');
var bodyParser = require('body-parser');
var urlHandler = require('./controllers/urlHandler.js');
var app = express();

// Basic Configuration for Heroku
var mongoURL = process.env.MONGOLAB_URI;
var port = process.env.PORT || 3000;

mongoose.connect(mongoURL);

**require('dotenv').config();**

Also, you have asked me to put urlHandler file so here it is …

'use strict';

var Counters = require('../models/counters.js');
var UrlEntries = require('../models/urlEntries.js');
var dns = require('dns');



function getCountAndIncrease (req, res, callback) {
  Counters
    .findOneAndUpdate({}, {$inc:{'count': 1}},function(err, data) {
      if (err) return;
      if (data) {
        callback(data.count);
      } else {
        var newCounter = new Counters();
        newCounter
          .save(function(err) {
            if (err) return;
            Counters
              .findOneAndUpdate({}, {$inc:{'count': 1}},function(err, data) {
                if (err) return;
                callback(data.count);
              });
          });
      }
    });
}



// Search for '://', store protocol and hostname+path
var protocolRegExp = /^https?:\/\/(.*)/i;

// Search for patterns like xxxx.xxxx.xxxx etc.
var hostnameRegExp = /^([a-z0-9\-_]+\.)+[a-z0-9\-_]+/i;

exports.addUrl = function (req, res) {
  
    var url = req.body.url;
    
    // "www.example.com/test/" and "www.example.com/test" are the same URL
    if ( url.match(/\/$/i))
      url = url.slice(0,-1);
    
    var protocolMatch = url.match(protocolRegExp);
    if (!protocolMatch) {
      return res.json({"error": "invalid URL"});
    }
    
    // remove temporarily the protocol, for dns lookup
    var hostAndQuery = protocolMatch[1];

    // Here we have a URL w/out protocol
    // DNS lookup: validate hostname
    var hostnameMatch = hostAndQuery.match(hostnameRegExp);
    if (hostnameMatch) {
      // the URL has a valid www.whaterver.com[/something-optional] format
      dns.lookup(hostnameMatch[0], function(err) {
        if(err) {
          // no DNS match, invalid Hostname, the URL won't be stored
          res.json({"error": "invalid Hostname"});
        } else {
          // URL is OK, check if it's already stored
          UrlEntries
            .findOne({"url": url}, function(err, storedUrl) {
              if (err) return;
              if (storedUrl) {
                // URL is already in the DB, return the matched one
                res.json({"original_url": url, "short_url": storedUrl.index});
              } else {
                // Increase Counter and store the new URL,
                getCountAndIncrease(req, res, function(cnt) {
                  var newUrlEntry = new UrlEntries({
                    'url': url,
                    'index': cnt
                  });
                  // then return the stored data.
                  newUrlEntry
                  .save(function(err) {
                    if (err) return;
                    res.json({"original_url": url, "short_url": cnt});
                  });
                });
              }
            });
          }
        });
      } else {
        // the URL has not a www.whatever.com format
        res.json({"error": "invalid URL"});
      }
    };

exports.processShortUrl = function (req, res) {
    var shurl = req.params.shurl;
    if (!parseInt(shurl,10)) {
      // The short URL identifier is not a number
      res.json({"error":"Wrong Format"});
      return;
    }
    UrlEntries
      .findOne({"index": shurl}, function (err, data) {
        if (err) return;
        if (data){
          // redirect to the stored page
          res.redirect(data.url);
        } else {
          res.json({"error":"No short url found for given input"});
        }
      });
  };

If you can, please, let me know what else I can do, I would greatly appreciate.

I am not getting frustrated with but I just want to get it done and I am not sure where else to look for fixation.

Thank you, again, for your assistance.

If you want, I can pair program with you tomorrow. We can try out the Live Share in VS Code.

1 Like

@imcodingideas

Thank you !

If you can, I would greatly appreciate.

I am one of those people where I would love to learn and get stuff going and get it done correctly.

Shoot me a PM with your contact info and I’ll help you.

The problem is that this line:

var mongoURL = process.env.MONGOLAB_URI;

is before you require the dotenv package. the object process.env already exists, but it is calling the statement require('dotenv').config(); after it’s trying to access process.env.MONGOLAB_URI - but that variables hasn’t been pulled from the .env file yet. Move require('dotenv').config() to above anything that accesses process.env. You can put it as line 2.

Also, just as a basic debugging skill, you could have put a console.log(process.env.MONGOLAB_URI) just to see what it was. That’s how I found it was a problem.

Also, you still haven’t actually confirmed to use what your problem is.

@kevinSmith

I have tried your advice and it is still not working

The error I am getting is following …

module.js:549

    throw err;

    ^


Error: Cannot find module 'mongodb-core'

    at Function.Module._resolveFilename (module.js:547:15)

    at Function.Module._load (module.js:474:25)

    at Module.require (module.js:596:17)

    at require (internal/module.js:11:18)

    at Object.<anonymous> (/rbd/pnpm-volume/0ab1f909-e174-47e1-b90a-cf2b31d2aa64/node_modules/.registry.npmjs.org/mongodb/3.0.9/node_modules/mongodb/index.js:4:14)

    at Module._compile (module.js:652:30)

    at Object.Module._extensions..js (module.js:663:10)

    at Module.load (module.js:565:32)

    at tryModuleLoad (module.js:505:12)

    at Function.Module._load (module.js:497:3)

The problem is it is not running at all ; it is not loading up

OK, this is useful information.

It says, Cannot find module 'mongodb-core' - so when it’s loading up, it’s looking for a module and can’t find it. The first thing I’d ask is if you’ve installed mongo on your system? Did you go to the mongo web page and install it? Have you successfully started mongo already?

If not, it sounds like it could be a package that something is trying load but can’t be found. If mongo works, then I might delete the node_modules folder and do an npm install again just to make sure everything is right. If not, you might do npm install --save mongodb-core just to see if that works.

One easy way to check if mongo is installed is this in the command line is:

mongod --version

I’ve been doing raw coding on glitch and I do have mongo installed as package there.

"dependencies": {
    "express": "^5.0.0-alpha.2",
    "mongodb": "^3.0.9",
    "mongoose": "^5.1.3",
    "cors": "^2.8.4",
    "body-parser": "^1.18.3",
    "node": "^10.3.0",
    "dotenv": "^5.0.1"

I am not sure if that’s what you are referring. I am still learning this whole back-end stuff so I might sound like a newbie.

I’m not familiar with glitch. And it just occured to me that you are trying to access through the mlab server so it doesn’t matter whether it’s installed locally or not. I can get it running fine. Here is my server.js:

'use strict';

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

var cors = require('cors');
var bodyParser = require('body-parser');

// var urlHandler = require('./controllers/urlHandler.js');

var app = express();

// var mongoURL = process.env.MONGOLAB_URI;

var mongoURL = "mongodb://admin:admin1@ds139950.mlab.com:39950/short-url"
console.log('just to confirm, here is my mongoURL  ***' + mongoURL + '***')

var port = process.env.PORT || 3000;

mongoose.connect(mongoURL);

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

app.use('/public', express.static(process.cwd() + '/public'));

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

// app.post('/api/shorturl/new', urlHandler.addUrl);
// app.get('/api/shorturl/:shurl', urlHandler.processShortUrl);

app.use(function(req, res, next){
  res.status(404);
  res.type('txt').send('Not found');
});

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

I’ve commented out the unneeded stuff and hardcoded the url into it. When I run into confusion, my first instinct is to simplify things to the point that they work.

This is my package.json:

{
  "name": "shorturl",
  "version": "0.0.1",
  "description": "API project for freeCodeCamp",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^5.0.0-alpha.2",
    "mongodb": "^3.0.8",
    "mongoose": "^5.1.3",
    "cors": "^2.8.4",
    "body-parser": "^1.18.3",
    "node": "^10.3.0"
  },
  "repository": {
    "type": "git",
    "url": "https://hyperdev.com/#!/project/welcome-project"
  },
  "keywords": [
    "node",
    "hyperdev",
    "express"
  ],
  "license": "MIT"
}

But if that doesn’t work, then I think it is something specific to glitch.

Again, I’m not familiar with glitch. Do you have a command line where you enter commands and do npm or yarn commands?

@kevinSmith

I thought we were suppose to put our projects on glitch :confused:

When I did it, we didn’t. I haven’t used glitch. Maybe it’s required in the new program.

Again, have you run mongo on this before?

This is what it says

Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicly visible for our testing.

Glitch does have a command line console. Click Logs > Console to open it.

1 Like

$ mongod --version
db version v2.6.10
2018-06-02T06:29:34.812+0000 git version: nogitversion
2018-06-02T06:29:34.812+0000 OpenSSL version: OpenSSL 1.0.2g 1 Mar 2016