Friday, August 5, 2022
HomeWeb DevelopmentMethods to prolong the Specific Request object in TypeScript

Methods to prolong the Specific Request object in TypeScript


The Request object is utilized by Specific to offer knowledge in regards to the HTTP request to the controllers of a Node.js server. Due to this fact, all knowledge from the request obtainable on the utility layer depends upon this object.

You would possibly must elaborate on the information you obtained within the HTTP request and supply the controllers with customized information. In JavaScript, you’ll be able to merely outline new properties within the Request object and use them when wanted. In TypeScript, you might want to prolong the Request sort if you wish to outline customized properties.

Let’s now be taught what Request is in Specific, and delve into the the reason why extending the Request sort in TypeScript will be helpful. Then, let’s see how one can make the most of the prolonged Request object by an Specific demo utility inbuilt TypeScript.

TL;DR: Let’s learn to prolong the Request sort in TypeScript to make its cases retailer customized knowledge you should utilize on the controller stage.

What’s the Request object in Specific?

The Request object represents the HTTP request carried out by the shopper to an Specific server. In different phrases, an Specific server can learn the information obtained from the shopper by Request cases. Due to this fact, Request has a number of properties to entry all the data contained within the HTTP request, however a very powerful ones are:

  • question: this object comprises a property for every question string parameter current within the URL of the request:
    app.get("/customers", (req: Request, res: Response) => {
      // on GET "/customers?id=4" this could print "4"
      console.log(req.question.id)
    });
  • params: this object comprises the parameters outlined within the API URL in accordance with the Specific routing conference:
    app.get("/customers/:id", (req: Request, res: Response) => {
      // on GET "/customers/1" this could print "1"
      console.log(req.params.id)
    });
  • physique: this object comprises key-value pairs of information submitted within the physique of the HTTP request:
    app.get("/customers", (req: Request<by no means, by no means, { title: string; surname: string }, by no means>, res: Response) => {
      const { title, surname } = req.physique
    
      // ...
    })
  • headers: this object comprises a property for every HTTP header despatched by the request.
  • cookies: when utilizing the cookie-parser Specific middleware, this object comprises a property for every cookie despatched by the request

Why prolong Request?

Specific controllers can entry all the information contained in an HTTP request with the Request object. This doesn’t imply that the Request object is the one approach to work together with the controllers. Quite the opposite, Specific additionally helps middlewares. Specific middlewares are features that can be utilized so as to add application-level or router-level performance.

The middleware features are related to the endpoints on the router stage as follows:

const authenticationMiddleware = require("../middlewares/authenticationMiddleware")
const FooController = require("../controllers/foo")

app.get(
  "/helloWorld",
  FooController.helloWorld, // (req, res) => { res.ship("Good day, World!") }
  // registering the authenticationMiddleware to the "/helloWorld" endpoint
  authenticationMiddleware,
)

Word that middleware features are executed earlier than the controller operate containing the enterprise logic of the API is known as. Be taught extra about how they work and what the Specific middlewares can supply right here.

What’s essential to note right here is that middlewares can modify the Request object, including customized info to make it obtainable on the controller stage. For instance, let’s say you wish to make your APIs obtainable solely to customers with a sound authentication token. To attain this, you’ll be able to outline a easy authentication middleware as follows:

import { Request, Response, NextFunction } from "categorical"

export operate handleTokenBasedAuthentication(req: Request, res: Response, subsequent: NextFunction) {
  const authenticationToken = req.headers["authorization"]

  if (authenticationToken !== undefined) {
    const isTokenValid = // verifying if authenticationToken is legitimate with a question or an API name...

    if (isTokenValid) {
      // shifting to the subsequent middleware
      return subsequent()
    }
  }

  // if the authorization token is invalid or lacking returning a 401 error
  res.standing(401).ship("Unauthorized")
}

When the authentication token obtained within the Authorization header of the HTTP request is legitimate, this worth is uniquely related to a person of your service. In different phrases, the authentication token permits you to determine the person who’s making the request, which is essential to know. For instance, the enterprise logic on the controller stage might change relying on the person’s function.


Extra nice articles from LogRocket:


If a number of controller-level features must know who the person who carried out the API name is, you need to replicate the logic required to retrieve the person from the Authorization header in a number of locations. As an alternative, you need to prolong the Request object with a person property and provides it a price within the authentication middleware.

Discover that the Specific Request sort in TypeScript doesn’t contain a person property. Which means you can not merely prolong the Request object as follows:

import { Request, Response, NextFunction } from "categorical"

