# API REST: Boas Práticas de Design e Testes

**Meta Description:** Aprenda a projetar e testar APIs REST: HTTP methods, status codes, versionamento, autenticação, documentação e segurança.

---

## Princípios REST

### Constraints REST

1. **Client-Server**: Separação de interesses
2. **Stateless**: Cada requisição contém toda informação
3. **Cacheable**: Respostas podem ser cacheadas
4. **Uniform Interface**: Interface consistente
5. **Layered System**: Arquitetura em camadas

---

## HTTP Methods

| Método | Semântica | Idempotente | Safe |
|--------|----------|-------------|------|
| GET | Ler recurso | Sim | Sim |
| POST | Criar recurso | Não | Não |
| PUT | Substituir recurso | Sim | Não |
| PATCH | Atualizar parcialmente | Não | Não |
| DELETE | Remover recurso | Sim | Não |
| HEAD | Ler headers | Sim | Sim |
| OPTIONS | Verbos suportados | Sim | Sim |

---

## Status Codes

### 2xx Success

| Código | Significado | Uso |
|--------|-------------|-----|
| 200 | OK | GET, PUT, PATCH bem-sucedido |
| 201 | Created | POST que cria recurso |
| 202 | Accepted | Async processing |
| 204 | No Content | DELETE bem-sucedido |

### 3xx Redirection

| Código | Significado | Uso |
|--------|-------------|-----|
| 301 | Moved Permanently | Redirect permanente |
| 302 | Found | Redirect temporário |
| 304 | Not Modified | Cache válido |

### 4xx Client Error

| Código | Significado | Uso |
|--------|-------------|-----|
| 400 | Bad Request | Dados inválidos |
| 401 | Unauthorized | Não autenticado |
| 403 | Forbidden | Sem permissão |
| 404 | Not Found | Recurso não existe |
| 409 | Conflict | Conflito de estado |
| 422 | Unprocessable | Validação falhou |
| 429 | Too Many Requests | Rate limit |

### 5xx Server Error

| Código | Significado | Uso |
|--------|-------------|-----|
| 500 | Internal Server Error | Erro genérico |
| 502 | Bad Gateway | Gateway inválido |
| 503 | Service Unavailable | Manutenção |
| 504 | Gateway Timeout | Timeout |

---

## Design de Endpoints

### Resource Naming

```markdown
# BOAS PRÁTICAS

# Recursos (substantivos, não verbos)
GET    /users              # Lista usuários
GET    /users/{id}         # Busca usuário
POST   /users              # Cria usuário
PUT    /users/{id}         # Atualiza usuário
DELETE /users/{id}         # Remove usuário

# Sub-recursos
GET    /users/{id}/orders          # Pedidos do usuário
GET    /users/{id}/orders/{orderId}  # Pedido específico
POST   /users/{id}/orders          # Cria pedido para usuário

# Evite:
GET /getUsers
POST /createUser
PUT /updateUserData
```

### Versionamento

```markdown
# Via URL (mais comum)
GET /v1/users
GET /v2/users

# Via Header
Accept: application/vnd.api.v2+json

# Via Query Param (evite)
GET /users?version=2
```

---

## Autenticação e Autorização

### Bearer Token (JWT)

```python
# Flask API
from flask import Flask, request, jsonify
import jwt

app = Flask(__name__)
SECRET_KEY = "your-secret-key"

@app.route("/protected")
def protected():
    token = request.headers.get("Authorization", "").replace("Bearer ", "")
    
    if not token:
        return jsonify({"error": "Token required"}), 401
    
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return jsonify({
            "user_id": payload["sub"],
            "role": payload["role"]
        })
    except jwt.ExpiredSignatureError:
        return jsonify({"error": "Token expired"}), 401
    except jwt.InvalidTokenError:
        return jsonify({"error": "Invalid token"}), 401

@app.route("/login", methods=["POST"])
def login():
    # Validate credentials
    user = validate_credentials(request.json)
    
    token = jwt.encode({
        "sub": user.id,
        "role": user.role,
        "exp": datetime.utcnow() + timedelta(hours=1)
    }, SECRET_KEY, algorithm="HS256")
    
    return jsonify({"token": token})
```

### OAuth 2.0

```python
# Flask + OAuth
from authlib.integrations.flask_client import OAuth

oauth = OAuth(app)
google = oauth.register(name='google')

@app.route("/login/google")
def login_google():
    return google.authorize_redirect(redirect_uri=url_for("callback", _external=True))

@app.route("/callback/google")
def callback():
    token = google.authorize_access_token()
    user_info = google.get("userinfo").json()
    # Create session
    return jsonify(user_info)
```

---

## Documentação OpenAPI

```yaml
# openapi.yaml
openapi: 3.0.3
info:
  title: E-commerce API
  version: 1.0.0
  description: API for e-commerce platform

servers:
  - url: https://api.exemplo.com/v1
    description: Production
  - url: https://staging.exemplo.com/v1
    description: Staging

paths:
  /users:
    get:
      summary: List users
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: List of users
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      summary: Create user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUser'
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '422':
          $ref: '#/components/responses/ValidationError'

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
          format: email
        created_at:
          type: string
          format: date-time
    
    CreateUser:
      type: object
      required:
        - name
        - email
        - password
      properties:
        name:
          type: string
          minLength: 1
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 8

  responses:
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
    
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            type: object
            properties:
              errors:
                type: array
                items:
                  type: object
                  properties:
                    field:
                      type: string
                    message:
                      type: string
```

---

## Conclusão

API REST bem desenhada é crucial para integrações. As chaves são:

1. **HTTP semantics corretas** - Métodos e status codes
2. **Recursos bem nomeados** - Substantivos, não verbos
3. **Documentação completa** - OpenAPI/Swagger
4. **Autenticação segura** - JWT/OAuth
5. **Versionamento** - Para evoluir sem quebrar
