NestJS Fastify - Best way to upload file on S3

1.1k views Asked by At

So, I have a few issues. main.ts

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
    { bufferLogs: true, bodyParser: false },
  );

  const configService = app.get(ConfigService);
  const PORT = configService.get('port');

  app.register(fmp, { attachFieldsToBody: true });

  await app.listen(PORT);

  logger.log(`Application is running on: ${await app.getUrl()}`);
}

author.controller.ts

@Post('/create')
  async createAuthor(
    @AuthUser() user: any,
    @Body() createAuthorDto: CreateAuthorDto,
  ): Promise<BaseResponseEntity> {
    return await this.authorsService.createAuthor(
      user,
      createAuthorDto,
    );
  }

I had to add { attachFieldsToBody: true } or else the createAuthorDto becomes undefined, but after adding that, the createAuthorDto becomes something like this:

<ref *1> {
  first_name: {
    fieldname: 'first_name',
    mimetype: undefined,
    encoding: '7bit',
    value: 'Deep',
    fieldnameTruncated: false,
    valueTruncated: false,
    fields: [Circular *1]
  },
  last_name: {
    fieldname: 'last_name',
    mimetype: undefined,
    encoding: '7bit',
    value: 'Mandal',
    fieldnameTruncated: false,
    valueTruncated: false,
    fields: [Circular *1]
  },
  email_id: {
    fieldname: 'email_id',
    mimetype: undefined,
    encoding: '7bit',
    value: '[email protected]',
    fieldnameTruncated: false,
    valueTruncated: false,
    fields: [Circular *1]
  },
  passwd: {
    fieldname: 'passwd',
    mimetype: undefined,
    encoding: '7bit',
    value: '123',
    fieldnameTruncated: false,
    valueTruncated: false,
    fields: [Circular *1]
  },
  delete_allowed: {
    fieldname: 'delete_allowed',
    mimetype: undefined,
    encoding: '7bit',
    value: 'true',
    fieldnameTruncated: false,
    valueTruncated: false,
    fields: [Circular *1]
  },
  posted_by_admin: {
    fieldname: 'posted_by_admin',
    mimetype: undefined,
    encoding: '7bit',
    value: 'true',
    fieldnameTruncated: false,
    valueTruncated: false,
    fields: [Circular *1]
  },
  status: {
    fieldname: 'status',
    mimetype: undefined,
    encoding: '7bit',
    value: '1',
    fieldnameTruncated: false,
    valueTruncated: false,
    fields: [Circular *1]
  },
  profile_image: {
    fieldname: 'profile_image',
    filename: 'fields.png',
    encoding: '7bit',
    mimetype: 'image/png',
    file: FileStream {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 5,
      _maxListeners: undefined,
      bytesRead: 17081,
      truncated: false,
      _read: [Function (anonymous)],
      [Symbol(kCapture)]: false
    },
    fields: [Circular *1],
    _buf: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 02 e5 00 00 00 a8 08 02 00 00 00 c8 01 07 32 00 00 00 03 73 42 49 54 08 08 08 db e1 4f e0 00 00 ... 17031 more bytes>,
    toBuffer: [AsyncFunction: toBuffer]
  },
  logo: {
    fieldname: 'logo',
    filename: 'london.png',
    encoding: '7bit',
    mimetype: 'image/png',
    file: FileStream {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 5,
      _maxListeners: undefined,
      bytesRead: 160296,
      truncated: false,
      _read: [Function (anonymous)],
      [Symbol(kCapture)]: false
    },
    fields: [Circular *1],
    _buf: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 05 56 00 00 03 00 08 02 00 00 00 40 5c ab 95 00 00 00 03 73 42 49 54 08 08 08 db e1 4f e0 00 00 ... 160246 more bytes>,
    toBuffer: [AsyncFunction: toBuffer]
  }
}

While my actual createAuthorDto is something like this:

export class CreateAuthorDto {
  @IsString()
  first_name: string;

  @IsString()
  last_name: string;

  @IsString()
  email_id: string;

  @IsString()
  passwd: string;

  passdis?: string;

  @IsBoolean()
  delete_allowed: boolean;

  @IsBoolean()
  posted_by_admin: boolean;

  @IsNumber()
  status: number;
}

I can work with what I'm getting but I feel like there is a better way to handle multipart/form data when using nestjs fastify.

2

There are 2 answers

0
Kai On

You can actually follow the file upload guide on the official nestjs doc.

I think the problem in your code is that the output of createAuthorDto has too many redundant properties.

as you can see on the link that I shared, you can intercept the request with @UseInterceptors() which extracts a file and pass it with Express.Multer.File. Of course the other data will simply pass through your custom dto class.

@Post('create')
@UseInterceptors(FileInterceptor('file'))
uploadFile(
  @UploadedFile() file: Express.Multer.File,
  @Body() createAuthorDto: CreateAuthorDto
) {
  console.log(file);
  console.log(createAuthorDto);
}

but in your case, you need to receive two files(profile_image, logo). You can also handle multiple images with specific field names like this.

@Post('create')
@UseInterceptors(FileFieldsInterceptor([
  { name: 'profile_image', maxCount: 1 },
  { name: 'logo', maxCount: 1 },
]))
uploadFile(
  @UploadedFiles() files: { profile_image?: Express.Multer.File[], logo?: Express.Multer.File[] }, 
  @Body() createAuthorDto: CreateAuthorDto,
) {
  console.log(files);
  console.log(createAuthorDto);
}

Now you can handle two different types of data separately, File and Dto without the redundant properties.

0
iimm On
app.register(fmp, { addToBody: true });