Routing

Routing

Routing is how the server will handle each request separatly based on link, and the method (GET, POST, PUT, DELETE, ...).

Methods

By convention here is how things should work:

  • GET: used to fetch some data from the server, it send data from link (www.yamicode.com/id/1?title=myttitle) wchich means that the data is exposed.
  • POST: Used to create data (The object to send is sent in the body of the http request which means that it's not exposed in the url).
  • PUT: Like the post method, but it's used for updating data.
  • DELETE: Used to delete some resources.

There a lot of methods beside those 4, but they are the most used. Those methods are just a convention that we aggree on, which means that the implementation depends on you (Even if it's not recommended but for example a get method can delete or insert data depending on the implementation). To know more you can search for REST.

Routing in Node.js

When working with Node.js request parameter, we got access to 3 attributes (url, method and headers), who will help us implement our routing system.

HTTP method

To get the http method (GET, POST, PUT or DELETE), we can use the req.method.

const router = (req, res) => {
    console.log(req.method)
}

Path and URL

To get the path that the user require to access to, we'll use the url module to parse our URL. Example: url=http://localhost:3000/posts?title=hello, path=/posts.

const url = require("url");
const router = (req, res) => {
    let baseURI = url.parse(req.url, true);
    console.log("path", baseURI.pathname);
}

Query parameters

To get the query parameters, we'll use the url module to parse our URL. Example: url=http://localhost:3000/posts?title=hello,id=2, queryParamter={title: "hello", id: 2}.

const url = require("url");
const router = (req, res) => {
    let baseURI = url.parse(req.url, true);
    console.log("path", baseURI.query);
}

Getting data from the body when dealing with POST method

When dealing with body data send by the client, Node.js use what we call Streams and Events (you'll find more about streams and events in the next chapters).

const router = (req, res) => {

    // Variable that holds our chunks of data
    let bodyString = "";

    // Updating our body whenever a new chunk of data comes in
    req.on("data", data => {
        bodyString += data
    })

    // Data received completely
    req.on("end", () => {

        //Parsing our data to get a json object
        let body = JSON.parse(bodyString);

    })

}

Share information between controllers

Since every request has only one req objects shared between those handlers, we can store our data in the request (You'll find this usefull when dealing with Express.js, Authentication, File handling, etc...).

const url = require("url");
const router = (req, res) => {
    let baseURI = url.parse(req.url, true);
    req.queryData = {
        query: baseURI.query,
        baseURI: baseURI.pathname
    }
}

Exception handling

for now we'll just stick with the famous Try, Catch block.

const url = require("url");
const router = (req, res) => {
    try {
        // Some code here
    } catch (error) {
        console.log(error)
        res.writeHead(200, { 'Content-type': 'text/html' });
        res.end("<h1>An error occured</h1>");
    }
}

In Node.js you should never forget to handle exceptions, because exceptions can make your server shutdown if they are not handled.

Routing in Node.js with example

Let's try to create an app that handle inserting and getting posts (every thing is static).

routing in node.js

Routing

In this example we'll have 3 routes: two GET methods (home, getPosts) and one POST method (insertPOST).

let routes = {
    "GET": {
        "/": controller.home,
        "/posts": controller.getPosts
    },
    "POST": {
        "/posts": controller.insertPost
    },
    "notFound": (req, res) => {
        res.writeHead(200, { 'Content-type': 'text/html' });
        res.end("<h1>Not found</h1>");
    }
}

Final implementation

const url = require("url");
const controller = require("./controller")

const router = (req, res) => {
    try {
        // Parsing and saving our url data in the req
        let baseURI = url.parse(req.url, true)
        req.queryData = {
            query: baseURI.query,
            baseURI: baseURI.pathname
        }

        // Searching for routes
        let handler = (routes[req.method]) ? routes[req.method][baseURI.pathname] : null
        if (handler) {
            // Handler of this route found
            handler(req, res)
        } else {
            // No route found matching the url
            routes["notFound"](req, res)
        }
    } catch (error) {
        // In case of error
        console.log(error)
        res.writeHead(200, { 'Content-type': 'text/html' });
        res.end("<h1>An error occured</h1>");
    }

}

// routes in form of: route[method][url]
let routes = {
    "GET": {
        "/": controller.home,
        "/posts": controller.getPosts
    },
    "POST": {
        "/posts": controller.insertPost
    },
    "notFound": (req, res) => {
        res.writeHead(200, { 'Content-type': 'text/html' });
        res.end("<h1>Not found</h1>");
    }
}

module.exports = router;
exports.home = (req, res) => {
    res.writeHead(200, { 'Content-type': 'text/html' });
    res.end("<h1>Hello</h1>");
}

exports.getPosts = (req, res) => {
    let posts = [
        { id: 1, title: "article1" },
        { id: 2, title: "article2" }
    ]
    res.writeHead(200, { 'Content-type': 'application/json' });
    res.end(JSON.stringify(posts));
}

exports.insertPost = (req, res) => {
    let bodyString = ""
    req.on("data", data => {
        bodyString += data
    })

    req.on("end", () => {
        let body = JSON.parse(bodyString);
        let post = {
            id: 1,
            title: body.title
        }
        res.writeHead(200, { 'Content-type': 'application/json' });
        res.end(JSON.stringify(post));
    })
}
const http = require("http");
const router = require("./router/router")

const server = http.createServer(router)
    .listen(3000, () => console.log("Server listening on port 3000"));

Source code

The full implementation of this article can be found in the GitHub project: Node.js learning