NestJS + OpenAPI でのファイルアップロード
こんにちは。
株式会社アルダグラムの安政です。
NestJS 内の OpenAPI の機構を用いて、日々開発しています。
ファイルをアップロードする処理を実装する機会がありましたので、実装方法などを紹介したいと思います。
この記事では、
- ファイルアップロードの基本的な処理
- 受け取るファイルの MimeType を用いてのバリデーション処理
- OpenAPI (Swagger)上から利用するためのデコレーター定義
の順でご紹介します。
また実装にあたってはこちらのサイトを参考にさせていただきました。
https://notiz.dev/blog/type-safe-file-uploads
ファイルアップロードの基本処理
ファイルアップロードに関する公式の記事としてはこちらです。
https://docs.nestjs.com/techniques/file-upload
Basic example に import を加えたソースコードがこちらになります。
import {
Controller,
Post,
UploadedFile,
UseInterceptors
} from '@nestjs/common'
import { FileInterceptor } from '@nestjs/platform-express'
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
console.log(file);
}
NestJS を Express ベースで動かしているので、Multer を用いてリクエストを受け取ることになります。
Interceptor
の機構( UseInterceptors
)を用いることで、リクエストを受け取る前にロジックをバインドすることができるので、ファイルの取得処理を挟むことができています。
ファイルを受け取るロジックは FileInterceptor
内にて書かれています。
これだけの記載でファイルを受け取る処理が完結するのはとてもありがたいです。
ファイルの MimeType によるバリデーション処理
FileInterceptor
に対して、 fileFilter
を指定することでバリデーション処理を加えることができます。
以下が filter
を指定したコードです。
import { fileMimetypeFilter } from './file-mimetype-filter'
@Post('upload')
@UseInterceptors(
FileInterceptor('file', { fileFilter: fileMimetypeFilter('image') })
)
uploadFile(@UploadedFile() file: Express.Multer.File) {
console.log(file);
}
fileMimetypeFilter
を作成・指定しています。
今回の場合は image
という文字列が mimeType に含まれるファイルのみを通す処理となっています。
# fileMimetypeFilter
import { UnsupportedMediaTypeException } from '@nestjs/common'
import Request from 'express-http-context'
// 指定された MimeType のみ通す
export function fileMimetypeFilter(...mimetypes: string[]) {
return (
request: Request,
file: Express.Multer.File,
callback: (error: Error | null, acceptFile: boolean) => void
) => {
if (mimetypes.some(m => file.mimetype.includes(m))) {
callback(null, true)
} else {
// 例外処理を返す
callback(
new UnsupportedMediaTypeException(
`File type is not matching: ${mimetypes.join(', ')}`
),
false
)
}
}
}
こちらを応用することで、アップロードできないファイルを指定することや、特定の拡張子を含んでいたら処理を行うなどができるかと思います。
OpenAPI 用のデコレーター定義
最後に、Swagger 上でファイルのアップロード処理を追加する定義の紹介です。
# image.controller.ts
import {
Controller,
Post,
Body,
UploadedFile,
UseInterceptors
} from '@nestjs/common'
import { ApiConsumes } from '@nestjs/swagger'
import { FileInterceptor } from '@nestjs/platform-express'
import { fileMimetypeFilter } from './file-mimetype-filter'
import { CreateImageRequest } from './dto/create-image.dto'
export class ImagesController {
@Post()
@ApiConsumes('multipart/form-data')
@UseInterceptors(
FileInterceptor('file', { fileFilter: fileMimetypeFilter('image') })
)
async create(
@Body() createImageRequest: CreateImageRequest,
@UploadedFile() file: Express.Multer.File
) {
console.log(file)
}
}
ApiConsumes
を指定することで、multipart のフォームを有効にしています。
また、 CreateImageRequest
内にて、 ApiProperty
をこのようにすることで、 Request Body
上にフォームが出てくることになります。
# dto/create-image.dto.ts
import { ApiProperty } from '@nestjs/swagger'
export class CreateImageRequest {
@ApiProperty({
description: 'アップロードするファイル',
type: 'file',
properties: {
file: {
type: 'string',
format: 'binary'
}
}
})
file!: Express.Multer.File
}
最後に
今回はソースコードの紹介がメインになってしまいましたが、いかがでしたでしょうか。
NestJS はライブラリが充実しているので、テクニックを組み合わせることで簡単に実装を行うことが可能です。
しかし、ドキュメントが最低限な記載である傾向もあるので、ライブラリの中まで読み込まないと、拡張ができないこともあります。
みなさまの開発時の参考になりましたら、幸いです。
株式会社アルダグラムのTech Blogです。 世界中のノンデスクワーク業界における現場の生産性アップを実現する現場DXサービス「KANNA」を開発しています。 採用情報はこちら herp.careers/v1/aldagram0508/
Discussion