# Aula 5.4 — DTOs, pipes e validação

## Identificação

- **Módulo:** 5 — Node.js, NestJS e APIs
- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** endpoints protegidos por DTOs e `ValidationPipe` global
- **Laboratório:** [`../exemplos/aula-5.4/index.html`](../exemplos/aula-5.4/index.html)

## Introdução

Na aula anterior construímos endpoints REST. Porém, qualquer pessoa pode enviar dados
diferentes do que nosso código espera:

```json
{
  "title": 42,
  "completed": "talvez",
  "isAdmin": true
}
```

Escrever `title: string` em TypeScript não impede que esse JSON chegue pela rede.
Tipos TypeScript ajudam durante o desenvolvimento, mas são apagados quando o código é
transformado em JavaScript.

Nesta aula criaremos uma fronteira de entrada:

```text
requisição bruta
→ transformação
→ remoção ou rejeição de campos desconhecidos
→ validação
→ controller
```

Perguntas que responderemos:

- DTO é a mesma coisa que entidade do banco?
- uma interface TypeScript valida JSON?
- pipe é um cano físico ou um operador do terminal?
- `"false"` recebido na URL já é um booleano falso?
- `whitelist` e `forbidNonWhitelisted` fazem a mesma coisa?
- validação elimina a necessidade de regras de negócio?

## Objetivos

Ao concluir a aula, você será capaz de:

1. explicar DTO, pipe, transformação e validação;
2. diferenciar tipagem estática de validação em ambiente de execução;
3. criar DTOs como classes concretas;
4. usar decorators do `class-validator`;
5. configurar `ValidationPipe` global;
6. aplicar `whitelist`, `forbidNonWhitelisted` e `transform`;
7. criar DTOs específicos para corpo, query e parâmetros;
8. usar `PartialType()` para atualizações;
9. diferenciar validação estrutural de regra de negócio;
10. devolver erros `400` compreensíveis e seguros.

## Pré-requisitos

- aulas 5.1 a 5.3 concluídas;
- noções de classe e decorator em TypeScript;
- endpoints REST do `TasksModule`;
- saber que corpo, query e parâmetros são dados externos.

## 1. O que é DTO?

DTO significa Data Transfer Object, ou objeto de transferência de dados. Ele descreve
o formato que atravessa uma fronteira, como a entrada de uma API.

```ts
export class CreateTaskDto {
  title!: string;
}
```

O DTO de criação não precisa possuir `id`, pois o servidor cria esse valor. Também
não precisa ser a mesma classe usada pelo ORM ou devolvida na resposta.

## 2. DTO não é entidade

| DTO de entrada | Entidade ou modelo persistido |
|---|---|
| contrato da API | estado interno do domínio ou banco |
| controla o que o cliente envia | pode possuir campos privados |
| muda com a operação | muda com as regras e persistência |
| não deve expor detalhes do MySQL | representa dados internos |

Reutilizar a entidade do banco como corpo pode permitir alterações em campos que o
cliente não deveria controlar.

## 3. Por que usar classe em vez de interface?

```ts
interface CreateTaskDto {
  title: string;
}
```

A interface desaparece em ambiente de execução. Uma classe continua existindo no JavaScript e pode
ser identificada pelo `ValidationPipe`. Por isso DTOs validados devem ser classes
concretas.

Não use importação somente de tipo para um DTO validado:

```ts
// correto: a classe permanece disponível em runtime
import { CreateTaskDto } from './dto/create-task.dto';

// incorreto para o metadado de validação
import type { CreateTaskDto } from './dto/create-task.dto';
```

## 4. O que é um pipe?

No NestJS, um pipe recebe um valor antes do controller e pode:

1. transformá-lo;
2. validá-lo;
3. devolver o valor aceito;
4. ou lançar uma exceção.

Se o pipe lança uma exceção, o método do controller não é executado.

## 5. Dependências necessárias

```bash
npm install class-validator class-transformer
```

Para mapped types usados neste exemplo:

```bash
npm install @nestjs/mapped-types
```

## 6. Primeiro DTO validado

