visit
Note: If this article helped you gain a better understanding of backend routing with Node, please give it a clap 👏 (or 50!) to help spread the word!
Let’s start out by emulating Express’ . We’ll modify it slightly since we won’t be pulling in express
but will rather be pulling in a module we create ourselves.
Verify your package.json
file looks as follows:
Next, we’ll create our index.js
file. In this file we’ll replicate the express
“Hello World” example but pull in our own module (we’ll create this module in short order).
This is essentially the same as the express
“Hello World” example example. Based on this code, we know our router
module should be a function that returns an app
object when called. This object should have a listen
method to start listening for requests on a port and a get
method to set up get
request handling. We’ll also set up a post
method since we’ll ultimately want our app to handle posts.
diy-router
ModuleNow we create the actual router module. Create the diy-router.js
file inside a new src
directory.
Hopefully this all makes sense so far: we created a router
function that, when called, returns a get
and a listen
method. At this point, each method ignores its parameters and simply logs that it has been called. This function is then wrapped in an Immediately Invoked Function Expression (IIFE). If you’re unfamiliar as to why we use an IIFE, we do so for data privacy. This will be a little more obvious is the coming steps when we have variables and functions that we don’t want to expose outside the module itself.
To get some basic HTTP request handling functionality, we bring in node’s built-in http
module to our diy-router
. The http
module has a createServer
method that takes a function with request and response parameters. This function gets executed every time an http request is sent to the port specified in the listen
method. The sample code below shows how the http
module can be used to return the text “Hello World” on port 8080
.
We’ll want to use this kind of functionality it our module, but we need to let the user specify their own port. Additionally, we’ll want to execute a user-supplied callback function. Let’s use this example functionality along with within the listen
method of our diy-router
module and make sure to be more flexible with the port and callback function.
Simple “Hello World!” App Looking good! We’re now serving content over port 3000. This is great, but we’re still not serving route-dependent content. For example, if you navigate to you’ll see the same “Hello World!” message. In any real-world application, we’ll want the content we serve to our user to be dependent on what’s in the provided URL.
We need to be able to add any number of routes to our application and execute the correct route handler function when that route is called. To do this, we’ll add a routes
array to our module. Additionally, we’ll create addRoute
and findRoute
functions. Notionally, the code might look something like this:
We’ll use the addRoute
method from our get
and post
methods. The findRoute
method simply returns the first element in routes
that matches the provided method
and url
.
In the following snippet, we add the array and two functions. Additionally, we modify our get
method and add a post
method, both of which use the addRoute
function to add user-specified routes to the routes
array.
Note: Since the routes
array and the addRoute
and findRoute
methods will only be accessed within the module, we can use our IIFE “revealing module” pattern to not expose them outside the module.
Finally, let’s employ the findRoute
function within the function we’re passing to our createServer
method. When a route is successfully found, we should call the handler function associated with it. If the route isn’t found, we should return a 404 error stating that the route wasn’t found. This code will notionally look like the following:
Now let’s incorporate this into our our module. While we’re at it, we’ll add one extra bit of code that creates a send
method for our response object.
Route not found
Now we want to confirm we can actually add /test-route
as a route in our application. In index.js
, set up this route.
Note: If you’ve had enough fun, you can end here! This was a great primer on routing. If you want to dig a little deeper and be able to extract parameters from ourroutes, read on!
In the real world, we’re likely to have parameters in our url strings. For example, say we have a group of users and want to fetch a user based on a parameter in the url string. Our url string might end up being something like /user/:username
where username
represents a unique identified associated with a user.
To create this function, we could develop some regular expression rules to match any url parameters. Instead of doing this, I’m going to recommend we pull in a great module called route-parser
to do this for us. The route-parser
module creates a new object for each route that has a match
method with all the regular expression magic baked in. To make the required changes in our module, do the following:
At the top of the diy-router.js
file, require the module.
In the addRoute
function, rather than adding the plan url string, add a new instance of the Route
class.
Next, we’ll update the findRoute
function. In this update, we use the Route
object’s match
method to match the provided url with a route string. In other words, navigating to /user/johndoe
will match the route string /user/:username
.
To handle this new functionality, we need to revisit where we call findRoute
in the function we pass to http.createServer
. We’ll want to make sure that any parameters in our route get added as a property on the request object.
Let’s test this out! In our index.js
file, we’ll add a new user endpoint and see if we can toggle between users by changing our url query string. Change you index.js
file as follows. This will filter our user
array based on the params
property of the provided request.