JavaScript

Creating a router in JavaScript for Nodejs

Every URL you type on this website are handled through one script. It is a common standard with many framework. The script in question is called a Router. It takes any URL you give it, and returns the proper functions to call to generate the page.

I wrote this framework over 3 years ago and I hardly ever look at the code that runs the framework. Recently however I have been experimenting with Nodejs and I needed a router. I started writing one and got confused quickly.

Since I have a router already on this website, I decided to consult with that one first. I read the code I wrote in PHP and it made very little sense. Well JavaScript and PHP are a little different so to speak. Mainly when it comes to regex.

But don't worry I finally converted the code and have something to share with you.

You don't need nodejs to follow along. The beauty of it is that it is all JavaScript and you can test it right here on the browser. We are only going to write template functions to run when the URL is matched, I will leave the rest of the work to you. So let's get started.

Routes

First of all, we need to have routes. Routes are simple urls that mean something on your website. For example, on this website, I have a homepage, a blog section, and a blog post. Here is how we are going to defined their routes.

var routes = [
    {
        name : "Homepage",
        controller : "homepageAction", 
        pattern: "/"
    },
    {
        name : "Blog",
        controller : "blogpageAction", 
        pattern : "/blog",
    },
    {
        name : "Article",
        controller : "blogarticleAction", 
        pattern: "/blog/{title}"
    }
];

The routes variable is an array that contains meta data for each route. The homepage url is /. If this URL is the one requested, our code will match it and call the correct controller to return the content of the page.

You don't have to write the exact same format I used. There is no standard, you add the information you want on it. For example for my website, I also check the method of request. Some urls only works if they are POST requests.

Routing

We will create a function route that will take a url as a parameter and will return the correct route if it finds anything. For a simple URL like the homepage, we can do a simple === comparison, but we want to make sure our function is flexible enough to handle any random URL.

In the third object, the article route, I have used the pattern /blog/{title}. The brackets mean that we can pass any title there and it will still match as an article route. We can use a simple regex there to match this section. For each request, we will loop through all our list of routes, making sure we stop as soon as we find a match.

So let's see the code and I will explain.

function route(url) {

    var urlParts = url.split("/");
    var param = [];
    var currentRoute = null;

    for(var index = 0,total = routes.length;index<total;index++){
        var match = true;
        var patternSplit = routes[index].pattern.split("/");
        if (urlParts.length === patternSplit.length) {
            for (var i = 0, l = urlParts.length;i<l;i++) {
                var reg = patternSplit[i].match(/{(.*)}/);
                if (reg){
                    param.push(urlParts[i]);
                }else {
                    if (patternSplit[i] !== urlParts[i]) {
                        match = false;
                        break;
                    }
                }
            }
        }else {
            match = false;
        }

        if (match === true){
            currentRoute = routes[index];
            break;
        }
    }

    if (!match) {
        return false;
    }
    return {match:currentRoute,param:param};
}

Yikes... Ok, once I explain it will make more sense. Let's imagine I pass the url "/blog/hello-world" to the function route().

We are splitting the url into parts based off the "/". We also split the pattern of the first route we are looping through. The first thing to do is test if they are both the same length: "/blog/hello-world" and "/blog/{title}" are the same length (both are equal to 2 )allowing us to continue.

Now we try to match each section: "blog" === "blog", so we continue. If they didn't match then we exit the loop. We only continue if there are curly brackets in the string. This way we know that it is a pattern to send to our controller.

Note, inside the first loop, we are setting match to true. This assumes that the pattern matches until proven otherwise. When there is nothing to prove that the url doesn't match, we assign the current route as the successful one, and we pass it the parameters of the url we matched.

This way, from the outside all we have to do is test if it worked then call the appropriate function.

var routes = [....];

function route(url){
   ...
}

var controller = {
    homepageAction: function(){
        console.log("Look at me ma! I'm a homepage.")
    },
    homepageAction: function(){
        console.log("Look at me ma! I'm a blog page")
    },
    homepageAction: function(title){
        console.log("Look at me ma! "+ title)
    }
};


var currentRoute = route(currentURL);

if (!currentRoute) {
    // Throw 404 and exit since there are no matches
}

// We have a match

var controlFunc = controller[currentRoute.match.controller];
var parameters = currentRoute.param;

render(controlFunc.apply(this,parameters));

That's it really. You call the route function and it returns the route for the url.

Enjoy it! As an exercise, create a function that does the opposite. Give it the route name and its parameters and it returns the correct url. This could be very useful to make your urls universal in your App. You don't have to hard code urls ever again.


Comments

There are no comments added yet.

Let's hear your thoughts

For my eyes only