UndefinedFunctionError at GET / function nil.id/0 is undefined

I am integrating phx.gen.auth with the phoenix context guides . But some problem with fetch_current_cart plug in the router. I get this error in the index page.

But when i comment out the fetch_current_cart plug everything works fine.

UndefinedFunctionError <small>at GET</small> <small>/</small>

function nil.id/0 is undefined

Here is the code

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, {ShopWeb.LayoutView, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :fetch_current_user
    plug :fetch_current_cart
  end



  def fetch_current_cart(conn, _opts) do
   if cart = ShoppingCart.get_cart_by_user_id(conn.assigns.current_user.id) do
      assign(conn, :cart, cart)
  else
      {:ok, new_cart} = ShoppingCart.create_cart(conn.assigns.current_user.id)
      assign(conn, :cart, new_cart)
   end
 end

Can someone help.

Thanks

conn.assigns.current_user may be nil if no user is logged in. You cannot fetch the user.id if you don’t know the user.

2 Likes

Thanks for pointing out. Sometimes I doubt whether I have a brain or not. :laughing:

If someone commits the same silly mistake, This is how the fetch_current_user plug must be.

 def fetch_current_cart(conn, _opts) do
  if conn.assigns[:current_user] do
    cart = ShoppingCart.get_cart_by_user_id(conn.assigns.current_user.id)
    assign(conn, :cart, cart)
  else
    conn
  end
end

But will this work if the user isn’t logged in? I’m doing something similar:

if conn.assigns.user.id do
  token = Phoenix.Token.sign(conn, "user socket", conn.assigns.user.id
  assign(conn, :user_token, token)
else
  conn
end

This works as long as the user is logged in. If not, I get the ‘function nil.id() is undefined’ error.

if conn.assigns.user.id do … expects conn.assigns.user to be a map with an :id column, or it will raise as you’ve noticed. If conn.assigns.user can be a user or nil you should check for that, e.g. if not is_nil(conn.assigns.user) do …

1 Like

Your code works, but now there is another problem. Now, no token is being added to conn, which is now being passed to the connect function in user_socket.ex:

def connect(%{"token" => token} = conn, socket, _connect_info) do
    case Phoenix.Token.verify(socket, "user socket", token, max_age: 
           1_209_600) do
      {:ok, user_id} ->
        {:ok, assign(socket, :user, user_id)}
      {:error, reason} ->
        :error
    end
  end

So when the user isn’t logged in, there is no token, and this function just throws and error. I changed the error clause to return {:ok, socket}. Now the app works as expected for an unauthorized user. However, I have problem when I try to log in, so I’ll have to deal with that. I’m using ueberauth, so if anyone knows of any good info in it. please pass it along. The docs are not helpful.

I figured it out. Dumb mistake on my part. I’m using Github to log in, and I have the client ID and secret in a .env file. I forgot to run source .env at my terminal.

i believe there is a way to get around this using the config file. Can anyone point me to this?

1 Like

If you set your configuration at runtime (i.e. config/runtime.exs) then I’ve had good experiences with using Mikko Ahlroth / dotenv-parser · GitLab by @Nicd

2 Likes