Phoenix in Action book: Running the code in 2023

Geoffrey Lessel’s 2019 book, Phoenix in Action, was written for Phoenix 1.4.
I found that the book’s code examples did not match the current Phoenix 1.7 web application structure.
Reverting to Phoenix 1.6, I was able to build, run, and test the example Auction Web application.

As I worked through the book, I made the following changes to accomodate Phoenix 1.6:

6.1 Installing Phoenix on your system

Install the latest version of Phoenix 1.6.

mix archive.install hex phx_new 1.6.16

6.2 Creating a new Phoenix application

Phoenix 1.6 applications include a mailer by default.
The auction project does not need it.
When generating the auction_web application, omit the mailer with this command.

mix phx.new.web auction_web --no-ecto --no-mailer

Listing 6.3 shows adding this line to configure Phoenix to use Jason.

config :phoenix, :json_library, Jason

Phoenix 1.6 generates that line automatically; there is no need to add it.

6.2.1 Running your server for the first time

The “good news” also includes this advice:

Your web app requires a PubSub server to be running.
The PubSub server is typically defined in a `mix phx.new.ecto` app.
If you don't plan to define an Ecto app, you must explicitly start
the PubSub in your supervision tree as:

    {Phoenix.PubSub, name: AuctionWeb.PubSub}

That line should be added to the list of children in
auction_umbrella/apps/auction_web/lib/auction_web/application.ex:

  @impl true
  def start(_type, _args) do
    children = [
      {Phoenix.PubSub, name: AuctionWeb.PubSub},
      # Start the Telemetry supervisor
      AuctionWeb.Telemetry,
      # Start the Endpoint (http/https)
      AuctionWeb.Endpoint
      # Start a worker by calling: AuctionWeb.Worker.start_link(arg)
      # {AuctionWeb.Worker, arg}
    ]

Running the web server without this line results in a barrage of log messages such as:

08:55:41.210 [error] GenServer #PID<0.629.0> terminating
** (ArgumentError) unknown registry: AuctionWeb.PubSub
...

When first run, phx.server complained:

>mix phx.server
warning: the :gettext compiler is no longer required in your mix.exs.

Please find the following line in your mix.exs and remove the :gettext entry:

    compilers: [..., :gettext, ...] ++ Mix.compilers(),

I deleted :gettext from this line in auction_umbrella\apps\auction_web\mix.exs:

    compilers: [:gettext] ++ Mix.compilers(),

10.4 Adding site navigation

In Phoenix 1.6, root.html.hexx contains the HTML boilerplate, including the <header>.
That file has a placeholder, @inner_content, for the contents of app.html.heex,
which provides two alerts and references additional @inner_content.
The site navigation can be added before those alerts.

11.3.3 Adding the list of bids to the view template

In Listing 11.21, item_bid_path should be Routes.item_bid_path in this line:

        <%= form_for @bid, item_bid_path(@conn, :create, @item), fn f -> %>

12.2 Connecting a user to a channel and a topic

The Phoenix app was missing user_socket.ex. Create it with these commands:

>cd auction_umbrella\apps\auction_web
>mix phx.gen.socket User

Follow the resulting instructions to add the socket handler to your lib/auction_web/endpoint.ex, for example:

    socket "/socket", AuctionWeb.UserSocket,
      websocket: true,
      longpoll: false

12.2.2 Getting the user’s browser to join a topic

With Phoenix 1.6, the socket.js file is named user_socket.js.

Listing 12.4 shows the automatically generated channel topic:subtopic.
Phoenix 1.6 instead generates the channel room:42.

In Listing 12.6, also uncomment this line near the beginning of the file:

// import "./user_socket.js"

12.4.1 Refactoring the rendering of an item’s bids

The code in Listing 12.12 must be saved in bid.html.eex, not bid.html.heex.

Phoenix 16 introduced a new HTML engine that uses HTML-aware template files
(HEEx: HTML Embedded Elixir).
I did not initially notice that many automatically generated templates had the
.heex extension.

See Phoenix 1.6.0-rc.0 released!
for a description of HEEx templates.

