56-Nodejs Course 2023: Creating our own Response Class Layer
Tags: node, typescript, mongodb, fastify
Published: 2022-11-10 15:43:20

---
title: 56-Nodejs Course 2023: Creating our own Response Class Layer
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yn6cjtmm4z056kh856vu.png
# Use a ratio of 100:42 for best results.
---

So we have updated our Request class in our previous article to be more organized and we also implemented the request events as well, we need to implement now the response events but there will be a single issue here, that the developer (us or anyone else uses this code) can use the `Fastify` response object to send the response directly,and does not return anything in the handler.

So we need to stop that from happening, we also need to strict the handler to always return a value, either an object or an array or the our response class.

But let's first create it.

## Response Class

Go to `src/core/http` and create `response.ts` file and add the following code:

```ts
// src/core/http/response.ts

import { FastifyReply } from 'fastify';

export class Response {
  /**
   * Fastify response object
   */
  protected baseResponse: FastifyReply;
}

const response = new Response();

export default response;
```

As usual, we defined a class and exported it, also we created a new instance for it to be used directly in anywhere in our application.

Now let's add a method that can receive the `FastifyReply` object and set it to our class property.

```ts
// src/core/http/response.ts
import { FastifyReply } from "fastify";

export class Response {
  /**
   * Fastify response object
   */
  protected baseResponse!: FastifyReply;

  /**
   * Set the Fastify response object
   */
  setResponse(response: FastifyReply) {
    this.baseResponse = response;

    return this;
  }
}

const response = new Response();

export default response;
```

We defined a method that receives Fastify response object and return this for `chaining`.

Now let's update the request class to use the response class.

```ts
// src/core/http/request.ts
import response, { Response } from "./response";
import { FastifyReply, FastifyRequest } from "fastify";

export class Request {
  // ...
  
  /**
   * Response Object
   * Replace the current one with this
   */
  protected response: Response = response;

  /**
   * Set Fastify response
   */
  public setResponse(response: FastifyReply) {
    this.response.setResponse(response);

    return this;
  }
```

Here we replaced the fastify response object with our response class, and we also updated the `setResponse` method to use the `setResponse` method in our response class.

## Response Methods

Now, let's see what methods and properties we need to implement in our response class.

* `status`: To set the status code.
* `setContentType`: To set the content type.
* `send`: To send the response.
* `sendFile`: To send a file as a response.
* `success`: To send a success response.
* `successCreate`: To send a success response with status code 201.
* `badRequest`: To send a bad request response with status code 400.
* `notFound`: To send a not found response with status code 404.
* `unauthorized`: To send an unauthorized response with status code 401.
* `forbidden`: To send a forbidden response with status code 403.
* `serverError`: To send an error response with status code 500.
* `header`: To set the response header.
* `headers`: to set multiple headers.
* `getHeaders`: To get the response headers.
* `getHeader`: To get a specific header.
* `removeHeader`: To remove a specific header.
* `getResponseTime`: To get the response time.
* `redirect`: To redirect the user to another route.
* `on`: To listen to a specific event.
* `trigger`: To trigger a specific event (protected method).

Too many methods right? But useful onces and we will implement them one by one.

Now let's define the public properties aka the getters that we'll allow:

* `statusCode`: To get the status code.
* `contentType`: To get the content type.
* `sent`: To check if the response is sent or not.

Now let's implement all off them.

### status

```ts
// src/core/http/response.ts
import { FastifyReply } from "fastify";

export class Response {
  // ...

  /**
   * Set the status code
   */
  public status(statusCode: number) {
    this.baseResponse.status(statusCode);

    return this;
  }
}
```

### setContentType

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Set the content type
   */
  public setContentType(contentType: string) {
    this.baseResponse.header("Content-Type", contentType);

    return this;
  }
}
```

### send

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Send the response
   */
  public send(data: any, statusCode = 200) {
    this.baseResponse.status(statusCode).send(data);

    return this;
  }
}
```

### sendFile

```ts
// src/core/http/response.ts
import fs from "fs";

export class Response {
  // ...

  /**
   * Send a file as a response
   */
  public sendFile(path: string) {
    if (! fs.existsSync(path)) {
      throw new Error(`Response Send Failed:  File not found: ${path}`);
    }

    const fileContent = fs.readFileSync(path);

    this.baseResponse.send(fileContent);

    return this;
  }
}
```

