# Aula 5.3 — REST, rotas e respostas HTTP

## Identificação

- **Módulo:** 5 — Node.js, NestJS e APIs
- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** API REST de tarefas com operações CRUD em memória
- **Laboratório:** [`../exemplos/aula-5.3/index.html`](../exemplos/aula-5.3/index.html)

## Introdução

Na aula anterior organizamos controller, service e repositório. Agora precisamos
definir como outras aplicações conversam com esse módulo.

Uma API não é apenas “uma URL que devolve JSON”. Ela é um contrato. O cliente precisa
saber qual endereço usar, qual ação pedir, quais dados enviar e como interpretar a
resposta. HTTP já oferece um vocabulário para isso: métodos, caminhos, cabeçalhos,
corpo e códigos de status.

Perguntas que responderemos:

- REST é uma biblioteca do NestJS?
- rota, endpoint e recurso significam exatamente a mesma coisa?
- por que usar `GET /tasks/42` em vez de `/buscarTarefa?id=42`?
- `POST`, `PUT` e `PATCH` são intercambiáveis?
- toda resposta bem-sucedida deve retornar `200`?
- o que significa uma operação ser idempotente?
- o navegador pode acessar o MySQL diretamente depois de criarmos a API?

REST não é um pacote que instalamos. É um estilo arquitetural. Nesta aula aplicaremos
uma parte prática desse estilo sobre HTTP.

## Objetivos

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

1. diferenciar API, recurso, representação, rota e endpoint;
2. modelar URLs orientadas a recursos;
3. usar `GET`, `POST`, `PUT`, `PATCH` e `DELETE` com intenção clara;
4. extrair parâmetros com `@Param()`, filtros com `@Query()` e dados com `@Body()`;
5. devolver status HTTP coerentes;
6. explicar operações seguras e idempotentes;
7. implementar CRUD de tarefas em memória;
8. devolver `404` quando um recurso não existir;
9. evitar verbos de ação desnecessários nas URLs;
10. inspecionar requisição e resposta completas.

## Pré-requisitos

- aulas 5.1 e 5.2 concluídas;
- noção de requisição e resposta;
- `TasksModule`, controller, service e repositório em memória;
- conhecimentos iniciais de objetos e JSON.

## 1. O que é uma API?

API significa interface de programação de aplicações. É uma fronteira com regras que
permite a comunicação entre sistemas.

```text
Angular → API NestJS → regra de negócio → repositório → MySQL
```

O Angular conhece o contrato HTTP. Ele não deve receber senha do banco nem executar
SQL diretamente.

## 2. O que é REST?

REST é um estilo arquitetural para sistemas distribuídos. Em APIs web, normalmente
aplicamos ideias como:

- recursos identificados por URLs;
- interface uniforme baseada na semântica HTTP;
- requisições autocontidas;
- separação entre cliente e servidor;
- representações transferidas, frequentemente em JSON;
- respostas que permitem ao cliente entender o resultado.

Uma API que usa HTTP e JSON não se torna automaticamente RESTful. A intenção das
rotas, métodos e respostas precisa ser coerente.

## 3. Recurso e representação

Um recurso é aquilo que queremos identificar ou manipular: tarefas, usuários ou
documentos. Uma representação é uma forma de descrever o estado do recurso.

```json
{
  "id": "task-1",
  "title": "Estudar rotas REST",
  "completed": false
}
```

O objeto JSON não é a tarefa física “dentro da internet”; é uma representação dela.

## 4. URL orientada a recursos

Prefira substantivos no plural:

```text
/api/tasks
/api/tasks/task-1
/api/users/user-8/tasks
```

Evite repetir ações que o método HTTP já expressa:

```text
/api/getTasks
/api/createTask
/api/deleteTask?id=task-1
```

Há exceções legítimas para operações que não se encaixam bem em CRUD, mas o ponto de
partida deve ser o recurso.

## 5. Rota versus endpoint

Neste curso usaremos:

- **rota:** padrão de caminho associado ao código, como `/tasks/:id`;
- **endpoint:** combinação acessível de método e caminho, como `GET /api/tasks/task-1`;
- **URL:** endereço completo, como `http://localhost:3000/api/tasks/task-1`.

Equipes podem usar termos de forma um pouco diferente. O importante é definir o
vocabulário e evitar ambiguidade.