See Phoenix.View.render_many can't render .leex templates · Issue #3952 · phoenixframework/phoenix · GitHub
for a discussion of render_many and HEEx files.

14.4.3 Viewing documentation with ExDoc

Add a project name to auction_umbrella/mix.exs:

  def project do
    [
      name: "Auction Umbrella",
      apps_path: "apps",
      version: "0.1.0",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

14.4.4 Adding examples and doctests

In the first example in Listing 14.22, start the second line with ...> and correct the double quotes
around the password confirmation value.

    iex> insert_user(%{username: "geo", password: "example",
    ...> password_confirmation: "example", email_address: "test@example.com"})
    ...> result = get_user_by_username_and_password("geo", "example")
    ...> match?(%Auction.User{username: "geo"}, result)
    true

14.5 Writing tests For Phoenix

The test runner displayed this error instead of the error shown in Listing 14.25:

  1) test GET / (AuctionWeb.PageControllerTest)
     apps/auction_web/test/auction_web/controllers/page_controller_test.exs:4
     ** (ArgumentError) cookie store expects conn.secret_key_base to be set
     code: conn = get(conn, "/")
     stacktrace: ...

Fix by adding a secret_key_base to the test.exs auction web endpoint:

config :auction_web, AuctionWeb.Endpoint,
  secret_key_base: "Dzlf6LT5j14IS16DQHviw2OuMiNjHBGGkrCDe8fMbR41Us/RMCPQfWzkigWa7Ags"

Next, the test runner displayed this error instead of that shown in Listing 14.25:

  1) test GET / (AuctionWeb.PageControllerTest)
     apps/auction_web/test/auction_web/controllers/page_controller_test.exs:4
     ** (DBConnection.OwnershipError) cannot find ownership process for #PID<0.627.0>.

     When using ownership, you must manage connections in one
     of the four ways:

     * By explicitly checking out a connection
     * By explicitly allowing a spawned process
     * By running the pool in shared mode
     * By using :caller option with allowed process

     ...

Fix by modifying auction_web/test/support/conn_case.ex to
checkout Auction.Repo in a sandbox and set its mode to shared.
Replace the setup _tags do section with:

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Auction.Repo)

    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(Auction.Repo, {:shared, self()})
    end

    {:ok, conn: Phoenix.ConnTest.build_conn()}
  end
15 Likes

This is awesome! Thank you for putting this up here.

4 Likes

Thanks for this post!!! I have been searching all over for:

(ArgumentError) unknown registry: AuctionWeb.PubSub

After a 1.5 days of searching and trying all kinds of things, I finally found your post.

I wish you could edit the title of your post to add:

(ArgumentError) unknown registry: AuctionWeb.PubSub

That is probably the first error someone is going to encounter when following the book, then they would find the rest of your post. I’ll leave some breadcrumbs at other places I was searching.

Somewhere I found something that said that adding:

{Phoenix.PubSub, name: AuctionWeb.PubSub},

should be above:

AuctionWeb.Endpoint

Thanks for taking the time to write this post. So helpful!

I’m using Phoenix Framework v1.7.10, and I’ll keep forging ahead to see how far I can get. :face_with_monocle:

1 Like

Thanks for your kind words. I’m glad my post helped you.

I’m using Phoenix Framework v1.7.10, and I’ll keep forging ahead to see how far I can get.

I look forward to reading about your solutions for Phoenix 1.7.

Manning should consider to add this posting as a part of the book’s appendix.
Nicely done!

Some more info about the AuctionWeb.PubSub problem here:

I got my database set up (it’s still empty), and everything is working well. Anything the book said to put in a config.exs file, I put it in auction_umbrella/config/config.exs.

I finished chapter 7, and everything is working error/warning free. My database has several items in it, and they are displayed on the web page.

[prevented from showing image here, oh well]

On to chapter 8 and changesets.

My full setup:

 $ asdf list erlang
  24.3.4
 *26.1.2

 $ asdf list elixir
  1.13.4
  1.14.4-otp-24
 *1.16.0-rc.1-otp-26

