Aprenda / Ruby on Rails

FundamentosAula 10 de 2026 min0% da trilha

Projeto guiado: API REST completa

Um recurso Pedido do scaffold ao endpoint autenticado, autorizado e serializado — as 9 lições juntas.

Última peça: um recurso Pedido inteiro, do zero ao endpoint pronto pra produção, encadeando tudo que foi visto — scaffold, model com validação, controller REST, serializer, service object, Devise e CanCanCan.

1. Scaffold

rails generate scaffold Pedido usuario:references status:string total:decimal --api
rails db:migrate

2. Model — validação e enum de status

app/models/pedido.rb
class Pedido < ApplicationRecord
  belongs_to :usuario
  has_many :itens

  enum status: { pendente: 0, finalizado: 1, cancelado: 2 }

  validates :total, numericality: { greater_than: 0 }

  def entregue?
    status == "finalizado"
  end
end

3. Ability — quem pode fazer o quê

app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(usuario)
    return unless usuario

    if usuario.admin?
      can :manage, :all
    else
      can [:read, :create], Pedido
      can [:update], Pedido, usuario_id: usuario.id
      cannot :destroy, Pedido
    end
  end
end

4. Service — a ação que orquestra mais de uma tabela

app/services/finalizar_pedido.rb
class FinalizarPedido
  def initialize(pedido)
    @pedido = pedido
  end

  def call
    return false unless @pedido.itens.any?

    ActiveRecord::Base.transaction do
      @pedido.finalizado!
      @pedido.itens.each { |item| item.produto.decrement!(:estoque, item.quantidade) }
    end
    true
  end
end

5. Serializer — o formato de resposta

app/serializers/pedido_serializer.rb
class PedidoSerializer
  include JSONAPI::Serializer

  attributes :status, :total
  belongs_to :usuario
end

6. Controller — a única peça que conhece HTTP

app/controllers/pedidos_controller.rb
class PedidosController < ApplicationController
  before_action :authenticate_usuario!    # Devise: exige token válido
  load_and_authorize_resource              # CanCanCan: aplica a Ability

  def index
    render json: PedidoSerializer.new(current_usuario.pedidos).serializable_hash
  end

  def show
    render json: PedidoSerializer.new(@pedido).serializable_hash
  end

  def create
    pedido = current_usuario.pedidos.new(pedido_params)
    if pedido.save
      render json: PedidoSerializer.new(pedido).serializable_hash, status: :created
    else
      render json: { errors: pedido.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def finalizar
    if FinalizarPedido.new(@pedido).call
      render json: PedidoSerializer.new(@pedido).serializable_hash
    else
      render json: { error: "Pedido sem itens" }, status: :unprocessable_entity
    end
  end

  private

  def pedido_params
    params.require(:pedido).permit(:total)
  end
end
config/routes.rb
Rails.application.routes.draw do
  resources :pedidos, except: [:new, :edit, :destroy] do
    member { post :finalizar }   # POST /pedidos/:id/finalizar — rota extra, fora do REST padrão
  end
end

Repare a ordem das camadas dentro de PedidosController: authenticate_usuario! (quem é você) roda antes de load_and_authorize_resource (o que você pode fazer), que roda antes de qualquer lógica de negócio. Autenticação, depois autorização, depois regra de negócio — nessa ordem, sempre.

Exercício

Um usuário comum tenta POST /pedidos/7/finalizar num pedido que não é dele. Em que camada exatamente essa tentativa é barrada, e com qual status HTTP?

Quiz rápido

Na ordem do controller final, o que roda primeiro: autenticação, autorização ou a regra de negócio (service)?
Por que a rota finalizar usa member { post :finalizar } em vez de resources sozinho?