0

I am building a simple todo app with nextjs and rails. I am using devise for user authentication, but I can't use current_user. Specifically, I am using JSON API Serializer to return user information during login in JSON format to the front. Here is the rails routing.

routes.rb

Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  root to: 'health_check#index'

  devise_for :users, skip: %w[registrations sessions]
  devise_scope :user do
    namespace :api do
      namespace :v1 do
        resource :current_user, controller: 'devise/current_user', only: %i[show]
        resource :user_sessions, controller: 'devise/user_sessions', only: %i[create destroy]
        resources :users, controller: 'devise/users' , only: %i[create]
      end
    end
  end
end

The following is api/v1/devise/current_user_controller.rb

# frozen_string_literal: true

module Api
  module V1
    module Devise
      class CurrentUserController < ApplicationController
        module Consts
          RESP_FIELDS = %i[id email name].map(&:freeze).freeze

          Consts.freeze
        end

        def show
          with_rescue(__method__) do
            render json: current_user_serializable_hash.to_json, status: :ok
          end
        end

        private

        def current_user_serializable_hash
          UserSerializer.new(current_user, { fields: { user: Consts::RESP_FIELDS } }).serializable_hash
        end
      end
    end
  end
end

controllers/application_controller.rb

# frozen_string_literal: true

class ApplicationController < ActionController::API
        include ActionController::Cookies
        include WithRescueConcern
end

here is app/serializers/user_serializer.rb

# == Schema Information
#
# Table name: users
#
#  id                     :bigint           not null, primary key
#  email                  :string(255)      default(""), not null
#  name                   :string(255)      not null
#  created_at             :datetime         not null
#  updated_at             :datetime         not null
#
# Indexes
#
#  index_users_on_email                 (email) UNIQUE
#  index_users_on_name                  (name)
#
class UserSerializer < BaseSerializer
  attributes :id, :email, :name
end

base_serializer.rb

# frozen_string_literal: true

class BaseSerializer
  include JSONAPI::Serializer

  set_key_transform :camel_lower
end

The error will be as follows The value of JSON returned to the front is null.

{"data":null}

However, to isolate the problem, I changed the current_user_serializable_hash method in current_user_controller to this.

def current_user_serializable_hash
  UserSerializer.new(current_user, { fields: { user: Consts::RESP_FIELDS } }).serializable_hash
end
↓
def current_user_serializable_hash
  UserSerializer.new(User.find(1), { fields: { user: Consts::RESP_FIELDS } }).serializable_hash
end

The response was then returned in JSON format as shown below.

{
    "data": {
        "id": "1",
        "type": "user",
        "attributes": {
            "id": 1,
            "email": "[email protected]",
            "name": "hoge1"
        }
    }
}

In other words, the current_user method is just not working. I wonder why. I have no idea. Does anyone have any idea what is going on? Thanks.

p.s. ----------------------------------

I have added part of the user_sessions_controller code for login to the body of the text.

# frozen_string_literal: true
module Api
  module V1
    module Devise
      class UserSessionsController < ApplicationController

        def create
          with_rescue(__method__) do
            user = User.find_for_authentication(email: user_session_params[:email])
            raise ApplicationController::UnauthorizedError, 'email' if user.blank?

            is_success = user&.valid_password?(user_session_params[:password])
            raise ApplicationController::UnauthorizedError, 'password' unless is_success

            bypass_sign_in(user)
            head :no_content
          end
        end
3
  • 1
    The current_user is null because the user is not logged in. Rails apis do not use cookies by default -- hence there is no session mechanism to store the logged in user. Usually when using rails as an api, you need to login, get a token and pass that to the front end, and then use that token on all subsequent requests from the front end. The rails api decodes the token and sets the user.
    – dbugger
    Commented Oct 27, 2023 at 15:27
  • Random thing, but don't put your constants in their own Consts module. You shouldn't do that any more than you would put your classes in their own Classes module, or your variables in a Vars module.
    – user229044
    Commented Oct 27, 2023 at 17:16
  • To dbugger, thanks for your comments. We are storing session information in redis by the login behavior. I have added part of the user_sessions_controller code for login to the body of the text. These have been confirmed to work.
    – Tsubasa
    Commented Oct 28, 2023 at 2:44

1 Answer 1

-1

I resolved the problem by myself. Specifically, I added the followings to config/environments/development.rb

config.cache_store = :redis_cache_store, { expires_in: 7.days, # TODO: 仮設定
                                               namespace: "#{Rails.application.class.module_parent_name.downcase}:#{
                                                   (ENV.fetch('RAILS_ENV', 'development') + ':').then.detect { |e| e != 'production:' }
                                                 }cache",
                                               url: "redis://#{ENV.fetch('REDIS_HOST', 'localhost')}:#{ENV.fetch('REDIS_PORT', '6379')}/0" }

    config.public_file_server.headers = {
      'Cache-Control' => "public, max-age=#{2.days.to_i}"
    }

thanks.

1
  • What do you intend to do in this snippet? (ENV.fetch('RAILS_ENV', 'development') + ':').then.detect { |e| e != 'production:' } Commented Oct 30, 2023 at 21:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.