So our last article was about creating the Fastify server and run it, now we will create the router handler for our server.
But keep your eyes open, because we're going to create our own router layer over the fastify router.
Updating the server call
As i reviewed the Fastify documentation, i found that we can enhance our code a little bit like the listen
method returns the url of the server so we can use.
// src/server.ts
import Fastify from "fastify";
const server = Fastify();
server.get("/", (request, response) => {
response.send("Hello World!");
});
async function start() {
// we w'll wrap the server in a try catch block so if there is any error
// we can catch it and log it
try {
// 👇🏻 We can use the url of the server
const address = await server.listen({ port: 3000 });
console.log(`Start browsing using ${address}`);
} catch (err) {
server.log.error(err);
process.exit(1);
}
}
start();
That's no more, now let's create our router layer.
Creating the router layer
Let's create src/core/router
folder and create index.ts
file inside it.
// src/core/router/index.ts
export default class Router {
}
Simple and easy, now let's describe what this router is going to do exactly.
Router Features
The main purpose for this router is to make it easier to call and organize our routes, so we will create a method for each HTTP method and store all of these routes in an array.
// src/core/router/index.ts
export default class Router {
// this were we will store all of our routes
private routes: any[] = [];
/**
* Add GET route
*/
public get(path: string, handler: any) {
this.routes.push({
method: "GET",
path,
handler,
});
return this;
}
}
So based on our previous code, we created an array that will contain all of our registered routes, and we created a method for each HTTP method, and we return this
so we can chain the methods.
We can also define a type for our routes, let's do it.
// src/core/router/types.ts
export type Route = {
method: string;
path: string;
handler: any;
};
// src/core/router/index.ts
import { Route } from "./types";
export default class Router {
private routes: Route[] = [];
public get(path: string, handler: any) {
this.routes.push({
method: "GET",
path,
handler,
});
return this;
}
}
Singleton Pattern
Now we have our router, but we need to make sure that we can only have one instance of it, so we will use the singleton pattern.
// src/core/router/index.ts
import { Route } from "./types";
export default class Router {
// 👇🏻 We will use this to store the instance (object) of the router
private static instance: Router;
/**
* Registered routes list
*/
private routes: Route[] = [];
// 👇🏻 Notice the constructor here is private
private constructor() {
//
}
/**
* Get router instance
*/
public static getInstance() {
if (!Router.instance) {
Router.instance = new Router();
}
return Router.instance;
}
/**
* Add new GET route
*/
public get(path: string, handler: any) {
this.routes.push({
method: "GET",
path,
handler,
});
return this;
}
}
What we did here is that we created a static method that will return the instance of the router, and if there is no instance, it will create one.
Keep in mind that the main key of singletons is to make the constructor private
, so we can't create new instances of the router, that's why we're using the static method.
Router Scanner
Now we're ready for our first step, let's give it a try.
// src/index.ts
import Router from "./core/router";
import Fastify from "fastify";
const server = Fastify();
const router = Router.getInstance();
router.get('/', (request, response) => {
return { hello: 'world' };
});
async function start() {
router.scan(server);
try {
const address = await server.listen({ port: 3000 });
console.log(`Start browsing using ${address}`);
} catch (err) {
server.log.error(err);
process.exit(1);
}
}
start();
So all what we did here is we imported the router, we replaced the server.get with router.get
and we called the scan
method in the start method, now let's define it in our router class.
// src/core/router/index.ts
import { Route } from "./types";
export default class Router {
private static instance: Router;
private routes: Route[] = [];
private constructor() {
//
}
public static getInstance() {
if (!Router.instance) {
Router.instance = new Router();
}
return Router.instance;
}
public get(path: string, handler: any) {
this.routes.push({
method: "GET",
path,
handler,
});
return this;
}
/**
* Scan all routes and register them in the server
*/
public scan(server: any) {
this.routes.forEach((route) => {
const requestMethod = route.method.toLowerCase();
server[requestMethod](route.path, route.handler);
});
}
}
So what we did here is we looped over all of our routes and we got the route request method and used it to call the server method, and we passed the path and the handler.
Now let's run the server again, it should be working just fine.
Conclusion
In this article, we learned how to create a router layer for our server, and we learned how to use the singleton pattern to make sure that we can only have one instance of the router.
🎨 Project Repository
You can find the latest updates of this project on Github
😍 Join our community
Join our community on Discord to get help and support (Node Js 2023 Channel).
Top comments (0)