phoenix v1.7.10
1 Like

I finished Chap 8, and I’m in the middle of Chap 9. I’m still getting an occasional PubSub error or another Logging error. I find those errors occur when I start the server in the directory auction_umbrella/apps/auction_web. However, if I switch directories to the root directory auction_umbrella, and I start the server in that directory, then I don’t get either error.

Chapter 9
Creating the WebAuction.ItemController
The directory structure is different in Phoenix 1.7.10 than in the book. I examined the directory structure for the generated PageController, and I copied that directory structure for the ItemController and its associated files. In apps/auction_web/lib/auction_web/controllers, I created the files: item_controller.ex, item_html.ex, and a folder named item_html. Inside the item_html folder, I created the file index.html.heex.

Then I copied the contents of page_html.ex and pasted it into item_html.ex, and I changed “page” to “item”, giving me this:

defmodule AuctionWeb.ItemHTML do
  use AuctionWeb, :html

  embed_templates("item_html/*")
end

I put the following in index.html.heex:

<ul>
  <%= for item <- @items do %>
    <li><strong><%= item.title %></strong>: <%= item.description %></li>
  <% end %>
</ul>

Then I started the server in the root directory:

auction_umbrella$ mix phx.server

In my browser’s address bar, I entered localhost:4000/items, and the browser successfully displayed the items in my database. But there was a broken image in the upper left hand corner, which I resolved to fix.

I looked at the source html in my browser, and the html contained this:

            <a href="/">
                <img src="/images/logo.png" width="36">
            </a>

Because that was not in my item.html.heex file, that had to be coming from the layout. So I looked at auction_web/components/layouts/root.html.heex, but that link wasn’t in there. Next, I opened up app.html.heex, and it contained this html:

    <a href="/">
      <img src={~p"/images/logo.png"} width="36" />
    </a>

Next, I did some searching around about where images should go in a Phoenix app. I think they are supposed to go in auction_web/priv/static/images. I’m using vscode, and I discovered that vscode wasn’t showing all the directories and files, and it was hiding the priv/static directory. Why is that?! In the vscode menu bar, I went to Code>Preferences>Settings and on the left I clicked on TextEditor>Files. Scrolling down in the right hand pane, I found the heading “Exclude”, and in the list of excluded files was **/priv/static. What?! I deleted that, and then the full priv/ dir was displayed in vscode.

Next, I created the directory priv/static/images, and then I downloaded a transparent image of the Phoenix logo, and I named it logo.png, and I put it in the images directory. Finally, I changed the html in app.html.eex to reflect the image name:

      <a href="/">
        <img src={~p"/images/logo.png"} width="36" />
      </a>

I looked up what the ~p sigil does, and it asks Phoenix to verify that there is a valid route for the specified path. Well, there’s nothing in my route.ex file for that image, so I think I should get a compile time error. But when I start the server in the root dircetory or run iex -S mix in the root directory, I don’t get any errors.

1 Like

Corrected some errors:

I finished Chap 8, and I’m in the middle of Chap 9. I’m still getting an occasional PubSub error or another Logging error. I find those errors occur when I start the server in the directory auction_umbrella/apps/auction_web. However, if I switch directories to the root directory auction_umbrella, and I start the server in that directory, then I don’t get either error. I asked about those errors here:

Chapter 9
Creating the WebAuction.ItemController
The directory structure is different in Phoenix 1.7.10 than in the book. I examined the directory structure for the generated PageController, and I copied that directory structure for the ItemController and its associated files. In apps/auction_web/lib/auction_web/controllers, I created the files: item_controller.ex, item_html.ex, and a folder named item_html. Inside the item_html folder, I created the file index.html.heex.

Then I copied the contents of page_html.ex and pasted it into item_html.ex, and I changed “page” to “item”, giving me this:

defmodule AuctionWeb.ItemHTML do
  use AuctionWeb, :html

  embed_templates("item_html/*")
end

I put the following in index.html.heex:

<ul>
  <%= for item <- @items do %>
    <li><strong><%= item.title %></strong>: <%= item.description %></li>
  <% end %>
</ul>

Then I started the server in the root directory:

auction_umbrella$ mix phx.server

In my browser’s address bar, I entered localhost:4000/items, and the browser successfully displayed the items in my database. But there was a broken image in the upper left hand corner, which I resolved to fix.

I looked at the source html in my browser, and the html contained this:

            <a href="/">
                <img src="/images/logo.png" width="36">
            </a>

Because that was not in my item.html.heex file, that had to be coming from a layout. So I looked at lib/auction_web/components/layouts/root.html.heex, but that link wasn’t in there. Next, I opened up app.html.heex, and it contained this html:

    <a href="/">
      <img src={~p"/images/logo.png"} width="36" />
    </a>

Next, I did some searching around about where images should go in a Phoenix app. I think they are supposed to go in apps/auction_web/priv/static/images. I’m using vscode, and I discovered that vscode wasn’t showing all the directories and files, and it was hiding the priv/static directory. Why is that?! In the vscode menu bar, I went to Code>Preferences>Settings and on the left I clicked on TextEditor>Files. Scrolling down in the right hand pane, I found the heading “Exclude”, and in the list of excluded files was **/priv/static. What?! I deleted that, and then the full priv/ dir was displayed in vscode.

Next, I created the directory priv/static/images. It seems pretty strange to me that the Phoenix generators would create a layout containing a link with a path to a non-existent directory. Then I downloaded a transparent image of the Phoenix logo I found on the internet, and I named it logo.png, and I put it in the images directory. Finally, I changed the html in app.html.heex to reflect the image name:

      <a href="/">
        <img src={~p"/images/logo.png"} width="36" />
      </a>

I looked up what the ~p sigil does, and it asks Phoenix to verify that there is a valid route for the specified path. Well, there’s nothing in my route.ex file for that image, so I think I should get a compile time error. But when I start the server in the root directory or run iex -S mix in the root directory, I don’t get any errors.

While I was looking in router.ex, I noticed this route:

    scope "/dev" do
      pipe_through(:browser)

      live_dashboard("/dashboard", metrics: AuctionWeb.Telemetry)
    end

Try entering the following in your browser:

localhost:4000/dev/dashboard

1 Like

Corrected some errors:

I finished Chap 8, and I’m in the middle of Chap 9. I’m still getting an occasional PubSub error or another Logging error. I find those errors occur when I start the server in the directory auction_umbrella/apps/auction_web. However, if I switch directories to the root directory auction_umbrella, and I start the server in that directory, then I don’t get either error. I asked about those errors here:

Chapter 9
Creating the AuctionWeb.ItemController
The directory structure is different in Phoenix 1.7.10 than in the book. I examined the directory structure for the generated PageController, and I copied that directory structure for the ItemController and its associated files. In apps/auction_web/lib/auction_web/controllers, I created the files: item_controller.ex, item_html.ex, and a folder named item_html. Inside the item_html folder, I created the file index.html.heex.

Then I copied the contents of page_html.ex and pasted it into item_html.ex, and I changed “page” to “item”, giving me this:

defmodule AuctionWeb.ItemHTML do
  use AuctionWeb, :html

  embed_templates("item_html/*")
end

I put the following in index.html.heex:

<ul>
  <%= for item <- @items do %>
    <li><strong><%= item.title %></strong>: <%= item.description %></li>
  <% end %>
</ul>

Then I started the server in the root directory:

auction_umbrella$ mix phx.server

In my browser’s address bar, I entered localhost:4000/items, and the browser successfully displayed the items in my database. But there was a broken image in the upper left hand corner, which I resolved to fix.

I looked at the source html in my browser, and the html contained this:

            <a href="/">
                <img src="/images/logo.png" width="36">
            </a>

Because that was not in my item.html.heex file, that had to be coming from a layout. So I looked at lib/auction_web/components/layouts/root.html.heex, but that link wasn’t in there. Next, I opened up app.html.heex, and it contained this html:

    <a href="/">
      <img src={~p"/images/logo.png"} width="36" />
    </a>

Next, I did some searching around about where images should go in a Phoenix app. I think they are supposed to go in apps/auction_web/priv/static/images. I’m using vscode, and I discovered that vscode wasn’t showing all the directories and files, and it was hiding the priv/static directory. Why is that?! In the vscode menu bar, I went to Code>Preferences>Settings and on the left I clicked on TextEditor>Files. Scrolling down in the right hand pane, I found the heading “Exclude”, and in the list of excluded files was **/priv/static. What?! I deleted that, and then the full priv/ dir was displayed in vscode.

Next, I created the directory priv/static/images. It seems pretty strange to me that the Phoenix generators would create a layout containing a link with a path to a non-existent directory. Then I downloaded a transparent image of the Phoenix logo I found on the internet, and I named it logo.png, and I put it in the images directory. Finally, I changed the html in app.html.heex to reflect the image name:

      <a href="/">
        <img src={~p"/images/logo.png"} width="36" />
      </a>

I looked up what the ~p sigil does, and it asks Phoenix to verify that there is a valid route for the specified path. Well, there’s nothing in my route.ex file for that image, so I think I should get a compile time error. But when I start the server in the root directory or run iex -S mix in the root directory, I don’t get any errors.

While I was looking in router.ex, I noticed this route:

    scope "/dev" do
      pipe_through(:browser)

      live_dashboard("/dashboard", metrics: AuctionWeb.Telemetry)
    end

Try entering the following in your browser:

localhost:4000/dev/dashboard

1 Like

This is amazing. Thank you so much for going through this and documenting it so thoroughly!!!

@7stud ,
Thanks for your detailed sharing!
I am working my way through the book, and am stuck at Ch 6.2.1 Running Your Server for the First Time. When I run

mix phx.server

I get the following messages & error:

16:00:09.503 [info] Running AuctionWeb.Endpoint with Bandit 1.5.0 at 127.0.0.1:4000 (http)
16:00:09.505 [info] Access AuctionWeb.Endpoint at http://localhost:4000
[watch] build finished, watching for changes...
node:internal/modules/cjs/loader:1146
  throw err;
  ^

Error: Cannot find module '/Users/pjteh/Desktop/Elixir/phoenix-in-action/auction_umbrella/apps/auction_web/assets/node_modules/webpack/bin/webpack.js'
    at Module._resolveFilename (node:internal/modules/cjs/loader:1143:15)
    at Module._load (node:internal/modules/cjs/loader:984:27)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
    at node:internal/main/run_main_module:28:49 {
  code: 'MODULE_NOT_FOUND',
  requireStack: []
}

Node.js v20.12.2

Rebuilding...

Done in 136ms.
16:00:11.528 [error] Task #PID<0.607.0> started from AuctionWeb.Endpoint terminating
** (stop) :watcher_command_error
    (phoenix 1.7.12) lib/phoenix/endpoint/watcher.ex:55: Phoenix.Endpoint.Watcher.watch/2
    (elixir 1.16.2) lib/task/supervised.ex:101: Task.Supervised.invoke_mfa/2
Function: &Phoenix.Endpoint.Watcher.watch/2
    Args: ["node", ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin", {:cd, "/Users/pjteh/Desktop/Elixir/phoenix-in-action/auction_umbrella/apps/auction_web/assets"}]]

Do you get a similar error if you run this now?

I’m a bit confused by the error message about the unfound node module, since the Phoenix docs seem to indicate that there’s no dependency on node or webpack:

From Phoenix v1.7, new applications use esbuild to prepare assets via the Elixir esbuild wrapper, and tailwindcss via the Elixir tailwindcss wrapper for CSS. 
The direct integration with esbuild and tailwind means that newly generated applications do not have dependencies on Node.js or an external build system (e.g. Webpack).

My setup is very similar to yours:

Elixir 1.16.2 (compiled with Erlang/OTP 26)
Phoenix v1.7.12

So I’m wondering if you might have encountered a similar problem when running this on Phoenix 1.7.10

Any help would be greatly appreciated! :pray: Thank you in advance!

With kindness,
PJ