CacheLane Logo

Chapter 06

Improving the Router API

The utility method on the Router class - addRoute is a bit too verbose. You need to specify the HTTP method as a string. It would get tedious when there are suppose hundreds of API routes in an application. Also, devs might not know whether the HTTP methods should be sent in lower-case or upper-case without looking at the source.

Let's abstract that functionality away from the developer, making sure the developers only need to worry about the important pieces.

Current way to add routes:

// file: index.js

class Router {
    constructor() {
        this.routes = {};
    }

    addRoute(method, path, handler) {
        this.routes[`${method} ${path}`] = handler;
    }
    ...
}

Let's add two new methods named get and post, and add some type checks in the addRoute method:

// file: index.js

const HTTP_METHODS = {
    GET: "GET",
    POST: "POST",
    PUT: "PUT",
    DELETE: "DELETE",
    PATCH: "PATCH",
    HEAD: "HEAD",
    OPTIONS: "OPTIONS",
    CONNECT: "CONNECT",
    TRACE: "TRACE",
};

class Router {
    constructor() {
        this.routes = {}
    }

    #addRoute(method, path, handler) {
        if (typeof path !== "string" || typeof handler !== "function") {
            throw new Error("Invalid argument types: path must be a string and handler must be a function");
        }

        this.routes.set(`${method} ${path}`, handler);
    }

    get(path, handler) {
        this.#addRoute(HTTP_METHODS.GET, path, handler);
    }
    post(path, handler) {
        this.#addRoute(HTTP_METHODS.POST, path, handler);
    }
    put(path, handler) {
        this.#addRoute(HTTP_METHODS.PUT, path, handler);
    }

    delete(path, handler) {
        this.#addRoute(HTTP_METHODS.DELETE, path, handler);
    }

    patch(path, handler) {
        this.#addRoute(HTTP_METHODS.PATCH, path, handler);
    }

    head(path, handler) {
        this.#addRoute(HTTP_METHODS.HEAD, path, handler);
    }

    options(path, handler) {
        this.#addRoute(HTTP_METHODS.OPTIONS, path, handler);
    }

    connect(path, handler) {
        this.#addRoute(HTTP_METHODS.CONNECT, path, handler);
    }

    trace(path, handler) {
        this.#addRoute(HTTP_METHODS.TRACE, path, handler);
    }

    ...
}

Let's go through the new additions in our code:

get(path, handler) {
    this.#addRoute(HTTP_METHODS.GET, path, handler);
}

post(path, handler) {
    this.#addRoute(HTTP_METHODS.POST, path, handler);
}
/** rest HTTP method handlers **/

We've created new utility methods on the Router class. Each one of these methods call the addRoute method by passing in required parameters. You'd notice that we've also made the addRoute method private. Since we wish to use it internally in our library and not expose it, it's a good practice to hide it from any external use.

const HTTP_METHODS = { ... }

We've created an object of all the HTTP methods, so that we can use their names with the HTTP_METHODS namespace, instead of directly passing in strings as an argument, for example:

this.#addRoute("GET", path, handler);

There's nothing wrong with this approach too, but I prefer to avoid raw strings. "GET" can mean many things, but HTTP_METHODS.GET gives us the actual idea of what it is all about.

Let's update our testing code to call the newly created http methods instead:

// file: index.js

...

router.get("/", function handleGetBasePath(req, res) {
    console.log("Hello from GET /");
    res.end();
});

router.post("/", function handlePostBasePath(req, res) {
    console.log("Hello from POST /");
    res.end()
});

...

If we do a quick test on both the endpoints, every thing seems to be working alright:

$ curl -X POST http://localhost:5255/ -v
## Success

$ curl -X POST http://localhost:5255/foo -v
## Not found

$ curl -X POST http://localhost:5255/foo/bar -v
## Not found

$ curl http://localhost:5255/ -v
## Success

$ curl http://localhost:5255/foo -v
## Not found

$ curl http://localhost:5255/foo -v
## Not found

Great! This looks much better than the previous implementation.

Previous
The Router class