DEV Community

Cover image for Minio integration with nestjs | file upload & retrieve
Manuchehr
Manuchehr

Posted on

1

Minio integration with nestjs | file upload & retrieve

What is minio? Minio is *free, open-source, scalable S3 compatible object storage.

First of all, let's setup minio in docker container. I use bitnami's minio package. Add minio service to your docker-compose.yaml:

services:
  minio:
    image: bitnami/minio:2024.11.7
    restart: always
    ports:
      - ${MINIO_PORT}:${MINIO_PORT}
      - ${MINIO_CONSOLE_PORT}:${MINIO_CONSOLE_PORT}
    environment:
      MINIO_ROOT_USER: ${MINIO_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
      MINIO_DEFAULT_BUCKETS: ${MINIO_BUCKET}
      MINIO_API_PORT_NUMBER: ${MINIO_PORT}
      MINIO_CONSOLE_PORT_NUMBER: ${MINIO_CONSOLE_PORT}
    volumes:
      - minio-data:/bitnami/minio/data

volumes:
  minio-data:
    driver: local

Enter fullscreen mode Exit fullscreen mode

Let me explain this quickly:

  1. ports: MINIO_PORT is minio's port for API requests, while as the name says MINIO_CONSOLE_PORT is minio's dashboard port:
    Minio console

  2. MINIO_ROOT_USER & MINIO_ROOT_PASSWORD are for login credentials to login minio console dashboard.

For more information about env variables of minio docker image, visit bitnami's minion docker image

Add these variables to your .env file:

MINIO_PORT=9000
MINIO_CONSOLE_PORT=8000
MINIO_USER="admin"
MINIO_PASSWORD="veryhardpassword"
MINIO_BUCKET="main"
MINIO_ENDPOINT="localhost"
MINIO_ACCESS_KEY=""
MINIO_SECRET_KEY=""
Enter fullscreen mode Exit fullscreen mode

To obtain access and secret keys:

  1. Go to minio console (it's http://localhost:8000 in our example) & sign in.

  2. Go Access Keys & Create access key
    Minio console access keys
    Minio console create access key

  3. Copy & add access/secret keys to .env

Setup minio client in nestjs

Install npm minio package

pnpm add minio
Enter fullscreen mode Exit fullscreen mode

Create minio/minio.decorator.ts & minio/minio.module.ts

import { Inject } from '@nestjs/common';

export const MINIO_TOKEN = 'MINIO_INJECT_TOKEN';

export function InjectMinio(): ParameterDecorator {
  return Inject(MINIO_TOKEN);
}
Enter fullscreen mode Exit fullscreen mode
import { Global, Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { MINIO_TOKEN } from './minio.decorator';
import * as Minio from 'minio';

@Global()
@Module({
  exports: [MINIO_TOKEN],
  providers: [
    {
      inject: [ConfigService],
      provide: MINIO_TOKEN,
      useFactory: async (
        configService: ConfigService,
      ): Promise<Minio.Client> => {
        const client = new Minio.Client({
          endPoint: configService.getOrThrow("MINIO_ENDPOINT"),
          port: +configService.getOrThrow("MINIO_PORT"),
          accessKey: configService.getOrThrow("MINIO_ACCESS_KEY"),
          secretKey: configService.getOrThrow("MINIO_SECRET_KEY"),
          useSSL: false,
        });
        return client;
      },
    },
  ],
})
export class MinioModule {}
Enter fullscreen mode Exit fullscreen mode

If you want to use minio in only one module (files module for example) you can remove @Global() decorator.

Import it to app.module.ts:

@Module({
  imports: [
    ...
    MinioModule,
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Now let's test it out. Inject minio to your server (files.service.ts for example)

import { Injectable } from '@nestjs/common';
import { randomUUID } from 'crypto';
import * as Minio from 'minio';
import { InjectMinio } from 'src/minio/minio.decorator';

@Injectable()
export class FilesService {
  protected _bucketName = 'main';

  constructor(@InjectMinio() private readonly minioService: Minio.Client) {}

  async bucketsList() {
    return await this.minioService.listBuckets();
  }

  async getFile(filename: string) {
    return await this.minioService.presignedUrl(
      'GET',
      this._bucketName,
      filename,
    );
  }

  uploadFile(file: Express.Multer.File) {
    return new Promise((resolve, reject) => {
      const filename = `${randomUUID().toString()}-${file.originalname}`;
      this.minioService.putObject(
        this._bucketName,
        filename,
        file.buffer,
        file.size,
        (error, objInfo) => {
          if (error) {
            reject(error);
          } else {
            resolve(objInfo);
          }
        },
      );
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

and it's my files.controller.ts

import { Body, Controller, Get, Param, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
import { FilesService } from './files.service';
import { FileInterceptor } from '@nestjs/platform-express';

@Controller('files')
export class FilesController {
  constructor(readonly service: FilesService) {}

  @Get('buckets')
  bucketsList() {
    return this.service.bucketsList();
  }

  @Get('file-url/:name')
  getFile(@Param('name') name: string) {
    return this.service.getFile(name);
  }

  @Post('upload')
  @UseInterceptors(FileInterceptor('file'))
  uploadFile(
    @UploadedFile('file') file: Express.Multer.File,
  ) {
    payload.file = file;
    return this.service.uploadFile(file);
  }
}
Enter fullscreen mode Exit fullscreen mode

Send a file to POST /files/upload endpoint. It should return this response:
Minio integration with nestjs | file upload & retrieve

Let's visit minio console and go main bucket to check if file was uploaded
Minio integration with nestjs | file upload & retrieve
It's uploaded 🥳

Now let's test generating file URL for uploaded file (retrieve file) GET /files/file-url/<file_name>. It should return URL to retrieve file something like this:
http://localhost:8000/main/2254a964-28dd-4c35-8494-5e767693cd26-iOS%2BSyllabus.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=m0iWmenUgJi2xBcFNNPF%2F20241129%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20241129T065735Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=a052b98dcfa95a4d1cdb1c9b0b91d1263440adf184aab5238e6d572c0f42e7c5
If you visit this URL you can retrieve that file.

You can find all minio methods with this link


*free - MinIO is free for self-hosted use under the GNU AGPLv3 license, which allows you to run and modify the software as long as any modifications you make are also made available under the same license. This license ensures freedom to use, study, share, and modify the software.

That's all. Thanks for reading I hope it's beneficial for you. If you find any mistake or issue in this post, feel free to comment or let me know.

Billboard image

Imagine monitoring that's actually built for developers

Join Vercel, CrowdStrike, and thousands of other teams that trust Checkly to streamline monitor creation and configuration with Monitoring as Code.

Start Monitoring

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay