diff --git a/content/custom-decorators.md b/content/custom-decorators.md index 4b261cddd8..15bd83b74e 100644 --- a/content/custom-decorators.md +++ b/content/custom-decorators.md @@ -1,6 +1,6 @@ ### Custom route decorators -Nest is built around a language feature called **decorators**. It's a well-known concept in a lot of commonly used programming languages, but in the JavaScript world, it's still relatively new. In order to better understand how the decorators work, you should take a look at [this](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841) article. Here's a simple definition: +Nest is built around a language feature called **decorators**. Decorators are a well-known concept in a lot of commonly used programming languages, but in the JavaScript world, they're still relatively new. In order to better understand how decorators work, we recommend reading [this article](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841). Here's a simple definition:
An ES2016 decorator is an expression which returns a function and can take a target, name and property descriptor as arguments. @@ -10,7 +10,7 @@ Nest is built around a language feature called **decorators**. It's a well-known #### Param decorators -Nest provides a set of useful **param decorators** that you can use together with the HTTP route handlers. Below is a comparison of the decorators with the plain express objects. +Nest provides a set of useful **param decorators** that you can use together with the HTTP route handlers. Below is a list of the provided decorators and the plain Express (or Fastify) objects they represent@@ -49,15 +49,15 @@ Nest provides a set of useful **param decorators** that you can use together wit
-Additionally, you can create your own, **custom decorator**. Why it is useful? +Additionally, you can create your own **custom decorators**. Why is this useful? -In the node.js world, it's a common practice to attach properties to the **request** object. Then you have to manually grab them every time in the route handlers, for example, using following construction: +In the node.js world, it's common practice to attach properties to the **request** object. Then you manually extract them in each route handler, using code like the following: ```typescript const user = req.user; ``` -In order to make it more readable and transparent, we can create a `@User()` decorator and reuse it across all existing controllers. +In order to make your code more readable and transparent, you can create a `@User()` decorator and reuse it across all of your controllers. ```typescript @@filename(user.decorator) @@ -86,44 +86,56 @@ async findOne(user) { #### Passing data -When the behavior of your decorator depends on some conditions, you may use the `data` param to pass an argument to the decorator's factory function. For example, the construction below: +When the behavior of your decorator depends on some conditions, you can use the `data` parameter to pass an argument to the decorator's factory function. One use case for this is a custom decorator that extracts properties from the request object by key. Let's assume, for example, that our authentication layer validates requests and attaches a user entity to the request object. The user entity for an authenticated request might look like: -```typescript -@@filename() -@Get() -async findOne(@User('test') user: UserEntity) { - console.log(user); -} -@@switch -@Get() -@Bind(User('test')) -async findOne(user) { - console.log(user); +```json +{ + "id": 101, + "firstName": "Alan", + "lastName": "Turing", + "email": "alan@email.com", + "roles": ["admin"] } ``` -Will make possible to access the `test` string via the `data` argument: +Let's define a decorator that takes a property name as key, and returns the associated value if it exists (or undefined if it doesn't exist, or if the `user` object has not been created). ```typescript @@filename(user.decorator) import { createParamDecorator } from '@nestjs/common'; export const User = createParamDecorator((data: string, req) => { - console.log(data); // test - return req.user; + return data ? req.user && req.user[data] : req.user; }); @@switch import { createParamDecorator } from '@nestjs/common'; export const User = createParamDecorator((data, req) => { - console.log(data); // test - return req.user; + return data ? req.user && req.user[data] : req.user; }); ``` +Here's how you could then access a particular property via the `@User()` decorator in the controller: + +```typescript +@@filename() +@Get() +async findOne(@User('firstName') firstName: string) { + console.log(`Hello ${firstName}`); +} +@@switch +@Get() +@Bind(User('firstName')) +async findOne(firstName) { + console.log(`Hello ${firstName}`); +} +``` + +You can use this same decorator with different keys to access different properties. If the `user` object is deep or complex, this can make for easier and more readable request handler implementations. + #### Working with pipes -Nest treats custom param decorators in the same fashion as the built-in ones (`@Body()`, `@Param()` and `@Query()`). It means that pipes are executed for the custom annotated parameters as well (in this case, for the `user` argument). Moreover, you can apply the pipe directly to the custom decorator: +Nest treats custom param decorators in the same fashion as the built-in ones (`@Body()`, `@Param()` and `@Query()`). This means that pipes are executed for the custom annotated parameters as well (in our examples, the `user` argument). Moreover, you can apply the pipe directly to the custom decorator: ```typescript @@filename()