ruby-on-railsrubyruby-grapegrape-api

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?


Solution

  • 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! }