#Response DTOs and Prisma selection

19 messages Ā· Page 1 of 1 (latest)

valid falcon
#

I am new to nestjs, coming from django and I am having a hard time understanding how to work with response dtos and prisma selection.

For example, I dont want all fields of User to be returned or serialized, just some, so I create a responsedto and apply it in my services. But then to optimize Prisma queries I also need to select the fields in the query.

Isnt this redundant and hard to maintain?

crisp folio
#

so, to avoid manually syncing dtos and prisma selections, you can automate selection based on the dto

#

you have to use a utility to generate prisma select from dto

#
function getPrismaSelect(dto: any) {
    return Object.keys(dto).reduce((acc, key) => {
        acc[key] = true;
        return acc;
    }, {} as Record<string, boolean>);
}
#

and define your dto

#
class UserResponseDto {
    id: number;
    name: string;
    email: string;
}
night lark
#

now your dto is married to the database though

crisp folio
valid falcon
#

This is exactly what I want, thanks @crisp folio

However I dont think this wont work for foreign keys or relation defined as nested Dtos

#

Isn't this use case common enough in the nestjs community?

crisp folio
#

the approach works well for flat dtos, but once you introduce relations, you need a more flexible way to handle it

#

so a common way to deal with this in nestjs is to extend the getPrismaSelect function to recursively process nested dtos

#
function getPrismaSelect(dto: any): Record<string, any> {
    return Object.keys(dto).reduce((acc, key) => {
        const value = dto[key];
        
        if (typeof value === 'function') {
            // If it's a class (nested DTO), call `getPrismaSelect` recursively
            acc[key] = { select: getPrismaSelect(new value()) };
        } else {
            acc[key] = true;
        }

        return acc;
    }, {} as Record<string, any>);
}
#

also this is usage with nested dtos

#
class ProfileResponseDto {
    bio: string;
    avatarUrl: string;
}

class UserResponseDto {
    id: number;
    name: string;
    email: string;
    profile: ProfileResponseDto;  // Nested DTO
}

const user = await this.prisma.user.findUnique({
    where: { id: userId },
    select: getPrismaSelect(new UserResponseDto()),
});

return user;
#

anyway, this way, your dtos remain structured and maintainable, and you dont have to manually sync them with prisma's selection

valid falcon
#

Great stuff Dan, will test it out. Appreciate it.

One last piece to the puzzle is making sure the dtos are referencing fields that do exist in prisma. Some kind of type check.