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

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