Now we're good with Databases, let's head back to our request and make more sense of it.
Request Handler
Basically what we're using so far is the request
and reply
objects from fastify
to handle our requests. But we can do better than that. We can create our own request handler.
Http Directory
Remember the requests
directory we created? let's rename it to http
, why??? because it will also contain a response handler so we can set both response and request in the same directory.
Request class
Create request.ts
file inside src/core/http
directory and add the following code:
// src/core/http/request.ts
export class Request {
/**
* Fastify Request instance
*/
public request: any;
/**
* Fastify response instance
*/
public response: any;
/**
* Set request instance
*/
public setRequest(request: any) {
this.request = request;
return this;
}
/**
* Set response instance
*/
public setResponse(response: any) {
this.response = response;
return this;
}
}
const request = new Request();
export default request;
We added only two properties and two methods, these properties will contain the current instance of Fastify request and response, and the methods will set the current instance of Fastify request and response.
We also need to add the handler controller
of current route so let's add it as well.
// src/core/http/request.ts
export class Request {
/**
* Fastify Request instance
*/
public request: any;
/**
* Fastify response instance
*/
public response: any;
/**
* Request handler
*/
public handler: any;
/**
* Set request instance
*/
public setRequest(request: any) {
this.request = request;
return this;
}
/**
* Set response instance
*/
public setResponse(response: any) {
this.response = response;
return this;
}
/**
* Set handler instance
*/
public setHandler(handler: any) {
this.handler = handler;
return this;
}
/**
* Execute handler
*/
public async execute() {
return await this.handler(this, this.response);
}
}
const request = new Request();
export default request;
How this will work?
Let's take a step back at the router.scan
method, it will loop through all the routes and register them to Fastify, but it will also set the handler of each route to the request
instance we created.
Now Fastify handler will be the a custom handler of our own which we will create in a bit, that handler will receive the handler of route and return a callback, that callback will receive from Fastify the request and response objects, and we will set them to our request
instance, then we will execute the handler of route and return the response.
But why would we do this? because we'll add more features to the request handler, also more features to the router, for instance we'll add a custom validation to the request handler controller
if we want to validate the request before accessing the controller, also managing request data like casting numbers or booleans.
Router Class
Open src/core/router/index.ts
and update the scan method with the following code:
// src/core/router/index.ts
/**
* Register routes to the server
*/
public scan(server: any) {
this.routes.forEach(route => {
const requestMethod = route.method.toLowerCase();
const requestMethodFunction = server[requestMethod].bind(server);
// 👇🏻 we changed `route.handler` to that new method handleRoute
requestMethodFunction(route.path, this.handleRoute(route));
});
}
/**
* Handle the given route
*/
private handleRoute(route: any) {
return async (request: any, response: any) => {
return await route.handler(request, response);
};
}
We just wrapped our handler in another method, that method will return the same exact response, except we added a little code to make it always async.
Now let's update our route handler to receive our request instance instead of Fastify request and response.
// src/routes/index.ts
import request from "core/http/request";
import { Route } from "./types";
export class Router {
// ...
/**
* Register routes to the server
*/
public scan(server: any) {
this.routes.forEach(route => {
const requestMethod = route.method.toLowerCase();
const requestMethodFunction = server[requestMethod].bind(server);
requestMethodFunction(route.path, this.handleRoute(route));
});
}
/**
* Handle the given route
*/
private handleRoute(route: any) {
return async (fastifyRequest: any, fastifyResponse: any) => {
// update request instance
request
.setRequest(fastifyRequest)
.setResponse(fastifyResponse)
.setHandler(route.handler);
// now execute the handler
return await request.execute();
};
}
}
const router = Router.getInstance();
export default router;
We imported our request, then injected the current request and response handlers of Fastify to our request instance, then we set the handler of current route to our request instance, then we executed the handler and returned the response.
That execute method will call the handler and return the proper response accordingly.
Request Executer
Navigating again to our request, let's create a new method called execute
which will execute the handler of current route.
// src/core/http/request.ts
export class Request {
/**
* Fastify Request instance
*/
public request: any;
/**
* Fastify response instance
*/
public response: any;
/**
* Request handler
*/
public handler: any;
/**
* Set request instance
*/
public setRequest(request: any) {
this.request = request;
return this;
}
/**
* Set response instance
*/
public setResponse(response: any) {
this.response = response;
return this;
}
/**
* Set handler instance
*/
public setHandler(handler: any) {
this.handler = handler;
return this;
}
/**
* Execute handler
*/
public async execute() {
return await this.handler(this, this.response);
}
}
const request = new Request();
export default request;
Now it is perfect, except that the controller now will receive our request instance instead of Fastify request.
This means that now we can add any feature we want to our request instance, and it will be available to the controller.
Conclusion
In this article we created a custom router, and we created a custom request instance, we also updated the router to use our request instance instead of Fastify request and response.
In our next article we'll start add some features to test it against our newly created request handler.
Next Lesson
In the next lesson we'll start working with Database Models
!
🎨 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).
🎞️ Video Course (Arabic Voice)
If you want to learn this course in video format, you can find it on Youtube, the course is in Arabic language.
💰 Bonus Content 💰
You may have a look at these articles, it will definitely boost your knowledge and productivity.
General Topics
- Event Driven Architecture: A Practical Guide in Javascript
- Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript
- After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
Packages & Libraries
- Collections: Your ultimate Javascript Arrays Manager
- Supportive Is: an elegant utility to check types of values in JavaScript
- Localization: An agnostic i18n package to manage localization in your project
React Js Packages
Courses (Articles)
Top comments (0)