```ts
import { IsString, MaxLength, MinLength } from 'class-validator';

export class CreateTaskDto {
  @IsString({ message: 'title deve ser texto.' })
  @MinLength(3, { message: 'title deve possuir ao menos 3 caracteres.' })
  @MaxLength(80, { message: 'title deve possuir no máximo 80 caracteres.' })
  title!: string;
}
```

Os decorators não são comentários. Eles registram metadados que o validador usa em
ambiente de execução.

## 7. Transformando o título

```ts
import { Transform } from 'class-transformer';

@Transform(({ value }) => typeof value === 'string' ? value.trim() : value)
@IsString()
@MinLength(3)
@MaxLength(80)
title!: string;
```

Transformamos apenas quando o valor já é string. Converter qualquer coisa com
`String(value)` faria `42` virar `"42"` e poderia esconder uma entrada inválida.

## 8. Configuração global

```ts
app.useGlobalPipes(
  new ValidationPipe({
    transform: true,
    whitelist: true,
    forbidNonWhitelisted: true,
    transformOptions: {
      enableImplicitConversion: false,
    },
  }),
);
```

Aplicar globalmente cria uma política consistente para todos os endpoints.

## 9. O que faz `transform`?

Requisições chegam como objetos JavaScript simples. Com `transform: true`, o pipe pode
transformá-los em instâncias das classes esperadas e aplicar transformações declaradas.

Isso não significa que toda conversão automática é segura. A string `"false"`, quando
convertida genericamente com `Boolean("false")`, resulta em `true`. Prefira conversões
explícitas para booleanos externos.

## 10. O que faz `whitelist`?

Com `whitelist: true`, propriedades sem decorator de validação são removidas:

```json
{
  "title": "Estudar pipes",
  "isAdmin": true
}
```

Depois da lista de permissões:

```json
{
  "title": "Estudar pipes"
}
```

Para um campo permitido permanecer, ele deve possuir ao menos um decorator adequado.

## 11. O que faz `forbidNonWhitelisted`?

Com `whitelist: true` e `forbidNonWhitelisted: true`, a propriedade desconhecida não é
apenas removida: a requisição é rejeitada com `400 Bad Request`.

Essa política ajuda o cliente a perceber que enviou um contrato incorreto e reduz
tentativas de mass assignment.

## 12. DTO de atualização

Na criação, `title` é obrigatório. Na atualização parcial, os campos são opcionais.

```ts
import { PartialType } from '@nestjs/mapped-types';
import { IsBoolean, IsOptional } from 'class-validator';
import { CreateTaskDto } from './create-task.dto';

export class UpdateTaskDto extends PartialType(CreateTaskDto) {
  @IsOptional()
  @IsBoolean({ message: 'completed deve ser booleano.' })
  completed?: boolean;
}
```

`PartialType()` produz uma classe derivada e preserva os metadados necessários. Não é
o mesmo que `Partial<CreateTaskDto>`, que existe apenas no sistema de tipos.

## 13. `@IsOptional()`

`@IsOptional()` ignora os validadores seguintes quando o valor é `null` ou
`undefined`. Ele não transforma string vazia em ausência.

Em updates, diferencie:

- campo omitido: não alterar;
- campo enviado com `false`: alterar para falso;
- campo enviado vazio: validar conforme o contrato.

## 14. Query DTO

Cadeias de consulta chegam como texto:

```text
GET /api/tasks?completed=false&search=nest
```

```ts
export class ListTasksQueryDto {
  @IsOptional()
  @Transform(({ value }) => {
    if (value === 'true') return true;
    if (value === 'false') return false;
    return value;
  })
  @IsBoolean({ message: 'completed deve ser true ou false.' })
  completed?: boolean;

  @IsOptional()
  @IsString()
  @MaxLength(50)
  search?: string;
}
```

O valor inválido `completed=talvez` continua como string e falha em `@IsBoolean()`.

## 15. DTO de parâmetros

Como nossos IDs de exemplo seguem `task-1`, podemos validar o padrão:

```ts
export class TaskParamsDto {
  @Matches(/^task-\d+$/, {
    message: 'id deve seguir o formato task-N.',
  })
  id!: string;
}
```

Em aplicações que usam UUID, o NestJS oferece `ParseUUIDPipe` e o class-validator
oferece `@IsUUID()`.

## 16. Pipes prontos

O NestJS inclui pipes como:

