Ajax POST request not hitting Elixir router path

297 views Asked by At

I have this redux-observable epic which does a POST ajax request using RxJS.ajax.post and I don't think it is hitting my Elixir router properly as nothing is happening on my elixir backend. I am doing get requests to get categories correctly and in the same manner so I am hitting other paths in my Elixir router correctly. I am expecting the issue to be with my backend Elixir code not my frontend. I might need to change my path in router.ex.

When I press a button on the frontend, this object is what gets sent to the elixir backend (it dispatches this action with a product as the payload and hits the redux-observable epic below):

onPress = {() => {
  props.uploadProduct({
    name: props.name,
    brand: props.brand,
    description: props.description,
    image: props.image
  })

The epic:

import { ajax } from 'rxjs/observable/dom/ajax'
import { Observable } from 'rxjs'

export const uploadProductEpic = action$ =>
  action$.ofType(UPLOAD_PRODUCT)
    .mergeMap(action => {
      ajax.post('http://localhost:4000/products/', action.payload)
    })
    .map(response => uploadProductFulfilled(response))
    .catch(error => Observable.of(
      uploadProductRejected(error))
    )

the elixir router:

defmodule Api.Router do
  use Plug.Router

  if Mix.env == :dev do
    use Plug.Debugger
  end
  plug :match
  plug :dispatch

  get "/categories/" do
    Api.Repo.getCategories(conn)
  end

  post "/products/:product" do
    IO.puts inspect conn
    Api.Repo.insertProduct(conn, product)
  end
end

IO.puts inspect conn doesn't log anything. So My Elixir router path post "/products/:product" do is not being hit by my POST request. What am I doing wrong?

This is the elixir function in repo.ex that I HOPE will insert the product into my database:

  def insertProduct(conn, product) do
    product = %Api.Product{name: product.name, brand: product.brand, description: product.description, image: product.image, rating: 0, numberOfVotes: 0}
    changeset = Api.Product.changeset(product)
    errors = changeset.errors
    valid = changeset.valid?
    case insert(changeset) do
      {:ok, product} ->
        conn
          |> put_resp_content_type("application/json")
          |> send_resp(200, Poison.encode!(%{
              successs: "success"
          }))
      {:error, changeset} ->
        conn
          |> put_resp_content_type("application/json")
          |> send_resp(500, Poison.encode!(%{
              failure: "Errors"
          }))
    end
  end

I am a frontend developer just trying to get into Elixir so any guidance and patience is appreciated. Thanks.

1

There are 1 answers

5
Dogbert On BEST ANSWER

Your data is sent in the body of the request, not in the URL, so the route should be:

post "/products"

You'll also need to plug in a JSON parser after plug :match and before plug :dispatch, as described in the Parameter Parsing section in the documentation of Plug.Router:

plug :match
plug Plug.Parsers, parsers: [:json],
                   pass:  ["application/json"],
                   json_decoder: Poison
plug :dispatch

The JSON data will now be present in conn.body_params, which you can send to your function:

post "/products" do
  Api.Repo.insertProduct(conn, conn.body_params)
end

And finally, the keys in the JSON would be strings, so you need to use the bracket syntax to access them instead of dots:

product = %Api.Product{name: product["name"], brand: product["brand"], description: product["description"], image: product["image"], rating: 0, numberOfVotes: 0}

I'm not sure how you've defined Api.Product.changeset, but the default Phoenix convention defines a 2 arity function which calls cast and extracts the data from a map itself. If you're doing the same, you can do this instead:

changeset = Api.Product.changeset(%Api.Product{}, product)