NestJSエンティティオブジェクトをDTOに変換する最良の方法を考えているだけです。
私が以下を持っているとしましょう:
import { IsString, IsNumber, IsBoolean } from 'class-validator';
import { Exclude } from 'class-transformer';
export class PhotoSnippetDto {
@IsNumber()
readonly id: number;
@IsString()
readonly name: string;
constructor(props) {
Object.assign(this, props);
}
}
export class Photo {
@IsNumber()
id: number;
@IsString()
name: string;
@IsString()
description: string;
@IsString()
filename: string;
@IsNumber()
views: number;
@IsBoolean()
isPublished: boolean;
@Exclude()
@IsString()
excludedPropery: string;
constructor(props) {
Object.assign(this, props);
}
}
@Controller()
export class AppController {
@Get()
@UseInterceptors(ClassSerializerInterceptor)
root(): PhotoSnippetDto {
const photo = new Photo({
id: 1,
name: 'Photo 1',
description: 'Photo 1 description',
filename: 'photo.png',
views: 10,
isPublished: true,
excludedPropery: 'Im excluded'
});
return new PhotoSnippetDto(photo);
}
}
ClassSerializerInterceptorが写真オブジェクトをDTOにシリアル化し、次のようなものを返すことを期待していました。
{
id: 1,
name: 'Photo 1'
}
しかし、私はまだすべてのプロパティを含む応答を取得しています:
{
id = 1,
name = 'Photo 1',
description = 'Photo 1 description',
filename = 'file.png',
views = 10,
isPublished = true
}
基本的に、DTOで定義されていないすべてのプロパティを削除します。
@Exclude()を使用するとClassSerializerInterceptorが完全に機能することを知っています。未定義のプロパティも削除されることを期待していました。
これについて最善の方法はありますか?私は次のようなことができることを知っています:
@Get('test')
@UseInterceptors(ClassSerializerInterceptor)
test(): PhotoSnippetDto {
const photo = new Photo({
id: 1,
name: 'Photo 1',
description: 'Photo 1 description',
filename: 'photo.png',
views: 10,
isPublished: true,
excludedPropery: 'Im excluded'
});
const { id, name } = photo;
return new PhotoSnippetDto({id, name});
}
しかし、応答に別のプロパティを追加したい場合は、クラスに新しいプロパティを追加するだけでは不十分です。それを行うためのより良い「ネスト方法」があるかどうか疑問に思っています。
可能なオプションの1つは、DTOオブジェクトを_@Exclude
_および_@Expose
_デコレーターでマークしてから、plainToClass
で変換を行うことです。
_@Exclude()
export class PhotoSnippetDto {
@Expose()
@IsNumber()
readonly id: number;
@Expose()
@IsString()
readonly name: string;
}
_
上記のように装飾したとすると、次のことができます:const dto = plainToClass(PhotoSnippetDto, photo);
結果のオブジェクトは、id
とname
のみが最終的なオブジェクトに表示される、期待どおりの形式です。後でさらにプロパティを公開することにした場合は、それらをDTOに追加して、_@Expose
_でタグを付けることができます。
このアプローチでは、_Object.assign
_を使用しているDTOからコンストラクターを削除することもできます。
Jesseの素晴らしい答えに基づいて、@ Exclude()と@Expose()を使用してDTOを作成し、公開されたプロパティ以外のすべてを削除しました。
import { IsString, IsEmail } from 'class-validator';
import { Exclude, Expose } from 'class-transformer';
@Exclude()
export class PhotoSnippetDto {
@Expose()
@IsNumber()
readonly id: number;
@Expose()
@IsString()
readonly name: string;
}
次に、plainToclassを呼び出してオブジェクトを変換する汎用変換インターセプターを作成しました。
import { Injectable, NestInterceptor, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { plainToClass } from 'class-transformer';
interface ClassType<T> {
new(): T;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<Partial<T>, T> {
constructor(private readonly classType: ClassType<T>) {}
intercept(context: ExecutionContext, call$: Observable<Partial<T>>, ): Observable<T> {
return call$.pipe(map(data => plainToClass(this.classType, data)));
}
}
次に、このインターセプターを使用して、データを任意のタイプに変換します。
@Get('test')
@UseInterceptors(new TransformInterceptor(PhotoSnippetDto))
test(): PhotoSnippetDto {
const photo = new Photo({
id: 1,
name: 'Photo 1',
description: 'Photo 1 description',
filename: 'photo.png',
views: 10,
isPublished: true,
excludedPropery: 'Im excluded'
});
return photo;
}
これは私が欲しかったものを私に与えます:
{
id: 1,
name: 'Photo 1'
}
間違いなく巣のように感じます!必要な場所で同じインターセプターを使用でき、DTOを変更するだけで応答を変更できます。
幸せな日々。
すべての型変換について、オブジェクトの変換を容易にするために、ライブラリ metamorphosis-nestjs を開発しました。
変換サービスに早期に登録された、コンバーターによって提供されたすべての変換のための注入可能な変換サービスの欠けている概念をNestJSに追加します(Javaアプリ)。
だからあなたの場合:
npm install --save @fabio.formosa/metamorphosis-nest
import { MetamorphosisNestModule } from '@fabio.formosa/metamorphosis-nest';
@Module({
imports: [MetamorphosisModule.register()],
...
}
export class MyApp{ }
import { Convert, Converter } from '@fabio.formosa/metamorphosis';
@Injectable()
@Convert(Photo, PhotoSnippetDto )
export default class PhotoToPhotoSnippetDtoConverter implements Converter<Photo,
PhotoSnippetDto> {
public convert(source: Photo): PhotoSnippetDto {
const target = new PhotoSnippetDto();
target.id = source.id;
target.name = source.name;
return target;
}
}
最後に、conversionServiceを注入して使用することができます。
const photoSnippetDto = <PhotoSnippetDto> await this.convertionService.convert(photo, PhotoSnippetDto);
この質問の他の回答に関連して、あなたはあなたの変換メソッドplainToClassに使うことができます:
@Injectable()
@Convert(Photo, PhotoSnippetDto )
export default class PhotoToPhotoSnippetDtoConverter implements Converter<Photo, PhotoSnippetDto> {
public convert(source: Photo): PhotoSnippetDto {
return plainToClass(PhotoSnippetDto, source);
}
}
必要に応じて、インターセプター内でもconversionServiceを呼び出すことができます。
READMEを取得して、 metamorphosis-nestjs の例とすべての利点を確認してください。