Leia
Leia

Reputation: 23

Rejected Promise but response.ok is true. JavaScript Rails fetch request "Failed to execute 'json' on 'Response': body stream already read"

I am trying to pass a member's information from my Rails backend to my vanilla JS frontend after a member logs in. This way I will be able to store the member's id to later persist data to the backend for the member's profile (e.g. if the member added a new book to their wishlist).

I am able to authenticate the member and confirm their credentials in the backend, but when I try to send the logged in member info from rails to the JS frontend, I get the following error when I run resp.json() in console.

Failed to execute 'json' on 'Response': body stream already read. Interestingly, I am getting a 'true' response.ok, and status: 201, but am not able to access the member's data.

[[PromiseResult]]: TypeError: Response {type: "cors", url: "http://localhost:3000/login", redirected: false, status: 201, ok: true, …}

[[PromiseState]]: "rejected"

Here's my sessions controller: The create action is the struggle.

class SessionsController < ApplicationController

    def create
        @member = Member
        .find_by(email: session_params[:email])
        .try(:authenticate, session_params[:password])
        binding.pry
        if @member 
            login!
            render json: {
                logged_in: true,
                member: @member 
            },
            status: :created 
        else 
            render json: { 
                status: 401,
             errors: ['No such member', 'Verify credentials and try again or sign up']
            }
        end
    end

    def is_logged_in? 
        if logged_in? && current_member
            render json: {
                logged_in: true,
                member: current_member
            }
         else
             render json: {
                 logged_in: false,
                 message: 'no such member'
            }
        end
    end

    def destroy
       logout!

        render json: { 
            status: 200, 
            logged_out: true 
        }
    end

    def session_params
        params.require(:member).permit(:email, :password)
    end
end

My application controller:

class ApplicationController < ActionController::Base
    skip_before_action :verify_authenticity_token 

    helper_method :login!, :logged_in?, :authorized_member?, :logout!, #current_member (current_member is currently in controller/concerns)

    def login!
        session[:member_id] = @member.id
    end

    def logged_in?
        if @current_member
            render json: {
                logged_in: true, 
                member: @current_member
            }
        else
            render json: {
                logged_in: false
            }
        end
    end


    def authorized_member?
        @member == current_member
    end

end

and my JavaScript fetch request Side bar: I do not believe I need the commented out lines:

async function submitLogin() {
  let email = document.getElementById("login-email").value;
  let password = document.getElementById("login-password").value;
  const memberLogin = { member: { email, password } };

  let options = {
    method: "POST",
    credential: "same-origin",
    headers: {
      "Content-Type": "application/json",
      // "Access-Control-Allow-Origin":
      //   "file:///Users/awb/Coding/Flatiron/Projects/bookclub-javascript-rails-api/bookclub-frontend-javascript/index.html",
      // "Access-Control-Allow-Methods": "POST",
      // "Access-Control-Allow-Headers": "Content-Type, Authorization",
      Accept: "application/json",
    },
    body: JSON.stringify(memberLogin),
  };

 fetch("http://localhost:3000/login", options)
    .then((resp) => {
      resp.json();
      debugger;
    })
}

Upvotes: 1

Views: 2504

Answers (1)

razvans
razvans

Reputation: 3251

It's probably javascript fetch - Failed to execute 'json' on 'Response': body stream is locked. Promises can be resolved only once.

Since your function is async, try:

let response = await fetch("localhost:3000/login", options); 
let data = await response.json();

Upvotes: 1

Related Questions