How to setup MinIO in a NestJS API to handle file uploads
Introduction
MinIO is an open source object storage server that is compatible with Amazon S3 cloud storage service. Using it in your NestJS API will allow you to store your files in it.
Requirements
Installation
Create a NestJS API
First, we need to create a NestJS API. To do so, we will use the NestJS CLI.
nest new <project-name>
Install the dependencies
To use MinIO in our NestJS API, we will use the MinIO client.
npm i minio && npm i -D @types/minio
We will also need to install the type definitions for Multer, which is a middleware for handling multipart/form-data.
npm i -D @types/multer
Install MinIO
To install MinIO, we will use Docker Compose.
touch docker-compose.yml
Fill your docker-compose.yml
file with the following content:
version: '3.8'
services:
minio:
image: docker.io/bitnami/minio:2022
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: supersecret
ports:
- '9000:9000'
- '9001:9001'
volumes:
- 'minio_data:/data'
volumes:
minio_data:
driver: local
Then, type the following command to start MinIO:
docker-compose up -d
You are now able to access the MinIO dashboard at http://127.0.0.1:9001.
Set up the NestJS Config module
To use MinIO in our NestJS API, we will use the NestJS Config module.
npm i @nestjs/config
Then, we will create a .env file in the root of our project.
touch .env
Fill your .env
file with the following content, and make sure to replace the keys with your own :
MINIO_ENDPOINT='localhost'
MINIO_PORT=9000
MINIO_ACCESS_KEY=<my_access_key>
MINIO_SECRET_KEY=<my_secret_key>
MINIO_USE_SSL=false
MINIO_BUCKET_NAME=<my_bucket_name>
After doing that, we will import the ConfigModule in our app.module.ts
file.
import { Module } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true
})
]
})
export class AppModule {}
Usage
Create a MinIO service
To use MinIO in our NestJS API, we will create a MinIO service.
nest g service minio
Fill your minio.service.ts
file with the following content:
import { Injectable } from '@nestjs/common'
import * as Minio from 'minio'
@Injectable()
class MinioService {
private minioClient: Minio.Client
private bucketName: string
constructor(private readonly configService: ConfigService) {
this.minioClient = new Minio.Client({
endPoint: this.configService.get('MINIO_ENDPOINT'),
port: Number(this.configService.get('MINIO_PORT')),
useSSL: this.configService.get('MINIO_USE_SSL') === 'true',
accessKey: this.configService.get('MINIO_ACCESS_KEY'),
secretKey: this.configService.get('MINIO_SECRET_KEY')
})
this.bucketName = this.configService.get('MINIO_BUCKET_NAME')
}
async createBucketIfNotExists() {
const bucketExists = await this.minioClient.bucketExists(this.bucketName)
if (!bucketExists) {
await this.minioClient.makeBucket(this.bucketName, 'eu-west-1')
}
}
async uploadFile(file: Express.Multer.File) {
const fileName = `${Date.now()}-${file.originalname}`
await this.minioClient.putObject(
this.bucketName,
fileName,
file.buffer,
file.size
)
return fileName
}
async getFileUrl(fileName: string) {
return await this.minioClient.presignedUrl('GET', this.bucketName, fileName)
}
async deleteFile(fileName: string) {
await this.minioClient.removeObject(this.bucketName, fileName)
}
}
Feel free to extend this service to suit your needs.
Example
To use our MinIO service, we will fill our app.controller.ts
file, in order to handle file uploads and downloads of book covers.
// app.controller.ts
import {
Controller,
Get,
Post,
Param,
Delete,
UploadedFile,
UseInterceptors
} from '@nestjs/common'
@Controller()
class AppController {
constructor(private readonly minioService: MinioService) {}
@Post('covers')
@UseInterceptors(FileInterceptor('file'))
async uploadBookCover(@UploadedFile() file: Express.Multer.File) {
await this.minioService.createBucketIfNotExists()
const fileName = await this.minioService.uploadFile(file)
return fileName
}
@Get('covers/:fileName')
async getBookCover(@Param('fileName') fileName: string) {
const fileUrl = await this.minioService.getFileUrl(fileName)
return fileUrl
}
@Delete('covers/:fileName')
async deleteBookCover(@Param('fileName') fileName: string) {
await this.minioService.deleteFile(fileName)
return fileName
}
}
Conclusion
You are now able to use MinIO in your NestJS API and store your files in it.
If you have any questions, feel free to reach me.
If you liked this article, follow me on Twitter @alexisbcz for more content like this.