12 apps in 12 weeks - App 1

6 min. read

Why this challenge?

Yes this is a funny challenge. When I got to programming I did Ruby on Rails. Why ? Cause I follow a guys on youtube call Mackenzie Child. He did this channel for learning RoR. The playlist here

So yes I try to make like him, not really for learning but more for push myself. I always want to build something but I never push myself til the end. So this is the opportunity for me to make little app and get stuff to show for portfolio.

The first app

So for the first week, the app I choice was a simple shortened URL. The stack is with Node.JS, MongoDB and jQuery. Nothing too hard so no need to add React on this stack.

If we take the app in part I need to:

  1. Get URL from the user
  2. Make a random smaller URL with an algorithm and be sure this is unique.
  3. Give back to the use the short URL version
  4. Make a route who handle the short URL as a params and redirect the user to this real link.

First thing

I don’t say I have the best strategies here. I just show you what I did and that work :)

The model schema

1
2
3
4
5
6
7
8
const UrlSchema = new Schema({
longUrl: { type: String, unique: true, required: true },
shortUrl: {
type: String,
maxlength: [6, 'Error with the length!'],
unique: true
}
});

So here nothing really amazing. You can see I have put the shortUrl to be maximum 6 character length. The only thing you need to know if the shortUrl gonna be the params now and we redirect the user to the longUrl version who is the real one.

The funny part

Now time to work on the algorithm. I use the [crypto](https://nodejs.org/api/crypto.html) module who is a node api.

1
2
3
4
5
6
7
8
const makeUniqueUrl = len => {
return crypto
.randomBytes(Math.ceil(len * (3 / 4)))
.toString('base64')
.slice(0, len)
.replace(/\+/g, '0')
.replace(/\//g, '0');
};

Nothing awesome, just a good plain base64 random string. The len arguments gonna 6 for my case.

How make sure this is unique ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
UrlSchema.pre('save', true, function(next, done) {
this.shortUrl = makeUniqueUrl(6);
this.longUrl = addHtpp(this.longUrl);
// if we have already this shortUrl we just keep doing until we have a random one
mongoose.models.Url.findOne({ shortUrl: this.shortUrl })
.then(url => {
if (url) {
this.shortUrl = makeUniqueUrl(6);
}
return done();
})
.catch(err => done(err));
next();
});

So I use the pre save method of mongoose and make it build the shortUrl. You can see I make a findOne too for looking if that exist. If yes I rerun it for be sure this is unique. About the addHtpp function who is:

1
2
3
4
5
6
7
8
const addHtpp = url => {
const pattern = /^((http|https|ftp):\/\/)/;
if (!pattern.test(url)) {
url = `http://${url}`; // eslint-disable-line
}
return url;
};

This is for adding http in front of longUrl. Cause I was getting problem with express for redirect user without. I got the redirect always on my domains and this is the hack I found.

The controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
export const createShort = (req, res) => {
let longUrl = req.body.longUrl;
// validation
if (!longUrl) {
return res.status(400).json({ success: false, message: 'You must provided a url!' });
} else if (!isURL(longUrl)) {
return res.status(400).json({ success: false, message: 'You must provided a valid url!' });
}
// if user send url who don't have http we need to match the db
const pattern = /^((http|https|ftp):\/\/)/;
if (!pattern.test(longUrl)) {
longUrl = `http://${longUrl}`; // eslint-disable-line
}
// search if we have already a url save
return Url.findOne({ longUrl })
.then(url => {
// if not we created a new one
if (!url) {
const newUrl = new Url({ longUrl });
return newUrl.save()
.then(u => res.status(201).json({ success: true, url: u }))
.catch(err => {
let errors;
if (err.code === 11000) {
errors = 'This url is already use';
}
return res.status(400).json({ success: false, errors });
});
}
// if yes we return the url
return res.status(200).json({ success: true, url });
})
.catch(err => res.status(400).json({ success: false, message: err }));
};

Nothing magic again here, the only weird thing is the fact than I reuse the addHtpp function. Because I put in the db the one with http I need to match it cause when I user create a shortUrl example with www.facebook.com I need to see if the http version is already there.

1
2
3
4
5
6
7
8
9
10
11
12
export const redirectLong = (req, res) => {
const { shortUrl } = req.params;
return Url.findOne({ shortUrl })
.then(url => {
// if we dont find a url we redirect back to home page
if (!url) { return res.redirect('/').json({ success: false, message: 'This url not exist in the system' }); }
// we redirect to external url
return res.redirect(url.longUrl);
})
.catch(err => res.redirect('/').json({ success: false, message: err }));
};

For the redirectLong action I just push the user with the longUrl coming from the Url find by the params in the url.


All the other code is more design, jQuery etc. Nothing is brand new and surely not the best jQuery code. I didn’t touch it for like 6 months.

Last Word

Hope this blog post help you to see the logic about making this kind of app. I talk here for the beginner one or the one who didn’t really think about make this. Let me know in the comment if you have any suggestions.

You can see the app here and play around with.

The code is here on github.