Envelope de resposta e tratamento de erros
Cada endpoint devolvendo um formato de erro diferente obriga o front a tratar cada um na mão — um envelope padronizado e um rescue_from central resolvem isso de uma vez.
Sem um padrão combinado, é fácil um endpoint devolver { error: "..." }, outro devolver { errors: [...] }, e um terceiro simplesmente estourar um 500 cru quando algo falha. O cliente da API — front-end, app mobile, outro serviço — precisa tratar cada formato na mão. Um envelope de resposta combinado com rescue_from centralizado resolve isso de vez, num lugar só.
O formato — sucesso e erro seguem sempre a mesma forma
{
"data": { "id": "1", "type": "pedido", "attributes": { "total": "150.0" } },
"meta": { "pagina": 1 }
}{
"error": { "codigo": "nao_encontrado", "mensagem": "Pedido não encontrado" }
}Um cliente que já sabe ler esse formato consegue tratar qualquer endpoint da API do mesmo jeito — olha se veio data ou error, sem precisar de um tratamento específico por rota.
rescue_from centralizado — um lugar só pra cada tipo de exceção
class ApplicationController < ActionController::API
rescue_from ActiveRecord::RecordNotFound, with: :registro_nao_encontrado
rescue_from ActiveRecord::RecordInvalid, with: :registro_invalido
rescue_from CanCan::AccessDenied, with: :acesso_negado
private
def registro_nao_encontrado(excecao)
render json: { error: { codigo: "nao_encontrado", mensagem: excecao.message } }, status: :not_found
end
def registro_invalido(excecao)
render json: { error: { codigo: "invalido", mensagens: excecao.record.errors.full_messages } },
status: :unprocessable_entity
end
def acesso_negado(_excecao)
render json: { error: { codigo: "acesso_negado", mensagem: "Você não tem permissão para essa ação" } },
status: :forbidden
end
endComo toda classe controller herda de ApplicationController, essas três regras valem pra qualquer controller do projeto — nenhum deles precisa saber que RecordNotFound existe, só precisa deixar o Rails levantar a exceção naturalmente.
O que isso substitui: begin/rescue repetido em cada action
def show
begin
pedido = Pedido.find(params[:id])
render json: pedido
rescue ActiveRecord::RecordNotFound
render json: { error: "não encontrado" }, status: :not_found # formato diferente do combinado acima!
end
endAlém de repetir o mesmo begin/rescue em toda action que pode levantar RecordNotFound, nada garante que dois desenvolvedores formatem o erro do mesmo jeito — é exatamente esse tipo de divergência que o envelope padronizado tenta eliminar, e ela volta a existir se cada action trata sua própria exceção.
Exercício
params.require(:pedido) levanta ActionController::ParameterMissing quando a chave pedido não vem na requisição. Adicione um rescue_from em ApplicationController pra essa exceção, seguindo o mesmo formato de envelope de erro usado acima.