DEV Community

Cover image for Upload image with NestJS Minio S3
Vodnyy
Vodnyy

Posted on

Upload image with NestJS Minio S3

MinIO - is a cloud-native object store built to run on any infrastructure - public, private or edge clouds. Primary use cases include data lakes, databases, AI/ML, SaaS applications and fast backup & recovery. MinIO is dual licensed under GNU AGPL v3 and commercial license. To learn more, visit www.min.io.

1. Create new project nest new minio-example.

2. Download packages npm i nestjs-minio-s3 @nestjs/config multer, and npm i --save-dev @types/multer.

3. Set docker environment variables in .docker.env:

MINIO_ROOT_USER="minioadmin"
MINIO_ROOT_PASSWORD="minioadmin"
Enter fullscreen mode Exit fullscreen mode

4. Add file docker-compose.yml and new Minio container:

services:
  minio:
    image: minio/minio:latest
    container_name: minio
    ports:
      - '9000:9000'
      - '9001:9001'
    env_file:
      - .docker.env
    command: server /data --console-address ":9001"
    volumes:
      - minio:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

volumes:
  minio:
    driver: local
Enter fullscreen mode Exit fullscreen mode

5. Start container docker compose up -d

6. Add global environment variables in .env:

MINIO_HOST="localhost"
MINIO_PORT=9000
MINIO_SSL=false
MINIO_USER="minioadmin"
MINIO_PASSWORD="minioadmin"
MINIO_REGION="us-east-1"
Enter fullscreen mode Exit fullscreen mode

7. Include MinioModulein AppModule:

import { Module } from '@nestjs/common';
import {MinioModule} from "nestjs-minio-s3";
import {ConfigModule, ConfigService} from "@nestjs/config";

import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    MinioModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        host: configService.get('MINIO_HOST')!,
        port: +configService.get('MINIO_PORT')!,
        useSSL: configService.get('MINIO_SSL') === 'true',
        accessKey: configService.get('MINIO_USER')!,
        secretKey: configService.get('MINIO_PASSWORD')!,
        region: configService.get('MINIO_REGION') || 'us-east-1',
      }),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

8. Add new files AvatarsModule, AvatarsService, AvatarsController.

9. Use MinioModule.forFeaturefor creating new Buckets S3:

import {Module} from "@nestjs/common";

import {AvatarsService} from "./avatars.service";
import {AvatarsController} from "./avatars.controller";
import {MinioModule} from "nestjs-minio-s3";

@Module({
    imports: [MinioModule.forFeature({bucketName: 'avatars', policy: 'public'})],
    providers: [AvatarsService],
    controllers: [AvatarsController],
})
export class AvatarsModule {}
Enter fullscreen mode Exit fullscreen mode

10. Use @InjectBucket()in your services:

import {Injectable} from "@nestjs/common";
import {InjectBucket} from "nestjs-minio-s3/dist/decorators/inject-bucket.decorator";
import {MinioService} from "nestjs-minio-s3";

@Injectable()
export class AvatarsService {
    constructor(@InjectBucket() private readonly bucketName: string,
                private readonly minioService: MinioService) {
    }

    async uploadAvatar(file: Express.Multer.File) {
        const key = `${Date.now()}.${file.originalname.split('.').pop()}`;

        return this.minioService.upload(
            this.bucketName,
            key,
            file.buffer,
            file.mimetype,
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

11. And implementing method upload image in controller:

import {Controller, Post, UploadedFile, UseInterceptors} from "@nestjs/common";
import type {Express} from 'express';

import {AvatarsService} from "./avatars.service";
import {FileInterceptor} from "@nestjs/platform-express";

@Controller('avatars')
export class AvatarsController {
    constructor(private readonly avatarsService: AvatarsService) {}

    @Post()
    @UseInterceptors(FileInterceptor('avatar'))
    uploadAvatar(@UploadedFile() file: Express.Multer.File) {
        return this.avatarsService.uploadAvatar(file);
    }
}
Enter fullscreen mode Exit fullscreen mode

12. After completing the request, view the changes at the address http://localhost:9001 with authorization data: username: minioadmin, password: minioadmin

Minio package: nestjs-minio-s3.
GitHub Repository this example: https://github.com/Vodnyy143/nestjs-minio-example.git.

Top comments (4)

Collapse
 
vaviloff profile image
Vaviloff •

Nice writeup, could you explain the need for InjectBucket here, as opposed to just using bucket name from a config. Does it auto-create a bucket maybe?

Collapse
 
vodnyy profile image
Vodnyy • • Edited

Good question! The InjectBucketapproach gives you flexibility to work with multiple buckets for different categories. For example, you might want one bucket for avatars, another for post photos, and a third for something else.
Also, something I didn't mention in the article - you can create buckets like this:

MinioModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        host: configService.get('MINIO_HOST')!,
        port: +configService.get('MINIO_PORT')!,
        useSSL: configService.get('MINIO_SSL') === 'true',
        accessKey: configService.get('MINIO_USER')!,
        secretKey: configService.get('MINIO_PASSWORD')!,
        region: configService.get('MINIO_REGION') || 'us-east-1',
        buckets: [{name: 'photos', policy: 'public'}, {name: 'emails', policy: 'private'}],
      }),
    })
Enter fullscreen mode Exit fullscreen mode

This way you can inject specific buckets where needed and manage different access policies for each one.

Collapse
 
vaviloff profile image
Vaviloff •

Oh, wow this is certainly very useful, thanks

Collapse
 
vodnyy profile image
Vodnyy •

Это великолепно