| Pipe | Função |
|---|---|
| `ParseIntPipe` | converte e valida inteiro |
| `ParseBoolPipe` | converte e valida booleano |
| `ParseUUIDPipe` | valida UUID |
| `ParseEnumPipe` | valida membro de enum |
| `ParseArrayPipe` | interpreta e valida arrays |
| `DefaultValuePipe` | fornece valor padrão |

```ts
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {}
```

Ou o valor chega convertido, ou uma exceção impede o controller de executar.

## 17. Controller com DTOs

```ts
@Post()
create(@Body() input: CreateTaskDto): Task {
  return this.tasksService.create(input.title);
}

@Get()
list(@Query() query: ListTasksQueryDto): Task[] {
  return this.tasksService.list(query);
}

@Patch(':id')
update(
  @Param() params: TaskParamsDto,
  @Body() input: UpdateTaskDto,
): Task {
  return this.tasksService.update(params.id, input);
}
```

O controller recebe dados já transformados e estruturalmente válidos.

## 18. Validação estrutural versus regra de negócio

DTO valida formato e limites da entrada:

- `title` é string;
- possui entre 3 e 80 caracteres;
- `completed` é booleano.

Service valida regras de negócio:

- usuário pode editar esta tarefa?
- tarefa arquivada pode ser reaberta?
- título já existe dentro deste projeto?

Não tente colocar toda a lógica de negócio em decorators.

## 19. Ordem conceitual

```text
middleware
→ guards
→ interceptors antes
→ pipes
→ controller
→ service
→ interceptors depois
→ filtros de exceção quando necessário
```

Nesta aula focamos o trecho em que pipes protegem os argumentos do controller.

## 20. Resposta de validação

Uma entrada inválida pode produzir:

```json
{
  "statusCode": 400,
  "message": [
    "title deve possuir ao menos 3 caracteres."
  ],
  "error": "Bad Request"
}
```

Mensagens devem orientar o cliente sem revelar stack trace, caminhos internos ou
segredos.

## 21. Propriedades aninhadas

Para validar objetos internos, não basta decorar apenas o objeto externo:

```ts
export class MetadataDto {
  @IsString()
  source!: string;
}

export class ImportTaskDto {
  @ValidateNested()
  @Type(() => MetadataDto)
  metadata!: MetadataDto;
}
```

`@Type()` informa ao class-transformer qual classe concreta deve ser criada.

## 22. Arrays

```ts
export class BulkCreateTasksDto {
  @IsArray()
  @ArrayMinSize(1)
  @ValidateNested({ each: true })
  @Type(() => CreateTaskDto)
  items!: CreateTaskDto[];
}
```

Também é possível usar `ParseArrayPipe` para arrays recebidos diretamente.

## 23. Evitando conversão implícita perigosa

Considere:

```text
completed=false
```

Uma conversão genérica baseada no construtor `Boolean` pode gerar `true`, porque a
string não está vazia. Neste curso, booleanos externos são convertidos explicitamente.

## 24. Atualização vazia

`UpdateTaskDto` torna campos opcionais, então `{}` pode passar pela validação estrutural.
Se uma atualização vazia não fizer sentido, essa é uma regra adicional:

```ts
if (Object.keys(input).length === 0) {
  throw new BadRequestException('Informe ao menos um campo para atualização.');
}
```

Ela pode estar em um validator de classe ou no service, dependendo da arquitetura.

## 25. Validação não é sanitização universal

Validar comprimento não torna texto automaticamente seguro em todos os contextos.

- HTML deve ser exibido como texto quando não for conteúdo confiável;
- SQL deve usar parâmetros ou ORM;
- registros precisam evitar quebra e dados sensíveis;
- URLs e nomes de arquivo exigem regras próprias.

## 26. Configuração recomendada para o curso

```ts
new ValidationPipe({
  transform: true,
  whitelist: true,
  forbidNonWhitelisted: true,
  validationError: {
    target: false,
    value: false,
  },
  transformOptions: {
    enableImplicitConversion: false,
  },
})
```

Ocultar `target` e `value` reduz a chance de refletir dados desnecessários na resposta.

## 27. XAMPP, Angular e MySQL

O fluxo permanece:

```text
Angular → JSON → ValidationPipe → controller → service → repositório → MySQL
```

