Search code examples
javascripttypescriptormprisma

How to use typescript automapper(nartc/mapper) with Prisma js?


I try to make Prisma work with Automapper (to be able to map Models to Dtos and back). Prisma has schema.prisma where we define models, and that models are generated then automatically for us.. BUT as they are generated we cant modify them, but we need, because automapper requires adding decorators to make automapping work. So, is it impossible to use Prisma with automapper? Thanks!


Solution

  • The key to the solution is a two-step process in which we use AutoMapper to handle the mapping from these modified models to your desired DTOs after first converting Prisma models into a more flexible format that can be decorated or altered as needed. This method converts Prisma models into class instances, which can then be enhanced or decorated, using the plainToInstance function from the class-transformer package. These instances can then be mapped to DTOs using AutoMapper following this transformation.

    Step-by-Step Guide

    Step 1: Convert Prisma Models to Class Instances

    We need to create entity classes that mirror the structure of Prisma models. These classes will serve as intermediaries, allowing us to attach decorators or perform manipulations.

    // user.entity.ts
    import { AutoMap } from '@automapper/classes';
    
    export class UserEntity {
      @AutoMap()
      id: number;
    
      @AutoMap()
      name: string;
    
      @AutoMap()
      email: string;
    }
    

    Next, we use plainToInstance from class-transformer to convert Prisma's plain objects to instances of our UserEntity class.

    import { plainToInstance } from 'class-transformer';
    import { UserEntity } from './user.entity';
    import { PrismaClient } from '@prisma/client';
    
    const prisma = new PrismaClient();
    
    async function findUserById(id: number): Promise<UserEntity> {
      const user = await prisma.user.findUnique({ where: { id } });
      return plainToInstance(UserEntity, user);
    }
    

    Step 2: Map to DTOs Using AutoMapper

    Using AutoMapper, we can effortlessly map our Prisma model data to a UserDto now that it is in an instance of UserEntity. Make sure that the mapping from UserEntity to UserDto is enabled in your AutoMapper profile first.

    // automapper.profile.ts
    import { ProfileBase } from '@automapper/classes';
    import { UserDto } from './user.dto';
    import { UserEntity } from './user.entity';
    
    export class UserProfile extends ProfileBase {
      constructor(mapper) {
        super();
        mapper.createMap(UserEntity, UserDto);
      }
    }
    

    Then, you can perform the mapping in your service layer or wherever you need to transform your data.

    // userService.ts
    import { AutoMapper } from '@automapper/classes';
    import { UserDto } from './user.dto';
    import { findUserById } from './yourUserFinderFunction'; // This function uses plainToInstance
    
    async function getUserDtoById(id: number, mapper: AutoMapper): Promise<UserDto> {
      const userEntity = await findUserById(id);
      return mapper.map(userEntity, UserDto, UserEntity);
    }
    

    We can efficiently use AutoMapper to map these instances to DTOs by converting these models into class instances using plainToInstance.