## 6. Anatomia de uma requisição

```http
POST /api/tasks HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Accept: application/json

{
  "title": "Estudar métodos HTTP"
}
```

| Parte | Significado |
|---|---|
| `POST` | intenção da operação |
| `/api/tasks` | recurso alvo |
| cabeçalhos | metadados da mensagem |
| corpo | representação enviada pelo cliente |

## 7. Anatomia de uma resposta

```http
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/tasks/task-2

{
  "id": "task-2",
  "title": "Estudar métodos HTTP",
  "completed": false
}
```

O status comunica o resultado de forma padronizada. O corpo fornece detalhes ou uma
representação quando necessário.

## 8. GET — consultar

`GET` recupera uma representação e não deve alterar o estado do recurso.

```ts
@Get()
list(@Query('completed') completed?: string): Task[] {
  return this.tasksService.list(completed);
}

@Get(':id')
findOne(@Param('id') id: string): Task {
  return this.tasksService.findOne(id);
}
```

Exemplos:

```text
GET /api/tasks
GET /api/tasks?completed=true
GET /api/tasks/task-1
```

## 9. Parâmetro de rota

Em `/tasks/:id`, `:id` é um espaço dinâmico definido na rota. Em uma requisição real,
ele recebe um valor:

```text
padrão: /tasks/:id
URL:    /tasks/task-1
valor:  task-1
```

```ts
@Param('id') id: string
```

Rotas estáticas, como `/tasks/summary`, devem ser declaradas antes de rotas dinâmicas
como `/tasks/:id`, evitando que `summary` seja interpretado como um identificador.

## 10. Cadeia de consulta

Cadeia de consulta costuma representar filtros, ordenação, busca e paginação:

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

```ts
@Query('completed') completed?: string
@Query('search') search?: string
```

Tudo chega como dado externo. A string `"false"` é verdadeira em uma condição
JavaScript se você apenas fizer `Boolean(value)`. Converta e valide explicitamente.

## 11. POST — criar ou processar

Para criar uma tarefa na coleção:

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

Por padrão, manipuladores `POST` no NestJS respondem com `201 Created`. É útil também
informar onde o recurso foi criado por meio do cabeçalho `Location`.

Na Aula 5.4 substituiremos o tipo introdutório por uma classe DTO validada em ambiente de execução.

## 12. PUT — substituir a representação

`PUT` representa substituição completa do estado conhecido do recurso:

```http
PUT /api/tasks/task-1
Content-Type: application/json

{
  "title": "Nova descrição completa",
  "completed": true
}
```

Se o contrato exige todos os campos editáveis, omitir um deles deve ser tratado de
acordo com esse contrato, e não silenciosamente como atualização parcial.

## 13. PATCH — modificar parcialmente

`PATCH` aplica um conjunto de alterações:

```http
PATCH /api/tasks/task-1
Content-Type: application/json

{
  "completed": true
}
```

O documento enviado não precisa conter a tarefa completa. O formato de patch deve ser
definido pelo contrato da API.

## 14. DELETE — remover

```ts
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
remove(@Param('id') id: string): void {
  this.tasksService.remove(id);
}
```

`204 No Content` comunica sucesso sem corpo. Não devolva JSON junto com `204`, pois a
semântica desse status é justamente não possuir conteúdo.

## 15. Métodos seguros

Um método seguro é destinado à leitura, sem solicitar alteração do estado do servidor.
`GET` é seguro. Isso não significa que absolutamente nada aconteça: registros e métricas
podem ser produzidos, mas a intenção solicitada pelo cliente é leitura.

## 16. Idempotência

Uma operação idempotente produz o mesmo efeito pretendido no servidor quando repetida
uma ou várias vezes.

```text
DELETE /tasks/task-1
DELETE /tasks/task-1
```

A primeira pode responder `204` e a segunda `404`, mas repetir a intenção não remove
duas tarefas diferentes. O estado final continua “task-1 ausente”.

Pela semântica HTTP, métodos seguros, `PUT` e `DELETE` são idempotentes. `POST` não é
idempotente por definição. `PATCH` pode ser projetado de forma idempotente, mas não há
essa garantia geral.

## 17. Status HTTP essenciais