export operate handleTokenBasedAuthentication(req: Request, res: Response, subsequent: NextFunction) {
  const authenticationToken = req.headers["authorization"]

  if (authenticationToken !== undefined) {
    const isTokenValid = // verifying if authenticationToken is legitimate with a question or an API name...

    if (isTokenValid) {
      const person = // retrieving the person information based mostly on authenticationToken  

      req["user"] = person // ERROR: Property 'person' doesn't exist on sort 'Request'

      // shifting to the subsequent middleware
      return subsequent()
    }
  }

  // if the authorization token is invalid or lacking returning a 401 error
  res.standing(401).ship("Unauthorized")
}

This might result in the next error:

Property 'person' doesn't exist on sort 'Request<ParamsDictionary, any, any, ParsedQs, File<string, any>>'.

Equally, you should utilize an prolonged Request to keep away from sort casting on the controller stage and make your codebase cleaner and sturdy. Let’s assume your backend utility helps solely three languages: English, Spanish, and Italian. In different phrases, you already know that the Content material-Language HTTP headers can solely settle for en, es, and it. When the header is omitted or comprises an invalid worth, you need the English language for use as default.

Needless to say req.headers["Content-Language"] returns a string | string[] | undefined sort. Which means if you wish to use Content material-Language header worth as a string, you need to forged it as follows:

const language = (req.headers["content-language"] || "en") as string | undefined

Filling your code with this logic shouldn’t be a chic resolution. As an alternative, you need to use a middleware to increase Request as beneath:

import { Request, Response, NextFunction } from "categorical"

const SUPPORTED_LANGUAGES = ["en", "es", "it"]
// this syntax is equals to "en" | "es" | "it"
export sort Language = typeof SUPPORTED_LANGUAGES[number] 

export operate handleCustomLanguageHeader(req: Request, res: Response, subsequent: NextFunction) {
  const languageHeader = req.headers["content-language"]

  // default language: "en"
  let language: Language = SUPPORTED_LANGUAGES[0]

  if (typeof languageHeader === "string" && SUPPORTED_LANGUAGES.contains(languageHeader)) {
    language = languageHeader
  }

  // extending the Request object with a language property of sort Language...

  return subsequent()
}

These had been simply two examples, however there are a number of different situations the place extending Request with customized knowledge can prevent time and make your codebase extra elegant and maintainable.

Extending the Specific Request sort in TypeScript

Including further fields to the Specific Request sort definition solely takes just a few traces of code. Let’s see the right way to obtain it and make the most of the prolonged object by a demo utility based mostly on the middlewares offered earlier.

Clone the GitHub repository that helps the article and launch the pattern backend utility regionally with the next instructions:

git clone https://github.com/Tonel/extend-express-request-ts-demo
cd extend-express-request-ts-demo
npm i
npm begin

Maintain following the article to learn to make the most of the prolonged Specific Request sort in TypeScript.

Conditions

You want these stipulations to duplicate the article’s objective:

When you don’t have an Specific mission in Typescript, you’ll be able to learn to arrange an Specific and TypeScript mission from scratch right here.

Including customized properties to the Request sort

All you need to do to increase the Request sort is outline an index.d.ts file as follows:

// src/varieties/categorical/index.d.ts

import { Language, Person } from "../customized";

// to make the file a module and keep away from the TypeScript error
export {}

declare international {
  namespace Specific {
    export interface Request {
      language?: Language;
      person?: Person;
    }
  }
}

Place this file within the src/varieties/categorical folder. Typescript makes use of the .d.ts declaration recordsdata to load sort details about a library written in JavaScript. Right here, the index.d.ts international module will probably be utilized by TypeScript to increase the Specific Request sort globally.

The Language and Person customized varieties are outlined within the src/varieties/customized.ts file as beneath:

// src/varieties/customized.ts

export const SUPPORTED_LANGUAGES = ["en", "es", "it"]
// this syntax is equals to "en" | "es" | "it"
export sort Language = typeof SUPPORTED_LANGUAGES[number]

export sort Person =  null

These varieties will probably be used within the handleCustomLanguageHeader() and handleTokenBasedAuthentication() middleware features, respectively. Let’s see how.

Utilizing the prolonged Request object

Now, let’s be taught how one can make use of the prolonged Request object. First, let’s full the middleware features launched earlier. That is what authentication.middleware.ts seems to be like:

// src/middlewares/authentication.middleware.ts 

import { Request, Response, NextFunction } from "categorical"
import { Person } from "../varieties/customized"

// in-memory database
const customers: Person[] = [
    {
        id: 1,
        name: "Maria",
        surname: "Williams",
        authenticationToken: "$2b$08$syAMV/CyYt.ioZ3w5eT/G.omLoUdUWwTWu5WF4/cwnD.YBYVjLw2O",
    },
    {
        id: 2,
        name: "James",
        surname: "Smith",
        authenticationToken: null,
    },
    {
        id: 3,
        name: "Patricia",
        surname: "Johnson",
        authenticationToken: "$2b$89$taWEB/dykt.ipQ7w4aTPGdo/aLsURUWqTWi9SX5/cwnD.YBYOjLe90",
    },
]