### success

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Send a success response
   */
  public success(data: any) {
    return this.send(data);
  }
}
```

### successCreate

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Send a success response with status code 201
   */
  public successCreate(data: any) {
    return this.send(data, 201);
  }
}
```

### badRequest

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Send a bad request response with status code 400
   */
  public badRequest(data: any) {
    return this.send(data, 400);
  }
}
```

### notFound

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Send a not found response with status code 404
   */
  public notFound(data: any) {
    return this.send(data, 404);
  }
}
```

### unauthorized

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Send an unauthorized response with status code 401
   */
  public unauthorized(data: any) {
    return this.send(data, 401);
  }
}
```

### forbidden

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Send a forbidden response with status code 403
   */
  public forbidden(data: any) {
    return this.send(data, 403);
  }
}
```

### serverError

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Send an error response with status code 500
   */
  public serverError(data: any) {
    return this.send(data, 500);
  }
}
```

### header

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Set the response header
   */
  public header(key: string, value: string) {
    this.baseResponse.header(key, value);

    return this;
  }
}
```

### headers

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Set multiple headers
   */
  public headers(headers: Record<string, string>) {
    this.baseResponse.headers(headers);

    return this;
  }
}
```

### getHeaders

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Get the response headers
   */
  public getHeaders() {
    return this.baseResponse.getHeaders();
  }
}
```

### getHeader

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Get a specific header
   */
  public getHeader(key: string) {
    return this.baseResponse.getHeader(key);
  }
}
```

### removeHeader

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Remove a specific header
   */
  public removeHeader(key: string) {
    this.baseResponse.removeHeader(key);

    return this;
  }
}
```

### getResponseTime

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Get the response time
   */
  public getResponseTime() {
    return this.baseResponse.getResponseTime();
  }
}
```

### redirect

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Redirect the user to another route
   */
  public redirect(url: string, statusCode = 302) {
    this.baseResponse.redirect(statusCode, url);

    return this;
  }
}
```

### statusCode

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Get the status code
   */
  public get statusCode() {
    return this.baseResponse.statusCode;
  }
}
```

### contentType

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Get the content type
   */
  public get contentType() {
    return this.baseResponse.getHeader("Content-Type");
  }
}
```

## sent

```ts
// src/core/http/response.ts

export class Response {
  // ...

  /**
   * Check if the response has been sent
   */
  public get sent() {
    return this.baseResponse.sent;
  }
}
```

We've now updated our file with mostly all the methods we need to work with the response. We can now use these methods in our controller to send a response to the user.

Now we need to update the route's handler type to receive the response as second argument and the middleware as well, also the custom validation and make sure to return a proper response.

But first let's define a proper response type for our API.

```ts
// src/core/http/types.ts


/**
 * Allowed response type
 */
export type ReturnedResponse =
  /**
   * Can be a response object
   */
  | Response
  /**
   * Or a promise returning a response object
   */
  | Promise<Response>
  /**
   * Or an object
   */
  | Record<string, any>
  /**
   * Or a promise returning an object
   */
  | Promise<Record<string, any>>
  /**
   * Or an array
   */
  | any[]
  /**
   * Or a promise returning an array
   */
  | Promise<any[]>;
```

Here we allowed three types of response, our response object, or a plain object or an array or a promise returning any of these types, thus the handler can not return anything else.

Now let's update the route's handler type.

```ts
// src/core/router/types.ts
import { Request } from "core/http/request";
import { Response } from "core/http/response";
import { ReturnedResponse } from "core/http/types";

/**
 * Middleware method
 * Receives the request and response objects
 * And returns a response object or undefined if the request should continue
 */
export type Middleware = (
  request: Request,
  response: Response,
) => ReturnedResponse | undefined;

/**
 * Route handler receives a request and a response
 * And returns a returning response type
 */
export type RouteHandler = {
  /**
   * Function Declaration
   */
  (request: Request, response: Response): ReturnedResponse;

  /**
   * Validation static object property which can be optional
   */
  validation?: {
    /**
     * Validation rules
     */
    rules?: Record<string, any>;
    /**
     * Validation custom message
     */
    validate?: Middleware;
  };
}

export type RouteOptions = {
  /**
   * Route middleware
   */
  middleware?: Middleware[];
  /**
   * Route name
   */
  name?: string;
};

/**
 * Route Object
 */
export type Route = RouteOptions & {
  /**
   * Route method
   */
  method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
  /**
   * Route path
   */
  path: string;
  /**
   * Route handler
   */
  handler: RouteHandler;
};
```