| Status | Uso nesta API |
|---:|---|
| `200 OK` | consulta ou atualização com corpo |
| `201 Created` | recurso criado |
| `204 No Content` | remoção concluída sem corpo |
| `400 Bad Request` | entrada inválida |
| `404 Not Found` | tarefa inexistente |
| `409 Conflict` | conflito com o estado atual |
| `500 Internal Server Error` | falha não tratada no servidor |

Status não é decoração. O cliente pode usá-lo para decidir qual interface apresentar.

## 18. Exceções no NestJS

```ts
findOne(id: string): Task {
  const task = this.repository.findById(id);
  if (!task) {
    throw new NotFoundException(`Tarefa ${id} não encontrada.`);
  }
  return task;
}
```

O framework converte a exceção HTTP em uma resposta coerente. Não exponha stack trace,
detalhes internos ou SQL ao cliente.

## 19. Resposta padrão do NestJS

Na abordagem padrão recomendada, o manipulador retorna objeto ou array, e o NestJS
serializa para JSON. Evite usar `@Res()` sem necessidade, porque isso acopla o código
ao adaptador HTTP e transfere para você a responsabilidade de concluir a resposta.

```ts
@Get()
list(): Task[] {
  return this.tasksService.list();
}
```

## 20. Contratos consistentes

Há duas abordagens comuns para coleções:

```json
[
  { "id": "task-1", "title": "Estudar REST", "completed": false }
]
```

ou:

```json
{
  "items": [
    { "id": "task-1", "title": "Estudar REST", "completed": false }
  ],
  "total": 1
}
```

Não existe uma única forma universal. Escolha uma convenção, documente e mantenha-a.
O envelope facilita adicionar paginação sem alterar a forma superior da resposta.

## 21. CRUD e REST não são sinônimos

CRUD descreve quatro operações de dados: criar, ler, atualizar e remover. REST é um
estilo arquitetural mais amplo. CRUD ajuda a exercitar os métodos HTTP, mas não cobre
cache, hipermídia, negociação de conteúdo e outras propriedades de sistemas REST.

## 22. Controller completo

```ts
@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Get()
  list(@Query('completed') completed?: string): Task[] {
    return this.tasksService.list(completed);
  }

  @Get(':id')
  findOne(@Param('id') id: string): Task {
    return this.tasksService.findOne(id);
  }

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

  @Patch(':id')
  update(@Param('id') id: string, @Body() input: UpdateTaskInput): Task {
    return this.tasksService.update(id, input);
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  remove(@Param('id') id: string): void {
    this.tasksService.remove(id);
  }
}
```

## 23. Service com busca e erro

```ts
findOne(id: string): Task {
  const task = this.repository.findById(id);
  if (!task) {
    throw new NotFoundException(`Tarefa ${id} não encontrada.`);
  }
  return task;
}
```

O service decide que uma tarefa precisa existir. O controller apenas conecta essa
operação à rota HTTP.

## 24. Repositório em memória ampliado

O contrato agora precisa de:

```ts
export interface TasksRepository {
  findAll(): Task[];
  findById(id: string): Task | undefined;
  add(title: string): Task;
  update(id: string, changes: Partial<Omit<Task, 'id'>>): Task | undefined;
  remove(id: string): boolean;
}
```

Na integração com MySQL, a implementação muda e o contrato permanece como fronteira.

## 25. Estado HTTP é diferente de estado do banco

HTTP é stateless: cada requisição deve trazer as informações necessárias para ser
interpretada. Isso não proíbe o servidor de persistir tarefas no MySQL. Significa que
o protocolo não deve depender de uma conversa implícita e invisível entre requisições.

## 26. Segurança inicial

- trate parâmetros, query e corpo como não confiáveis;
- limite tamanho de textos e paginação;
- não exponha mensagens internas;
- não permita atualização de campos que o cliente não controla;
- nunca construa SQL concatenando entrada do usuário;
- não confie apenas nos tipos TypeScript para validar rede.

A validação completa será implementada na Aula 5.4.

## 27. Roteiro de implementação

| Etapa | Tempo sugerido |
|---|---:|
| API, REST e recursos | 15 min |
| Métodos e URLs | 20 min |
| Parâmetros, query e corpo | 20 min |
| Status e erros | 20 min |
| Controller, service e repositório | 25 min |
| Laboratório | 15 min |
| Revisão | 5 min |

