Princípios REST
Constraints REST
- Client-Server: Separação de interesses
- Stateless: Cada requisição contém toda informação
- Cacheable: Respostas podem ser cacheadas
- Uniform Interface: Interface consistente
- 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
# 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
# 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)
# 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
# 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
# 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:
- HTTP semantics corretas – Métodos e status codes
- Recursos bem nomeados – Substantivos, não verbos
- Documentação completa – OpenAPI/Swagger
- Autenticação segura – JWT/OAuth
- Versionamento – Para evoluir sem quebrar
