Eki Eqbal Eki Eqbal - 1 month ago 8
Ruby Question

How to handle before filter for specific action in Grape?

I'm mounting Grape in my Rails project to build a RESTful API.

Now some end-points have actions need authentication and others which don't need authentication.

As for example I have

users
end-point which looks something like:

module Backend
module V1
class Users < Grape::API
include Backend::V1::Defaults

before { authenticate! }

resource :users do

desc "Return a user"
params do
requires :id, type: Integer, desc: 'User id'
end
get ':id' do
UsersService::Fetch.new(current_user,params).call
end

desc "Update a user"
params do
requires :id, type: Integer, desc: 'User id'
requires :display_name, type: String, desc: 'Display name'
requires :email, type: String, desc: 'Email'
end
post ':id' do
UsersService::Save.new(current_user,params).call
end

desc "Reset user password"
params do
requires :old_password, type: String, desc: 'old password'
requires :password, type: String, desc: 'new password'
end
post 'password/reset' do
PasswordService::Reset.new(current_user,params).call
end

desc "Forget password"
params do
requires :email, type: String
end
post 'password/forget' do
PasswordService::Forget.new(current_user,params).call
end

end
end
end
end


Now as you can see, all the actions except
password/forget
needs the user to be logged-in/authenticated. It doesn't make sense too to create a new end-point let's say
passwords
and just remove
password/forget
there as logically speaking, this end-point should be related to users resource.

The problem is with Grape
before
filter has no options like
except, only
in which I can say apply the filter for certain actions.

How do you usually handle such a case in a clean way?

Answer

Thanks @abhishek for the hint.

A dirty way to help would be by using namespace, something like:

module Backend
  module V1
    class Users < Grape::API
      include Backend::V1::Defaults

      namespace :users do
        desc "Forget password"
        params do
          requires :email, type: String
        end
        post 'password/forget' do
          PasswordService::Forget.new(current_user,params).call
        end

        namespace do
          before { authenticate! }

          desc "Return a user"
          params do
            requires :id, type: Integer, desc: 'User id'
          end
          get ':id' do
            UsersService::Fetch.new(current_user,params).call
          end

          desc "Update a user"
          params do
            requires :id, type: Integer, desc: 'User id'
            requires :display_name, type: String, desc: 'Display name'
            requires :email, type: String, desc: 'Email'
          end
          post ':id' do
            UsersService::Save.new(current_user,params).call
          end

          desc "Reset user password"
          params do
            requires :old_password, type: String, desc: 'old password'
            requires :password, type: String, desc: 'new password'
          end
          post 'password/reset' do
            PasswordService::Reset.new(current_user,params).call
          end            

        end
      end
    end
  end
end

This way we wont run before filter for users/password/forget but for the rest we will run before { authenticate! }