export operate handleTokenBasedAuthentication(req: Request, res: Response, subsequent: NextFunction) {
    const authenticationToken = req.headers["authorization"]

    if (authenticationToken !== undefined) {
        // utilizing the in-memory pattern database to confirm if authenticationToken is legitimate
        const isTokenValid = !!customers.discover((u) => u.authenticationToken === authenticationToken)

        if (isTokenValid) {
            // retrieving the person related to the authenticationToken worth
            const person = customers.discover((u) => u.authenticationToken === authenticationToken)

            req.person = person

            // shifting to the subsequent middleware
            return subsequent()
        }
    }

    // if the authorization token is invalid or lacking returning a 401 error
    res.standing(401).ship("Unauthorized")
}

For the sake of simplicity, the authentication token is validated by an in-memory database. In a real-world situation, substitute this easy logic with some queries or API calls. Discover how one can now assign the person related to the token to the Request customized person property.

Now, let’s see the ultimate language.middleware.ts middleware file:

// src/middlewares/headers.middleware.ts

import { Request, Response, NextFunction } from "categorical"
import { Language, SUPPORTED_LANGUAGES } from "../varieties/customized"

export operate handleLanguageHeader(req: Request, res: Response, subsequent: NextFunction) {
    const languageHeader = req.headers["content-language"]

    // default language: "en"
    let language: Language = SUPPORTED_LANGUAGES[0]

    if (typeof languageHeader === "string" && SUPPORTED_LANGUAGES.contains(languageHeader)) {
        language = languageHeader
    }

    req.language = language

    return subsequent()
}

The Request customized language property is used to retailer the Language info.

Subsequent, let’s see the Request customized properties in motion in two totally different APIs. That is what the HelloWorldController object seems to be like:

// src/controllers/helloWorld.ts 

import { NextFunction, Request, Response } from "categorical"

export const HelloWorldController = {
    default: async (req: Request<by no means, by no means, by no means, by no means>, res: Response, subsequent: NextFunction) => {
        let message

        swap (req.language) {
            default:
            case "en": {
                message = "Good day, World!"
                break
            }
            case "es": {
                message = "¡Hola, mundo!"
                break
            }
            case "it": {
                message = "Ciao, mondo!"
                break
            }
        }

        res.json(message)
    },
    hey: async (req: Request<by no means, by no means, by no means, by no means>, res: Response, subsequent: NextFunction) => {
        res.json(`Hey, ${req.person?.title}`)
    },
}

As you’ll be able to see, HelloWorldController defines two APIs. The primary one makes use of the Request customized language property, whereas the second employs the Request customized person property.

Lastly, you need to register the middleware features with their endpoints within the router file as beneath:

// src/routes/index.ts

import { Router } from "categorical"
import { HelloWorldController } from "../controllers/helloWorld"
import { handleLanguageHeader } from "../middleware/customHeaders.middleware"
import { handleTokenBasedAuthentication } from "../middleware/authentication.middleware"

export const router = Router()

router.get("https://weblog.logrocket.com/", handleLanguageHeader, HelloWorldController.default)
router.get("/hey", handleTokenBasedAuthentication, HelloWorldController.hey)

Testing the prolonged Request

Let’s take a look at the APIs outlined earlier with curl. First, launch the demo Specific utility with npm run begin.

Now, let’s take a look on the conduct of the / endpoint.

As you’ll be able to see, the language of the message returned by the API returns by the Content material-Language header as anticipated.

Now, let’s take a look at the /hey endpoint.

Once more, the response depends upon the person loaded due to the Authorization header worth.

Et voila! You simply discovered the right way to prolong the Specific Request sort and the right way to use it to offer customized info to the controllers.

Conclusion

On this article, you checked out what the Specific Request object is, why it’s so essential, and once you would possibly want to increase it. The Request object shops all the information in regards to the HTTP request. With the ability to prolong it represents an efficient approach to move customized knowledge to controllers straight. As proven, this lets you keep away from code duplication.

Right here, you discovered the right way to prolong the Specific Request sort in TypeScript. That is simple and requires just a few traces of code. Additionally, it may convey a number of benefits to your backend utility, as proven by the pattern demo Specific utility developed in TypeScript.

Thanks for studying! I hope that you simply discovered this text useful. Be at liberty to succeed in out to me with any questions, feedback, or ideas.

Writing quite a lot of TypeScript? Watch the recording of our current TypeScript meetup to find out about writing extra readable code.

TypeScript brings sort security to JavaScript. There generally is a stress between sort security and readable code. Watch the recording for a deep dive on some new options of TypeScript 4.4.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments