Can someone please help me with this (Basic API creation) Rubber Ducky Edition

I am using Phoenix1.17 and the respective versions of Elixir and Erlang. I used ASDF to set it up on Ubuntu

I created a basic Phoenix Instance

mix phx.new app

I install all the dependencies. When the installer gets to the page asking me to run mix ecto.create
I stop what I am doing.

At his point I create a new command line instance, go into PostGres and I create a database. I name the database app.

I verify the existence of the database in Postgres using the command: \l

I go back to to the command line instance that I am running the phoenix install from. I CD into the app directory. I run mix ecto.create

I get a validation message

Compiling 15 files (.ex)
Generated app app
The database for App.Repo has been created

I go to the phoenix documentation to look up how to generate routes. The Phoenix documentation doesn’t say the right way to do it directly; instead, it demonstrates it in the context of building an app. I don’t want to build the app in the documentation, I simply want to know the correct syntax and read a few assuring words explaining it.

Based on what they have this is the synax they use:

mix phx.gen.json Urls Url urls link:string title:string

I am going to replace Urls Url urls with Items Item items and I am only going to have one table called name:string

mix phx.gen.json Items Item items name:string

I get the prompt


Add the resource to your :api scope in lib/app_web/router.ex:
    resources "/items", ItemController, except: [:new, :edit]

I do what it says. I go to router and put that puppy in there:

  pipeline :api do
    plug :accepts, ["json"]
    resources "/items", ItemController, except: [:new, :edit]
  end

I run the following command:

mix ecto.migrate

Welp, I have a warning. I have no idea what it means or why it appears.


Compiling 7 files (.ex)
warning: no route path for AppWeb.Router matches "/api/items/#{item}"
  lib/app_web/controllers/item_controller.ex:18: AppWeb.ItemController.create/2

warning: ItemController.init/1 is undefined (module ItemController is not available or is yet to be defined)
  lib/app_web/router.ex:2: AppWeb.Router.__checks__/0

Generated app app

21:16:43.543 [info] == Running 20230205031130 App.Repo.Migrations.CreateItems.change/0 forward

21:16:43.547 [info] create table items

Can someone explain how the hell I’m supposed to read this? There is no hint as to how to fix this

Ignoring the warning, the next thing I want to do is determine if my app configured correctly. I honestly have no idea how to do that other than to run it and assume that if I go to 4000/items that I will see an empty array or JSON object.

mix phx.server

I go to localhost:4000/items

A missing route.

I try localhost:4000/api/items

A missing route.

I looked at the Phoenix instructions and it says to run a curl command. I did.

curl -i http://localhost:4000/api/items

I got this:


HTTP/1.1 404 Not Found
cache-control: max-age=0, private, must-revalidate
content-length: 56354
content-type: text/html; charset=utf-8
date: Sun, 05 Feb 2023 03:24:09 GMT
server: Cowboy

What am I doing wrong?

EDIT

I thought I shut down the server and ran it again. The error below ran.

(Mix) Could not start application app: App.Application.start(:normal, []) returned an error: shutdown: failed to start child: AppWeb.Endpoint
    ** (EXIT) shutdown: failed to start child: {:ranch_listener_sup, AppWeb.Endpoint.HTTP}
        ** (EXIT) shutdown: failed to start child: :ranch_acceptors_sup
            ** (EXIT) {:listen_error, AppWeb.Endpoint.HTTP, :eaddrinuse}

The error has no hint that another instance of the server is running. Shouldn’t it tell the operator that “You may have another instance running on this port”. Most other platforms will tell you that.

It looks like the auto-generated template expects a route path of /api/items while in your router file you’ve defined it as /items:

resources "/items", ItemController, except: [:new, :edit]

To fix the problem, you can wrap it with a scope

scope "/api" do
  resources "/items", ItemController, except: [:new, :edit]
end

The second error is due to namespacing issues. It’s AppWeb.ItemController.
Here’s a quick overview of modules.
Phoenix apps have two toplevel modules, App and AppWeb. Someone may put this in better verbage, but you can think of App as where your business logic resides, and AppWeb to interface with the browser.

To fix the issue, you can pass in the parent module with the scope.

So the final result is:

scope "/api", AppWeb do
  resources "/items", ItemController, except: [:new, :edit]
end

Here are the relevant docs on routing with examples.

When the installer gets to the page asking me to run mix ecto.create
I stop what I am doing.
At his point I create a new command line instance, go into PostGres and I create a database. I name the database app.

I imagine the mix ecto.create didn’t work due to permissions or not configuring a postgres user. I usually run createuser -s postgres and then Ecto will be able to talk to your postgres db, for it has the default credentials of the postgres user defined in the config/dev.exs.

I highly recommend looking for an example project and fiddling with that while working on your basic API creation, though reading through the Phoenix docs will also save you a lot of confusions if this is your first time using it.

One final thing: you need to include the Plug pipeline within the scope.

I’ve not made an API with phoenix before so I can’t advise what the pipeline will be. By default there is the :browser pipeline and :api pipeline which you’ll find generated in your router.ex. Seeing as you expect to serve a template, I think you’ll want this:

scope "/api", AppWeb do
  pipe_through :api
  resources "/items", ItemController, except: [:new, :edit]
end

Is there a step by step guide that explains how to do this correctly anywhere online?

I am unclear what you want me to do.

My code is the following:


  pipeline :api do
    plug :accepts, ["json"]
    resources "/items", ItemController, except: [:new, :edit]
  end

  scope "/", AppWeb do
    pipe_through :browser

    get "/", PageController, :home
  end

Am I placing code in the scope “/” AppWeb function?

Seriously frustrated wit the discontinuity between the documentation and the reality.

The resources "/items" on line three should instead be under the scope block.
And to make the template work, you want to rename the scope to scope "/api", AppWeb do so it matches the path in your error:

warning: no route path for AppWeb.Router matches "/api/items/#{item}"
  lib/app_web/controllers/item_controller.ex:18: AppWeb.ItemController.create/2

It takes awhile to grok Phoenix/Elixir depending on your background.
You will need to reread things a couple of times and then it eventually clicks.

I don’t even know how to tell if this is working.
If I go to localhost:4000/api/items should I see an empty json object?

If not, what do I need to do to validate that I am even on the right track?

Is this what I should have?

  pipeline :api do
    plug :accepts, ["json"]
    resources "/items", ItemController, except: [:new, :edit]
  end

  scope "/", AppWeb do
    pipe_through :browser
     resources "/items", ItemController, except: [:new, :edit]
    get "/", PageController, :home
  end

You put resources "/items", ItemController, except: [:new, :edit] inside a scope block as mentioned above.

In your case it should be like this:

  pipeline :api do
    plug :accepts, ["json"]
  end

 scope "/", AppWeb do
   pipe_through :api
   
   resources "/items", ItemController, except: [:new, :edit]
 end

To have an idea of what routes being generated of the resources then type mix phx.routes in your terminal.

1 Like

Thanks this worked. I appreciate it. The prompt instructions in the command line should be more clear and beginner friendly. The documentation is difficult.

Add the resource to your :api scope in lib/app_web/router.ex:
    resources "/items", ItemController, except: [:new, :edit]

I assumed they wanted me to put the code in the function with the :api atom.
They should have an example.

EDIT:
The documentation does have an example but I was reading off the command prompt messages. So for anyone reading this, the command prompt can be confusing.

scope "/api", LinksWeb do
  pipe_through :api
  resources "/urls", UrlController, except: [:new, :edit]
end
1 Like