O Apache do XAMPP pode servir o curso e a interface. A validação real executa no
processo NestJS. O navegador continua sem acessar MySQL diretamente.

## 28. Roteiro de implementação

| Etapa | Tempo sugerido |
|---|---:|
| Tipos versus ambiente de execução | 15 min |
| DTOs e decorators | 25 min |
| ValidationPipe global | 20 min |
| Corpo, query e params | 25 min |
| Regras, erros e segurança | 15 min |
| Laboratório | 15 min |
| Revisão | 5 min |

## 29. Laboratório guiado

Abra o [Fluxo de validação](../exemplos/aula-5.4/index.html).

### Etapa 1 — Entrada válida

1. selecione `CreateTaskDto`;
2. envie um título válido;
3. acompanhe transformação, lista de permissões e validação;
4. confirme que o controller recebe o DTO.

### Etapa 2 — Tipo incorreto

1. altere o tipo do título para número;
2. execute novamente;
3. confirme que o controller não é chamado.

### Etapa 3 — Campo desconhecido

1. adicione `isAdmin`;
2. teste lista de permissões sem proibição;
3. depois ative `forbidNonWhitelisted`;
4. compare remoção silenciosa e rejeição explícita.

### Etapa 4 — Booleano da query

1. selecione `ListTasksQueryDto`;
2. teste `true`, `false` e `talvez`;
3. observe tipo antes e depois da transformação.

### Etapa 5 — Update parcial

1. selecione `UpdateTaskDto`;
2. omita o título e envie `completed: false`;
3. confirme que ausência e falso não são confundidos.

## 30. Erros comuns

### Usar interface para DTO validado

Ela desaparece antes da aplicação executar.

### Importar DTO com `import type`

Remove a referência necessária em ambiente de execução.

### Confiar apenas em `transform: true`

Transformação não substitui regras explícitas.

### Ativar lista de permissões sem decorators

Campos sem decorators podem ser removidos, mesmo que existam na classe.

### Converter booleano com `Boolean(value)`

`Boolean("false")` produz `true`.

### Usar o mesmo DTO em todas as operações

Criação, atualização, filtros e parâmetros possuem contratos diferentes.

## 31. Exercício de fixação

Crie DTOs para documentos:

- `CreateDocumentDto` com título de 3 a 120 caracteres;
- `UpdateDocumentDto` parcial;
- `ListDocumentsQueryDto` com `search` e `limit`;
- `DocumentParamsDto` validando o formato do ID;
- lista de permissões e rejeição de campos desconhecidos.

## 32. Desafio individual

Implemente importação em lote:

1. wrapper com propriedade `items`;
2. mínimo de 1 e máximo de 20 itens;
3. validação aninhada de cada `CreateTaskDto`;
4. resposta que identifica o índice de cada erro;
5. limite de tamanho da requisição.

## 33. Lista de verificação de conclusão

- [ ] Sei por que TypeScript não valida a rede.
- [ ] Diferencio DTO de entidade.
- [ ] Uso classes concretas para DTOs validados.
- [ ] Entendo transformação e validação.
- [ ] Configurei o `ValidationPipe` global.
- [ ] Sei comparar lista de permissões e proibição.
- [ ] Converto booleanos externos explicitamente.
- [ ] Criei DTOs separados para create, update, query e params.
- [ ] O controller não executa quando o pipe falha.
- [ ] Regras de negócio continuam no service.

## 34. Rubrica da entrega

| Critério | Pontos |
|---|---:|
| DTOs como classes | 15 |
| Regras declarativas | 20 |
| ValidationPipe global | 15 |
| Lista de permissões e campos desconhecidos | 15 |
| Transformação segura | 15 |
| DTOs por operação | 10 |
| Erros seguros | 10 |
| **Total** | **100** |

## 35. Fontes oficiais

- [NestJS — Validation](https://docs.nestjs.com/techniques/validation)
- [NestJS — Pipes](https://docs.nestjs.com/pipes)
- [NestJS — ciclo de vida da requisição](https://docs.nestjs.com/faq/request-lifecycle)

## Próxima aula

Na Aula 5.5, trataremos exceções de forma consistente, criaremos filtros e carregaremos
configurações por ambiente sem expor segredos.