## 28. Testando manualmente

```bash
curl http://localhost:3000/api/tasks
```

```bash
curl -X POST http://localhost:3000/api/tasks \
  -H "Content-Type: application/json" \
  -d '{"title":"Estudar REST"}'
```

```bash
curl -X PATCH http://localhost:3000/api/tasks/task-1 \
  -H "Content-Type: application/json" \
  -d '{"completed":true}'
```

No PowerShell, você também pode usar `Invoke-RestMethod`.

## 29. Laboratório guiado

Abra o [Console REST da Knowledge API](../exemplos/aula-5.3/index.html).

### Etapa 1 — Consulte a coleção

1. selecione `GET /api/tasks`;
2. envie a requisição;
3. identifique método, caminho, status e corpo.

### Etapa 2 — Crie e consulte

1. selecione `POST /api/tasks`;
2. envie um título válido;
3. copie o identificador criado;
4. consulte `GET /api/tasks/:id`.

### Etapa 3 — Atualize parcialmente

1. selecione `PATCH /api/tasks/:id`;
2. marque a tarefa como concluída;
3. observe que o título não foi removido.

### Etapa 4 — Remova e repita

1. envie `DELETE` para a tarefa;
2. observe `204` sem corpo;
3. repita a remoção;
4. explique por que o segundo status muda, mas a operação continua idempotente.

### Etapa 5 — Explore erros

1. busque um identificador inexistente;
2. envie um título curto;
3. use um filtro inválido;
4. compare `400` e `404`.

## 30. Erros comuns

### Usar GET para alterar dados

Pode causar efeitos inesperados por cache, pré-carregamento e robôs.

### Retornar sempre 200

Obriga o cliente a adivinhar o resultado lendo textos no corpo.

### Colocar verbos em todas as URLs

Duplica a intenção já comunicada pelo método HTTP.

### Tratar PUT como PATCH sem documentar

Cria ambiguidade sobre campos omitidos.

### Devolver corpo com 204

Contraria a semântica de `No Content`.

### Acreditar que TypeScript validou a requisição

O JSON veio da rede e os tipos foram apagados em ambiente de execução.

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

Implemente o recurso `documents` com:

- `GET /api/documents`;
- `GET /api/documents/:id`;
- `POST /api/documents`;
- `PATCH /api/documents/:id`;
- `DELETE /api/documents/:id`;
- status coerentes e erro `404`.

## 32. Desafio individual

Acrescente ao laboratório ou ao exemplo:

1. filtro `search` no `GET /tasks`;
2. ordenação `sort=title`;
3. envelope `{ items, total }`;
4. cabeçalho `Location` na criação;
5. uma tabela documentando todos os contratos.

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

- [ ] Sei explicar API, REST, recurso e representação.
- [ ] Diferencio rota, endpoint e URL.
- [ ] Uso substantivos nas URLs.
- [ ] Diferencio parâmetro, cadeia de consulta e corpo.
- [ ] Sei quando usar GET, POST, PUT, PATCH e DELETE.
- [ ] Entendo método seguro e idempotente.
- [ ] Uso 200, 201, 204, 400 e 404 corretamente.
- [ ] Não devolvo corpo em uma resposta 204.
- [ ] Mantenho regras no service.
- [ ] Continuo sem expor MySQL ao navegador.

## 34. Rubrica da entrega

| Critério | Pontos |
|---|---:|
| Modelagem dos recursos e URLs | 15 |
| Uso dos métodos HTTP | 20 |
| Parâmetros, query e corpo | 15 |
| Status e erros | 20 |
| Separação controller/service/repositório | 15 |
| Contratos consistentes | 10 |
| Explicação de idempotência | 5 |
| **Total** | **100** |

## 35. Fontes oficiais

- [NestJS — Controllers](https://docs.nestjs.com/controllers)
- [NestJS — Exception filters](https://docs.nestjs.com/exception-filters)
- [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)
- [RFC 5789 — PATCH Method for HTTP](https://www.rfc-editor.org/rfc/rfc5789.html)

## Próxima aula

Na Aula 5.4, criaremos DTOs como classes e aplicaremos pipes de transformação e
validação para impedir que dados inválidos cheguem às regras da aplicação.
