DEV Community

Leonardo Minora
Leonardo Minora

Posted on • Edited on

NestJS - criar endpoint para upload de 1 arquivo

Informações gerais

  • aula introdutória de endpoint para upload de arquivo para os alunos de programação orientada a serviços, do 4o ano de infoweb, do CNAT-IFRN
  • repositório de código
  • código final zip

objetivo

  • criar 1 endpoint para API de upload de arquivo

notas de aula

  1. criar um projeto nestjs e instalar a lib necessária
  2. executar (levantar) a api
  3. configurar a API para documentar com swagger
  4. criar módulo nestjs para upload
  5. programar endpoint para upload de 1 arquivo
  6. Programar o serviço para processar e responder ao endpoint de upload

1. criar um projeto nestjs e instalar a lib necessária

## cria novo projeto nestjs
npx @nestjs/cli --package-manager npm new upload-api

## após criado o projeto, e em tudo terminando ok
## acessa a pasta do projeto nestjs
cd upload-api

## instala a lib em modo desenvolvimento
npm install -D @types/multer

npm install @nestjs/swagger

Enter fullscreen mode Exit fullscreen mode

o resultado deve parecer com o terminal abaixo.

$ npx @nestjs/cli --package-manager npm new upload-api
⚡  We will scaffold your app in a few seconds..

CREATE upload-api/.eslintrc.js (663 bytes)
CREATE upload-api/.prettierrc (51 bytes)
CREATE upload-api/README.md (3340 bytes)
CREATE upload-api/nest-cli.json (171 bytes)
CREATE upload-api/package.json (1955 bytes)
CREATE upload-api/tsconfig.build.json (97 bytes)
CREATE upload-api/tsconfig.json (546 bytes)
CREATE upload-api/src/app.controller.spec.ts (617 bytes)
CREATE upload-api/src/app.controller.ts (274 bytes)
CREATE upload-api/src/app.module.ts (249 bytes)
CREATE upload-api/src/app.service.ts (142 bytes)
CREATE upload-api/src/main.ts (208 bytes)
CREATE upload-api/test/app.e2e-spec.ts (630 bytes)
CREATE upload-api/test/jest-e2e.json (183 bytes)

✔ Installation in progress... ☕

🚀  Successfully created project upload-api
👉  Get started with the following commands:

$ cd upload-api
$ npm run start:dev


                          Thanks for installing Nest 🙏
                 Please consider donating to our open collective
                        to help us maintain this package.


               🍷  Donate: https://opencollective.com/nest

$ cd upload-api

[upload-api]$ npm i -D @types/multer

added 1 package, and audited 687 packages in 2s

106 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

[upload-api]$

Enter fullscreen mode Exit fullscreen mode

2. executar (levantar) a api

levantar a api em modo de desenvolvimento para que no momento que houver mudança nos códigos-fonte, a API ser re-lançada.

executar a ferramenta npm com o comando run para executar o script start:dev.

[upload-api]$ npm run start:dev

Enter fullscreen mode Exit fullscreen mode

resultado do comando acima deve ser algo parecido com o terminal abaixo.

[15:31:39] Starting compilation in watch mode...

[15:31:41] Found 0 errors. Watching for file changes.

[Nest] 620648  - 07/09/2024, 15:31:41     LOG [NestFactory] Starting Nest application...
[Nest] 620648  - 07/09/2024, 15:31:41     LOG [InstanceLoader] AppModule dependencies initialized +13ms
[Nest] 620648  - 07/09/2024, 15:31:41     LOG [RoutesResolver] AppController {/}: +5ms
[Nest] 620648  - 07/09/2024, 15:31:41     LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 620648  - 07/09/2024, 15:31:41     LOG [NestApplication] Nest application successfully started +2ms

Enter fullscreen mode Exit fullscreen mode

3. configurar a API para documentar com swagger

editar o arquivo /src/main.ts para colocar a configuração do swagger.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
++import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

++  const config = new DocumentBuilder()
++    .setTitle('Infoweb - Upload API')
++    .setDescription('API exemplo de upload de arquivos')
++    .setVersion('1.0')
++    .build();
++  const document = SwaggerModule.createDocument(app, config);
++  SwaggerModule.setup('docs', app, document);

  await app.listen(3000);
}
bootstrap();

Enter fullscreen mode Exit fullscreen mode
  • DocumentBuilder é responsável por configurar a documentação da API.
  • SwaggerModule.createDocument é responsável por construir a documentação.
  • SwaggerModule.setup cria um endpoint no app e vincula a documentação da API.

4. criar módulo nestjs para upload

abrir um 2o terminal, e executar o @nestjs/cli com o comando generate resource, podendo abreviar g res.