We defined a new type `RouteHandler` as with a function declaration and a static property `validation` which is optional and can be an object with two properties `rules` and `validate` which are also optional.

You will notice that the `RouteHandler` almost identical to the `Middleware` type, the only difference is that the `Middleware` returns a `ReturnedResponse` or `undefined` while the `RouteHandler` returns a `ReturnedResponse` only.

Now go to the request class and hover on the handler or the middleware, you'll see now its fully typed!!

Now let's update the route methods to accept RouteHandler as the handler as type.

```ts
// src/core/router/index.ts

import { Route, RouteHandler, RouteOptions } from "./types";

export class Router {
  // ...
  /**
   * Add get request method
   */
  public get(path: string, handler: RouteHandler, options: RouteOptions = {}) {
    this.routes.push({
      method: "GET",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Add post request method
   */
  public post(path: string, handler: RouteHandler, options: RouteOptions = {}) {
    this.routes.push({
      method: "POST",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Add put request method
   */
  public put(path: string, handler: RouteHandler, options: RouteOptions = {}) {
    this.routes.push({
      method: "PUT",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Add delete request method
   */
  public delete(
    path: string,
    handler: RouteHandler,
    options: RouteOptions = {},
  ) {
    this.routes.push({
      method: "DELETE",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Add patch request method
   */
  public patch(
    path: string,
    handler: RouteHandler,
    options: RouteOptions = {},
  ) {
    this.routes.push({
      method: "PATCH",
      path,
      handler,
      ...options,
    });

    return this;
  }
}
```

We updated the route methods to accept `RouteHandler` as the handler type.

Now our code is ready to be used, our `Response class` is now more powerful and can adapt the events architecture, this is what we'll do in our next article.

## 🎨 Conclusion

In this article we've created our `Response class` to manage how the response will be sent, we've also updated the route's handler type to receive the response as second argument and the middleware as well, also the custom validation and make sure to return a proper response.

## 🚀 Project Repository

You can find the latest updates of this project on [Github](https://github.com/hassanzohdy/nodejs-2023)

## 😍 Join our community

Join our community on [Discord](https://discord.gg/pb2vmdfhGf) 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](https://www.youtube.com/playlist?list=PLGO8ntvxgiZMJc7RN2lIq9WmMOlWZGzmz), 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](https://dev.to/hassanzohdy/event-driven-architecture-the-best-paradigm-that-i-love-to-work-with-in-javascript-and-node-js-1gnk)
* [Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript](https://dev.to/hassanzohdy/best-practices-for-case-styles-camel-pascal-snake-and-kebab-case-in-node-and-javascript-55oi)
* [After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
](https://dev.to/hassanzohdy/after-6-years-of-practicing-mongodb-here-are-my-thoughts-on-mongodb-vs-mysql-574b)

Packages & Libraries

* [Collections: Your ultimate Javascript Arrays Manager](https://dev.to/hassanzohdy/collections-your-ultimate-javascript-array-handler-3o15)
* [Supportive Is: an elegant utility to check types of values in JavaScript](https://dev.to/hassanzohdy/supportive-is-an-elegant-utility-to-check-types-of-values-in-javascript-1b3e)
* [Localization: An agnostic i18n package to manage localization in your project](https://dev.to/hassanzohdy/mongez-localization-the-simplest-way-to-translate-your-website-regardless-your-favorite-framework-4gi3)

 React Js Packages

* [useFetcher: easiest way to fetch data in React Js](https://dev.to/hassanzohdy/usefetcher-the-easiest-way-to-fetch-data-in-react-45o9)

Courses (Articles)

* [React Js: Let"s Create File Manager With React Js and Node Js](https://dev.to/hassanzohdy/lets-create-a-file-manager-from-scratch-with-react-and-typescript-chapter-i-a-good-way-to-expand-your-experience-5g4k)