Automapper and Mapped type in nest js?

192 Views Asked by At

so I am creating a backend service using nest js I am following the dto pattern now I created a product dto like this:

import { OmitType } from "@nestjs/mapped-types";
import { Product } from "../entities/product.entity";
import {AutoMap} from '@automapper/classes'
import { ApiProperty } from "@nestjs/swagger";
export class CreateProductDto{

    
    
  @AutoMap()
  @ApiProperty()
  productId: string;
  

  @AutoMap()
  @ApiProperty()
  productName: string;

  @AutoMap()
  @ApiProperty()
  price: number;


  @AutoMap()
  @ApiProperty()
  userId: string;
}

and the entity like this:

import { v4 as uuid  } from 'uuid';
import { BaseEntity, Column, Double, Entity, PrimaryGeneratedColumn } from "typeorm";

import { AutoMap } from "@automapper/classes";
@Entity()
export class Product {


  @AutoMap()
  @PrimaryGeneratedColumn()
  productId: number;
  

 

     @AutoMap()
      @Column({ type: 'varchar' })
      productName: string

;

  @AutoMap()
  @Column({ type: 'float' })
  price: number;


  @AutoMap()
  @Column({ type: 'uuid' })
  userId: string;
}

and then created an automapper profile like this:

import { Injectable } from "@nestjs/common";
import { AutomapperProfile, InjectMapper } from "@automapper/nestjs";
import { Mapper, createMap, createMapper, forMember, ignore } from "@automapper/core";
import { CreateProductDto } from "./create-product.dto";
import { Product } from "../entities/product.entity";
import { UpdateProductDto } from "./update-product.dto";

@Injectable()
export class ProductMapper extends AutomapperProfile{
    constructor(
        @((InjectMapper as any)()) mapper: Mapper
    ){
        super(mapper);
    }

    override get profile(){
        return (mapper: Mapper) => {
            createMap(mapper, CreateProductDto, Product, forMember(dest => dest.productId, ignore()))
            createMap(mapper, UpdateProductDto, Product)
            createMap(mapper, Product, CreateProductDto )
            createMap(mapper, UpdateProductDto, Product)
            createMap(mapper, Product, UpdateProductDto)
        }

    }
}

and used that in the service and it working just fine,

now I have another DTO for updating like below where I used mapped type from the swagger lib:

import { PartialType } from '@nestjs/mapped-types';
import { CreateProductDto } from './create-product.dto';

export class UpdateProductDto extends PartialType(CreateProductDto) {}

but this one deos not work and it cannot perform the mapping... any thoughts?? thanks.

1

There are 1 best solutions below

0
Nick Verbraecken On

The problem is that when you use PartialType, it doesn't automatically add the @AutoMap decoration to fields, which you need for AutoMapper. To fix this, make a new file named custom-field-partial-type.ts and copy the code from this link: https://gist.github.com/Copystrike/bfc5010100aba362002616f9eca7fa25

What the code does is simple: it lets you add custom decorators when you use the applyFields function. This means you can add the @AutoMap decorator, which is important for mapping.

Now, you can do things like:

@InputType('UpdateTodoItemInput')
export class UpdateTodoItemInput extends CustomFieldPartialType(CreateTodoItemInput, {
  customFields(partialObjectType, item) {
    AutoMap()(partialObjectType.prototype, item.name); // This adds the AutoMap annotation to the field.
  },
}) {}

Or you could even go a little more complex and do:

@InputType('UpdateTodoItemInput')
export class UpdateTodoItemInput extends CustomFieldPartialType(CreateTodoItemInput, {
  customFields(partialObjectType, item) {
    if (item.name === 'text') {
      AutoMap()(partialObjectType.prototype, item.name);
    }
  },
}) {}

This code only adds the @AutoMap decorator to the "text" field.

I have opened an issue on the @nestjs/graphql repository on GitHub, and maybe someone will be able to integrate this into the actual library. Here is the link to the issue: https://github.com/nestjs/graphql/issues/3201