[upload-api]$ npx @nestjs/cli generate resource upload --no-spec

Enter fullscreen mode Exit fullscreen mode

o resultado do comando deverá parecer com o terminal abaixo.

[upload-api]$ npx @nestjs/cli generate resource upload --no-spec
? What transport layer do you use? REST API
? Would you like to generate CRUD entry points? No
CREATE src/upload.controller.ts (216 bytes)
CREATE src/upload.module.ts (255 bytes)
CREATE src/upload.service.ts (90 bytes)
UPDATE package.json (2020 bytes)
UPDATE src/app.module.ts (309 bytes)
✔ Packages installed successfully.

Enter fullscreen mode Exit fullscreen mode

ao final, o terminal que esta executando a API deve adicionar a rota /upload como [RoutesResolver], ver terminal abaixo como exemplo.

[09:45:33] File change detected. Starting incremental compilation...

[09:45:33] Found 0 errors. Watching for file changes.

[Nest] 14414  - 13/09/2024, 09:45:35     LOG [NestFactory] Starting Nest application...
[Nest] 14414  - 13/09/2024, 09:45:35     LOG [InstanceLoader] AppModule dependencies initialized +12ms
[Nest] 14414  - 13/09/2024, 09:45:35     LOG [InstanceLoader] UploadModule dependencies initialized +1ms
[Nest] 14414  - 13/09/2024, 09:45:35     LOG [RoutesResolver] AppController {/}: +170ms
[Nest] 14414  - 13/09/2024, 09:45:36     LOG [RouterExplorer] Mapped {/, GET} route +20ms
[Nest] 14414  - 13/09/2024, 09:45:36     LOG [RoutesResolver] UploadController {/upload}: +9ms
[Nest] 14414  - 13/09/2024, 09:45:36     LOG [NestApplication] Nest application successfully started +32ms

Enter fullscreen mode Exit fullscreen mode

5. programar endpoint para upload de 1 arquivo

para fazer o upload de um único arquivo no endpoint de uma API usando nestjs, preciso vincular o interceptor FileInterceptor() ao endpoint e extrair esse arquivo da requisição usando o decorador @UploadedFile().

editar o arquivo src/uplaod/upload.controller.ts para inserir os códigos abaixo.

++import {
++  Controller,
++  Post,
++  UploadedFile,
++  UseInterceptors,
++} from '@nestjs/common';
import { UploadService } from './upload.service';
++import { FileInterceptor } from '@nestjs/platform-express';
++import {
++  ApiBody,
++  ApiConsumes,
++  ApiOperation,
++  ApiResponse,
++  ApiTags,
++} from '@nestjs/swagger';

@Controller('upload')
++@ApiTags('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

++  @Post('exemplo-simples')
++  @UseInterceptors(FileInterceptor('arquivo'))
++  @ApiConsumes('multipart/form-data')
++  @ApiBody({
++    schema: {
++      type: 'object',
++      properties: {
++        arquivo: {
++          type: 'string',
++          format: 'binary',
++        },
++      },
++    },
++  })
++  @ApiOperation({ summary: 'Exemplo de upload de 1 arquivo qualquer' })
++  @ApiResponse({ status: 201, description: 'Arquivo enviado com sucesso.' })
++  @ApiResponse({ status: 400, description: 'Erro no envio do arquivo.' })
++  uploadArquivoSimples(@UploadedFile() arq: Express.Multer.File) {
++    console.log(arq);
++
++    return {
++      estado: 'ok',
++    };
++  }
}

Enter fullscreen mode Exit fullscreen mode

os decorators novos são:

  • swagger
    • @ApiTags cria uma divisão na documentação da API organizada pela string passada como parâmetro.
    • @ApiConsumes
    • @ApiBody configura o que capturar do corpo da mensagem de requisição
    • @ApiOperation configura como será descrito o endpoint na documentação
    • @ApiResponse configura as respostas na documentação
  • nestjs
    • @UseInterceptors usa um interceptors que tem a função de interceptar as requisições antes que ela chegue na função que trata o endpoint.
    • @UploadedFile trabalha em conjunto com interceptor para extração do arquivo da mensagem de requisição.

FileInterceptor é um interceptor padrão do nestjs para extrair um arquivo da mensagem de requisição. Este interceptor trabalha junto do decorator @UploadedFile. É definido aqui o nome do campo do formulário que referencia o arquivo.

o console onde executa a API deve parecer como o terminal abaixo.

[10:14:23] File change detected. Starting incremental compilation...

[10:14:23] Found 0 errors. Watching for file changes.

