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 thecookie-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.