Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Serializing responses

Serialization is turning an object into JSON before sending it to the client. By default NestJS sends the whole object with every field. The built-in ClassSerializerInterceptor together with the class-transformer library lets you mark the entity fields that must not be shown — for example, the password hash.

Marking fields

class-transformer was already installed in the validation lesson. Add the @Exclude() decorator to the password:

// src/users/entities/user.entity.ts
import { Exclude } from 'class-transformer';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
 
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;
 
  @Column({ unique: true })
  email: string;
 
  @Column()
  name: string;
 
  @Exclude()
  @Column()
  password: string;
 
  @Column({ default: 'user' })
  role: string;
}

The decorator alone changes nothing: it only marks the field for the interceptor.

Enabling the interceptor

The interceptor is enabled for the whole application in src/main.ts:

// src/main.ts
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';
import { NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from './app.module';
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

Now the registration controller can return the whole entity:

// src/users/users.controller.ts
@Post()
register(@Body() dto: CreateUserDto) {
  return this.usersService.create(dto);
}

Send POST /users — the response has id, email, name and role, and no password field. Listing fields by hand, as in the users lesson, is no longer needed.

Computed fields

The @Expose() decorator on a getter adds a field to the response that is not in the database:

// src/posts/entities/post.entity.ts
import { Expose } from 'class-transformer';
 
@Expose()
get excerpt(): string {
  return this.content.slice(0, 100);
}

Responses with posts now include an excerpt field — the first 100 characters of the text.

Main decorators

DecoratorWhat it does
@Exclude()Removes the field from the response
@Expose()Adds a getter or field to the response
@Transform(({ value }) => ...)Changes the field value before sending
@Type(() => Comment)Names the class of nested objects

Common mistake

The interceptor works only with class instances. If the service returns a plain object, for example { ...user } or the result of JSON.parse(), it carries no @Exclude() marks, and the password goes to the client. Return entities obtained from the repository, and if you need to build a response by hand, create a class instance with plainToInstance(User, data) from class-transformer.

Thanks for reading!

Donate to the site — we work hard for you.