[Nest] 23723  - 13/09/2024, 10:14:23     LOG [NestFactory] Starting Nest application...
[Nest] 23723  - 13/09/2024, 10:14:23     LOG [InstanceLoader] AppModule dependencies initialized +11ms
[Nest] 23723  - 13/09/2024, 10:14:23     LOG [InstanceLoader] UploadModule dependencies initialized +0ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [RoutesResolver] AppController {/}: +13ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [RouterExplorer] Mapped {/, GET} route +3ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [RoutesResolver] UploadController {/upload}: +0ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [RouterExplorer] Mapped {/upload/exemplo-simples, POST} route +1ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [NestApplication] Nest application successfully started +1ms

Enter fullscreen mode Exit fullscreen mode

para testar o endpoint, acessar o link http://localhost:3000/docs, ir em /upload/exemplo-simples, clicar no botão try it out, escolher um arquivo e executar a requisição.

teste do endpoint de upload com o swagger

após a execução do teste, o terminal ...

[10:14:23] File change detected. Starting incremental compilation...

[10:14:23] Found 0 errors. Watching for file changes.

[Nest] 23723  - 13/09/2024, 10:14:23     LOG [NestFactory] Starting Nest application...
[Nest] 23723  - 13/09/2024, 10:14:23     LOG [InstanceLoader] AppModule dependencies initialized +11ms
[Nest] 23723  - 13/09/2024, 10:14:23     LOG [InstanceLoader] UploadModule dependencies initialized +0ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [RoutesResolver] AppController {/}: +13ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [RouterExplorer] Mapped {/, GET} route +3ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [RoutesResolver] UploadController {/upload}: +0ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [RouterExplorer] Mapped {/upload/exemplo-simples, POST} route +1ms
[Nest] 23723  - 13/09/2024, 10:14:24     LOG [NestApplication] Nest application successfully started +1ms
{
  fieldname: 'arquivo',
  originalname: 'README.md',
  encoding: '7bit',
  mimetype: 'text/markdown',
  buffer: <Buffer 23 20 4e 65 73 74 4a 53 20 2d 20 55 70 6c 6f 61 64 20 64 65 20 61 72 71 75 69 76 6f 73 0a 0a 0a 23 23 20 49 6e 66 6f 72 6d 61 c3 a7 c3 b5 65 73 20 67 ... 958 more bytes>,
  size: 1008
}

Enter fullscreen mode Exit fullscreen mode

6. Programar o serviço para processar e responder ao endpoint de upload

editar o arquivo src/uplaod/upload.service.ts para inserir os códigos abaixo.

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

@Injectable()
export class UploadService {
  responderInformacaoArquivo(arquivo: Express.Multer.File) {
    return {
      estado: 'ok',
      dados: {
        nome: arquivo.originalname,
        tamanho: arquivo.size,
        mimetype: arquivo.mimetype,
        encode: arquivo.encoding,
      },
    };
  }
}

Enter fullscreen mode Exit fullscreen mode

também deve modificar o arquivo src/uplaod/upload.controller.ts para inserir os códigos abaixo.

import {
  Controller,
  Post,
  UploadedFile,
  UseInterceptors,
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { FileInterceptor } from '@nestjs/platform-express';
import {
  ApiBody,
  ApiConsumes,
  ApiOperation,
  ApiResponse,
  ApiTags,
} from '@nestjs/swagger';

@Controller('upload')
@ApiTags('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @Post('exemplo-simples')
  @UseInterceptors(FileInterceptor('arquivo'))
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        arquivo: {
          type: 'string',
          format: 'binary',
        },
      },
    },
  })
  @ApiOperation({ summary: 'Exemplo de upload de 1 arquivo qualquer' })
  @ApiResponse({ status: 201, description: 'Arquivo enviado com sucesso.' })
  @ApiResponse({ status: 400, description: 'Erro no envio do arquivo.' })
  uploadArquivoSimples(@UploadedFile() arq: Express.Multer.File) {
    console.log(arq);

--    return {
--      estado: 'ok',
--    };
++    return this.uploadService.responderInformacaoArquivo(arq);
  }
}

Enter fullscreen mode Exit fullscreen mode

novamente para testar o endpoint, acessar o link http://localhost:3000/docs, ir em /upload/exemplo-simples, clicar no botão try it out, escolher um arquivo e executar a requisição.
perceba a mudança com a resposta da requisição que deverá parecer com algo como o json abaixo.

{
  "estado": "ok",
  "dados": {
    "nome": "README.md",
    "tamanho": 1008,
    "mimetype": "text/markdown",
    "encode": "7bit"
  }
}

Enter fullscreen mode Exit fullscreen mode

Referência e link

Top comments (0)