{
  "slug": "Implementing-Ownership-and-Authentication-in-NestJS--A-Comprehensive-Approach-9d027f58faf2",
  "title": "Implementing Ownership and Authentication in NestJS: A Comprehensive Approach",
  "subtitle": "The document discusses implementing ownership and authentication in NestJS, focusing on many-to-one/one-to-many relationships, custom…",
  "excerpt": "The document discusses implementing ownership and authentication in NestJS, focusing on many-to-one/one-to-many relationships, custom…",
  "date": "2024-07-03",
  "tags": [
    "Node.js",
    "Security",
    "Machine Learning"
  ],
  "readingTime": "8 min",
  "url": "https://medium.com/@mobinshaterian/implementing-ownership-and-authentication-in-nestjs-a-comprehensive-approach-9d027f58faf2",
  "hero": "https://cdn-images-1.medium.com/max/800/1*yf_Uw0KcgNQIDZ599AW0Kg.jpeg",
  "content": [
    {
      "type": "paragraph",
      "html": "The document discusses implementing ownership and authentication in NestJS, focusing on many-to-one/one-to-many relationships, custom decorators, and interceptors. It covers topics such as connecting users to tasks, setting entity names, creating custom decorators, implementing JWT authentication, and using transformer interceptors. The document also provides code examples for creating, retrieving, and filtering tasks associated with authenticated users."
    },
    {
      "type": "paragraph",
      "html": "NestJS, a progressive Node.js framework, offers robust tools for building scalable and efficient server-side applications. This essay explores the implementation of ownership and authentication in NestJS, focusing on key concepts and best practices.<br>At the core of this implementation is the many-to-one/one-to-many relationship paradigm. This relational model effectively captures hierarchical relationships, such as between users and tasks. In the context of a task management system, a user can own multiple tasks, while each task belongs to a single user. This relationship is established using TypeORM decorators like <a href=\"http://twitter.com/OneToMany\" target=\"_blank\" rel=\"noreferrer noopener\">@OneToMany</a> and <a href=\"http://twitter.com/ManyToOne\" target=\"_blank\" rel=\"noreferrer noopener\">@ManyToOne</a>, allowing for efficient data organization and retrieval."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*yf_Uw0KcgNQIDZ599AW0Kg.jpeg",
      "alt": "Implementing Ownership and Authentication in NestJS: A Comprehensive Approach",
      "caption": "",
      "width": 1024,
      "height": 1024
    },
    {
      "type": "paragraph",
      "html": "Custom decorators play a crucial role in this implementation. The @GetUser() decorator, for instance, extracts the authenticated user from the request object. This approach keeps controller methods clean and focused on business logic, promoting code reusability and maintainability. By leveraging ExecutionContext, these decorators can access request data and inject it into route handlers."
    },
    {
      "type": "paragraph",
      "html": "Authentication is further enhanced by integrating JWT (JSON Web Tokens). The user’s ID is saved within the JWT, allowing for secure and stateless authentication. This token is then used to identify the user in subsequent requests, ensuring that users can only access and modify their own resources."
    },
    {
      "type": "paragraph",
      "html": "Interceptors, such as the TransformerInterceptor, are utilized to automatically transform response data before sending it back to the client. This is particularly useful for serializing complex objects, handling circular references, and applying exclusion strategies. By using the class-transformer library, developers can control which properties are exposed in the API responses, enhancing security and reducing unnecessary data transfer."
    },
    {
      "type": "paragraph",
      "html": "The implementation also focuses on efficient task management. Methods for creating, retrieving, and filtering tasks are designed with user ownership in mind. The getTasks method, for example, uses QueryBuilder to construct database queries that filter tasks based on the authenticated user and additional criteria like status or search terms."
    },
    {
      "type": "paragraph",
      "html": "Error handling and validation are integral parts of this implementation. The use of ValidationPipe ensures that incoming data adheres to defined schemas, while custom exception filters can be employed to handle and format error responses consistently."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Source code:"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Many-to-one / one-to-many relations"
    },
    {
      "type": "paragraph",
      "html": "The many-to-one or one-to-many relationship paradigm in database design and entity modeling describes an asymmetric association between two entities. In this relational structure, an entity of type A may be associated with multiple instances of entity type B, while each instance of entity type B is exclusively associated with a single instance of entity type A."
    },
    {
      "type": "paragraph",
      "html": "To elucidate this concept, consider the relationship between ‘User’ and ‘Task’ entities in a system. In this scenario, a single User entity may be linked to an arbitrary number of Taks entities, representing the user’s collection of Tasks. Conversely, each individual Task entity is uniquely associated with one and only one User entity, denoting the task’s owner or creator."
    },
    {
      "type": "paragraph",
      "html": "This relational model effectively captures the hierarchical nature of many real-world relationships, where ownership or containment is unidirectional and multiplicative in one direction but singular in the other. It allows for efficient data organization and retrieval, supporting scenarios where aggregation of related entities is required from the ‘one’ side of the relationship, or where tracing ownership is necessary from the ‘many’ side."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Connect user to task"
    },
    {
      "type": "paragraph",
      "html": "user.entity.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "@OneToMany(() => Tasks, (tasks) => tasks.user, { eager: true })  tasks: Tasks[];"
    },
    {
      "type": "paragraph",
      "html": "task.entity.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "@ManyToOne(() => Users, (user) => user.tasks, { eager: false })  user: Users;"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Set name for entity"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "@Entity({\n  name: 'system_users'\n})\nexport class User {\n  @PrimaryGeneratedColumn() id: number;\n  @Column({\n    length: 255, name: \"first_name\"\n  }) firstName: string;\n  @Column({\n    length: 255, name: \"last_name\"\n  }) lastName: string;\n}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "how to get user data with req.user on decorator in nest js"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "import {\n  createParamDecorator, ExecutionContext\n}\nfrom '@nestjs/common';\nexport const User = createParamDecorator((data: unknown, ctx: ExecutionContext) => {\n  const request = ctx.switchToHttp().getRequest();\n  return request.user;\n},);"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Custom route decorators"
    },
    {
      "type": "paragraph",
      "html": "Nest is built around a language feature called <strong>decorators</strong>. 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 <a href=\"https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841\" target=\"_blank\" rel=\"noreferrer noopener\">this article</a>. Here’s a simple definition:"
    },
    {
      "type": "paragraph",
      "html": "Let’s dive deeper into custom decorators in NestJS, particularly focusing on param decorators like <code>@GetUser()</code> in your code."
    },
    {
      "type": "paragraph",
      "html": "Custom decorators in NestJS allow you to create reusable pieces of code that can be applied to classes, methods, or parameters. They’re especially useful for extracting and transforming data from the request object, or for applying metadata to route handlers."
    },
    {
      "type": "paragraph",
      "html": "Certainly. Let’s dive deeper into custom decorators in NestJS, particularly focusing on param decorators like `<a href=\"http://twitter.com/GetUser\" target=\"_blank\" rel=\"noreferrer noopener\">@GetUser</a>()` in your code."
    },
    {
      "type": "paragraph",
      "html": "Custom decorators in NestJS allow you to create reusable pieces of code that can be applied to classes, methods, or parameters. They’re especially useful for extracting and transforming data from the request object, or for applying metadata to route handlers."
    },
    {
      "type": "paragraph",
      "html": "In your case, `<a href=\"http://twitter.com/GetUser\" target=\"_blank\" rel=\"noreferrer noopener\">@GetUser</a>()` is a custom parameter decorator. Here’s how it likely works:"
    },
    {
      "type": "paragraph",
      "html": "1. Definition: The `<a href=\"http://twitter.com/GetUser\" target=\"_blank\" rel=\"noreferrer noopener\">@GetUser</a>()` decorator is probably defined in the `../auth/get-user.decorator.ts` file. It’s used to extract the user object from the request."
    },
    {
      "type": "paragraph",
      "html": "2. Implementation: A typical implementation might look like this:"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "import {\n  createParamDecorator, ExecutionContext\n}\nfrom '@nestjs/common';\nimport {\n  User\n}\nfrom './user.entity';\nexport const GetUser = createParamDecorator((data: unknown, ctx: ExecutionContext): User => {\n  const request = ctx.switchToHttp().getRequest();\n  return request.user;\n},);"
    },
    {
      "type": "paragraph",
      "html": "This decorator extracts the `user` object from the request, which is typically set by an authentication middleware or guard."
    },
    {
      "type": "paragraph",
      "html": "3. Usage: In your controller method, `<a href=\"http://twitter.com/GetUser\" target=\"_blank\" rel=\"noreferrer noopener\">@GetUser</a>() user: User` tells NestJS to use this custom decorator to extract the user and inject it as a parameter."
    },
    {
      "type": "paragraph",
      "html": "4. Benefit: This approach keeps your controller methods clean and focused on business logic, rather than dealing with request parsing."
    },
    {
      "type": "paragraph",
      "html": "In your specific code:"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "@Post()createTask(@Body() createTaskDto: CreateTaskDto, @GetUser() user: User,): Promise<Task> {\n  console.log(user);\n  return this.tasksService.createTask(createTaskDto, user);\n}"
    },
    {
      "type": "paragraph",
      "html": "- `<a href=\"http://twitter.com/Post\" target=\"_blank\" rel=\"noreferrer noopener\">@Post</a>()` is a built-in NestJS decorator that defines this method as a POST route handler.<br>- `<a href=\"http://twitter.com/Body\" target=\"_blank\" rel=\"noreferrer noopener\">@Body</a>()` is another built-in decorator that extracts the request body and injects it as `createTaskDto`.<br>- `<a href=\"http://twitter.com/GetUser\" target=\"_blank\" rel=\"noreferrer noopener\">@GetUser</a>()` is your custom decorator that extracts the authenticated user from the request."
    },
    {
      "type": "paragraph",
      "html": "The method then logs the user (for debugging purposes) and calls `this.tasksService.createTask()` with both the DTO and the user object."
    },
    {
      "type": "paragraph",
      "html": "This pattern allows you to easily access the authenticated user in any route handler, promoting code reuse and keeping your controllers clean and focused on their primary responsibilities."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Save user id with JWT token"
    },
    {
      "type": "paragraph",
      "html": "user.decorator.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "import {\n  createParamDecorator, ExecutionContext\n}\nfrom '@nestjs/common';\nexport const GetUser = createParamDecorator((data: unknown, ctx: ExecutionContext) => {\n  const request = ctx.switchToHttp().getRequest();\n  return request.user;\n},);"
    },
    {
      "type": "paragraph",
      "html": "tasks.controller.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "import {\n  User\n}\nfrom 'src/auth/user.entity';\nimport {\n  GetUser\n}\nfrom 'src/auth/user.decorator';\n...@Post() createTask(@Body() createTaskDto: CreateTaskDto, @GetUser() user: User,): Promise<Task> {\n  return this.tasksService.createTask(createTaskDto, user);\n}"
    },
    {
      "type": "paragraph",
      "html": "service.controller.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "createTask(createTaskDto: CreateTaskDto, user: User): Promise<Task> {    const task =\nthis.taskRepository.createTask(createTaskDto, user);    return task;  }"
    },
    {
      "type": "paragraph",
      "html": "task.repository.ts"
    },
    {
      "type": "code",
      "lang": "javascript",
      "code": "public async createTask(    createTaskDto: CreateTaskDto,    user: User,  ): Promise<Task> {\nconst { title, description } = createTaskDto;    const task = this.create({      title,\ndescription,      status: TaskStatus.OPEN,      user,    });"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Transformer Interceptor"
    },
    {
      "type": "paragraph",
      "html": "trasnfor.interceptor.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "import {\n  CallHandler, ExecutionContext, Injectable, NestInterceptor,\n}\nfrom '@nestjs/common';\nimport {\n  instanceToPlain\n}\nfrom 'class-transformer';\nimport {\n  map\n}\nfrom 'rxjs/operators';\n@Injectable()\nexport class TransformerInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler<any>) {\n    return next.handle().pipe(map((data) => instanceToPlain(data)));\n  }\n}"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Class Declaration:"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "@Injectable()\nexport class TransformerInterceptor implements NestInterceptor"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "This declares a class named <code>TransformerInterceptor</code> that implements the <code>NestInterceptor</code> interface.",
        "<code>@Injectable()</code> marks this class as a provider that can be injected into other classes."
      ]
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Intercept Method:"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "intercept(context: ExecutionContext, next: CallHandler<any>) {  return next.handle().pipe(map((data)\n=> instanceToPlain(data)));}"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "This is the core of the interceptor. It's called for each request that this interceptor is applied to.",
        "<code>context</code>: Provides access to the current execution context (e.g., request and response objects).",
        "<code>next</code>: Represents the next handler in the request-response cycle."
      ]
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Transformation Logic:"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "return next.handle().pipe(map((data) => instanceToPlain(data)));"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "<code>next.handle()</code> calls the next handler (route handler or next interceptor) and returns an Observable.",
        "<code>.pipe()</code> is used to apply RxJS operators to the Observable.",
        "<code>map()</code> transforms each emitted value (the response data).",
        "<code>instanceToPlain(data)</code> converts class instances to plain JavaScript objects."
      ]
    },
    {
      "type": "paragraph",
      "html": "The purpose of this interceptor is to automatically transform the response data before it’s sent back to the client. It’s particularly useful for:"
    },
    {
      "type": "list",
      "ordered": true,
      "items": [
        "Serializing complex objects: Converting class instances (which may include methods or non-enumerable properties) into plain objects that can be easily JSON-serialized.",
        "Handling circular references: <code>instanceToPlain</code> can handle circular references in your data structures, preventing JSON serialization errors.",
        "Applying exclusion strategies: If you’ve used class-transformer decorators like <code>@Exclude()</code> or <code>@Expose()</code> on your entity classes, <code>instanceToPlain</code> will respect these, allowing you to control which properties are sent in the response."
      ]
    },
    {
      "type": "paragraph",
      "html": "To use this interceptor, you would typically apply it globally in your main.ts file or to specific controllers or routes using the <code>@UseInterceptors()</code> decorator."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*4PThZawq2bhTd_Hn7yiqSQ.jpeg",
      "alt": "Implementing Ownership and Authentication in NestJS: A Comprehensive Approach",
      "caption": "",
      "width": 1024,
      "height": 1024
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Interceptor"
    },
    {
      "type": "paragraph",
      "html": "task.entity.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "@ManyToOne(() => User, (user) => user.tasks, { eager: false })  @Exclude({ toPlainOnly: true })\n@JoinColumn({ name: 'user_id' })  user: User;"
    },
    {
      "type": "paragraph",
      "html": "transform.interceptor.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "import {\n  Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn,\n}\nfrom 'typeorm';\nimport {\n  TaskStatus\n}\nfrom './tasks-status.enum';\nimport {\n  User\n}\nfrom 'src/auth/user.entity';\nimport {\n  Exclude\n}\nfrom 'class-transformer';\n@Entity({\n  name: 'tasks'\n})\nexport class Task {\n  @PrimaryGeneratedColumn('uuid') id: string;\n  @Column() title: string;\n  @Column() description: string;\n  @Column() status: TaskStatus;\n  @ManyToOne(() => User, (user) => user.tasks, {\n    eager: false\n  }) @Exclude({\n    toPlainOnly: true\n  }) @JoinColumn({\n    name: 'user_id'\n  }) user: User;\n  @Column() created_at: Date;\n}"
    },
    {
      "type": "paragraph",
      "html": "main.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "import {\n  NestFactory\n}\nfrom '@nestjs/core';\nimport {\n  AppModule\n}\nfrom './app.module';\nimport {\n  ValidationPipe\n}\nfrom '@nestjs/common';\nimport {\n  TransformerInterceptor\n}\nfrom './transform.interceptor';\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.useGlobalPipes(new ValidationPipe());\n  app.useGlobalInterceptors(new TransformerInterceptor());\n  await app.listen(3000);\n}\nbootstrap();"
    },
    {
      "type": "paragraph",
      "html": "before"
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*Xbrb3T3SbXCmqHX_jRMQyg.png",
      "alt": "Implementing Ownership and Authentication in NestJS: A Comprehensive Approach",
      "caption": "",
      "width": 1156,
      "height": 607
    },
    {
      "type": "paragraph",
      "html": "after"
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*DkmxokU1bTyyLr2weet-4A.png",
      "alt": "Implementing Ownership and Authentication in NestJS: A Comprehensive Approach",
      "caption": "",
      "width": 1153,
      "height": 559
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Get all tasks for current user"
    },
    {
      "type": "paragraph",
      "html": "tasks.controller.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "@Get() getTasks(@Query() filterDto: GetTasksFilterDto, @GetUser() user: User,): Promise<Task[]> {\n  return this.tasksService.getTasks(filterDto, user);\n}"
    },
    {
      "type": "paragraph",
      "html": "tasks.service.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "async getTasks(filterDto: GetTasksFilterDto, user: User): Promise<Task[]> {    return await\nthis.taskRepository.getTasks(filterDto, user);  }"
    },
    {
      "type": "paragraph",
      "html": "tasks.repository.ts"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "async getTasks(filterDto: GetTasksFilterDto, user: User): Promise<Task[]> {    const { status,\nsearch } = filterDto;    const query = this.createQueryBuilder('tasks');    query.where({ user });\nif (status) {      query.andWhere('tasks.status = :status', { status });    }    if (search) {\nquery.andWhere(\n'(LOWER(tasks.title) LIKE LOWER(:search) OR LOWER(tasks.description) LIKE LOWER(:search))',        {\nsearch: `%${search}%` },      );    }    const tasks = await query.getMany();    return tasks;  }"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Get task by ID for user"
    },
    {
      "type": "paragraph",
      "html": "controller"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "@Get('/:id') getTaskById(@Param('id') id: string, @GetUser() user: User): Promise<Task> {\n  return this.tasksService.getTaskById(id, user);\n}"
    },
    {
      "type": "paragraph",
      "html": "service"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "async getTaskById(id: string, user: User): Promise<Task> {    const task = await\nthis.taskRepository.getTaskById(id, user);    return task;  }"
    },
    {
      "type": "paragraph",
      "html": "repository"
    },
    {
      "type": "code",
      "lang": "typescript",
      "code": "public async getTaskById(id: string): Promise<Task> {    const found = await this.findOne({ where: {\nid: id } });    if (!found) {      throw new NotFoundException(`Task with ID \"${id}\" not found`);\n}    return found;  }"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "In conclusion"
    },
    {
      "type": "paragraph",
      "html": "This approach to implementing ownership and authentication in NestJS demonstrates a comprehensive and secure method for managing user-specific data. By leveraging TypeORM relationships, custom decorators, JWT authentication, and interceptors, developers can create robust APIs that maintain data integrity and user privacy. This implementation not only ensures proper data access control but also promotes code organization and reusability, key factors in developing maintainable and scalable applications."
    },
    {
      "type": "paragraph",
      "html": "Visit us at <a href=\"https://www.datadriveninvestor.com/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>DataDrivenInvestor.com</em></a>"
    },
    {
      "type": "paragraph",
      "html": "Subscribe to DDIntel <a href=\"https://www.ddintel.com/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>here</em></a>."
    },
    {
      "type": "paragraph",
      "html": "Join our creator ecosystem <a href=\"https://join.datadriveninvestor.com/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>here</em></a>."
    },
    {
      "type": "paragraph",
      "html": "DDI Official Telegram Channel: <a href=\"https://t.me/+tafUp6ecEys4YjQ1\" target=\"_blank\" rel=\"noreferrer noopener\">https://t.me/+tafUp6ecEys4YjQ1</a>"
    },
    {
      "type": "paragraph",
      "html": "Follow us on <a href=\"https://www.linkedin.com/company/data-driven-investor\" target=\"_blank\" rel=\"noreferrer noopener\"><em>LinkedIn</em></a>, <a href=\"https://twitter.com/@DDInvestorHQ\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Twitter</em></a>, <a href=\"https://www.youtube.com/c/datadriveninvestor\" target=\"_blank\" rel=\"noreferrer noopener\"><em>YouTube</em></a>, and <a href=\"https://www.facebook.com/datadriveninvestor\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Facebook</em></a>."
    }
  ]
}
