nartc / mapper

🔥 An Object-Object AutoMapper for TypeScript 🔥
https://automapperts.netlify.app/
MIT License
982 stars 88 forks source link

Sequelize object is empty #532

Open LBeckX opened 1 year ago

LBeckX commented 1 year ago

Is there an existing issue for this?

Describe the issue

Sequelize map is empty

Models/DTOs/VMs

export class User extends Model {
}

User.init({
    id: {
        type: DataTypes.INTEGER,
        autoIncrement: true,
        allowNull: false,
        primaryKey: true,
        validate: {
            isNumeric: true
        }
    },
    username: {
        type: DataTypes.STRING,
        allowNull: false,
        validate: {
            len: [5, 20]
        }
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: true,
        validate: {
            isEmail: true
        }
    },
    password: {
        type: DataTypes.STRING,
        allowNull: true,
    }
}, {
    tableName: 'User',
    sequelize
});

export class UserResponseDto {
    @AutoMap()
    username: string;

    @AutoMap()
    email: string;
}

export const mapper = createMapper({
    strategyInitializer: sequelize(),
});

createMap(mapper, UserEntity, UserResponseDto);

const userResponse = await UserEntity.findByPk(1);

console.log(mapper.map(userResponse .get(), User, UserResponseDto))

Mapping configuration

No response

Steps to reproduce

npm i @automapper/core @automapper/classes @automapper/sequelize

Expected behavior

{
  username: 'MY-USER',
  email: 'my@mail.com'
}

Screenshots

No response

Minimum reproduction code

No response

Package

Other package and its version

No response

AutoMapper version

8.7.7

Additional context

Node: v18.14.0

"@automapper/classes": "^8.7.7",
"@automapper/core": "^8.7.7",
"@automapper/sequelize": "^8.7.7",
"bcrypt": "^5.1.0",
"body-parser": "^1.20.1",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"helmet": "^6.0.1",
"joi": "^17.7.0",
"mariadb": "^3.0.2",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"reflect-metadata": "^0.1.13",
"sequelize": "^6.28.0"
spuxx1701 commented 2 weeks ago

@LBeckX did you ever happen to find a solution?

Edit: Should anyone be in dire need of a mapping functionality that works with sequelize, I implemented one myself here: https://github.com/spuxx1701/jslibs/tree/main/packages/nest-utils/src/mapping

It's very simple and easy to extend with more features. Feel free to copy.

LBeckX commented 2 weeks ago

@spuxx1701 As I remember, I used the following solutions for the problems.

Install https://github.com/typestack/class-transformer

Create the user entity


User.init({
    id: {
        type: DataTypes.INTEGER,
        autoIncrement: true,
        allowNull: false,
        primaryKey: true,
        validate: {
            isNumeric: true
        }
    },
    username: {
        type: DataTypes.STRING,
        allowNull: false,
        validate: {
            len: [5, 20]
        }
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: true,
        validate: {
            isEmail: true
        }
    },
    password: {
        type: DataTypes.STRING,
        allowNull: true,
    }
}, {
    tableName: 'User',
    sequelize
});

Create a UserDto


export class UserDto {
    @Expose()
    id: number;

    @Expose()
    username: string;
}

Create a helper function

export const getUserDto = (obj: any) => plainToInstance(UserDto , obj, {excludeExtraneousValues: true})

It also works with arrays or child entities

spuxx1701 commented 2 weeks ago

@LBeckX Thanks for letting me know! I'll take a look at your solution. Utilizing class-transformer sounds like a good approach since it's commonly part of the nestjs app stack anyways. 🎉

LBeckX commented 2 weeks ago

If you use nestjs, I can recommend this article from the documentation.

https://docs.nestjs.com/interceptors

My code to transform all outgoing entites:

transform.interceptor.ts

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { map, Observable } from 'rxjs';
import { instanceToPlain } from 'class-transformer';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(map((data) => instanceToPlain(data)));
  }
}

app.module.ts

...
providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: TransformInterceptor,
    },
  ],
...