Separate routes for different pipelines

Hi everyone,

I’m building a blog where I want anyone coming to my website to be able to see all posts and also the specific page of each post (so index and show routes). But I want only users with is_admin: true to be able to create, edit or delete post.

Hence, here is what I did:

router.ex:

 [...]
  pipeline :admin_required do
    plug Gazette.CheckAdmin
  end

  scope "/", GazetteWeb do
    pipe_through [:browser, :with_session]

    get "/", PageController, :index

    resources "/users", UserController, only: [:show, :new, :create]
    resources "/sessions", SessionController, only: [:new, :create, :delete]
    resources "/posts", PostController, only: [:index, :show]
  end

  scope "/posts", GazetteWeb do
    pipe_through [:admin_required]
    resources "/", PostController, except: [:index, :show]
  end
 [...]

my problem is, when I go to /posts/new, Phoenix believes I make a request to the show route (/posts/:id) where id is “new”. In another word it goes to the wrong pipeline.

How can I solve that ?

Try moving your routes around so that :new comes before :show. I don’t see /posts/new in your code snippet … But for users that would be

resources "/users", UserController, only: [:new, :show, :create]

instead of what you have right now.

Oh, I see where your posts/new route is! I misread except as only.

Does it work if you move admin routes above non-admin routes?

  scope "/posts", GazetteWeb do
    pipe_through [:admin_required]
    resources "/", PostController, except: [:index, :show]
  end

  scope "/", GazetteWeb do
    pipe_through [:browser, :with_session]

    get "/", PageController, :index

    resources "/users", UserController, only: [:show, :new, :create]
    resources "/sessions", SessionController, only: [:new, :create, :delete]
    resources "/posts", PostController, only: [:index, :show]
  end

Then posts/new would have a higher “precedence” than posts/:id for show.


Also, shouldn’t pipe_through for admin also have :browser pipeline to fetch session, for example?

  scope "/posts", GazetteWeb do
    pipe_through [:browser, :admin_required]
    resources "/", PostController, except: [:index, :show]
  end
2 Likes

It works indeed ! but doesn’t it looks like duplicated code a bit ?
I think that is the right way to do it but perhaps there is a way to refactor that code ?

pipeline :admin_required do
    plug Gazette.CheckAdmin
  end

  scope "/posts", GazetteWeb do
    pipe_through [:browser, :with_session, :admin_required]
    resources "/", PostController, except: [:index, :show]
  end

  scope "/", GazetteWeb do
    pipe_through [:browser, :with_session]

    get "/", PageController, :index

    resources "/users", UserController, only: [:show, :new, :create]
    resources "/sessions", SessionController, only: [:new, :create, :delete]
    resources "/posts", PostController, only: [:index, :show]
  end

I don’t see much code duplication but maybe

scope "/", GazetteWeb do
    pipe_through [:browser, :with_session]

    get "/", PageController, :index

    scope "/posts", GazetteWeb do
      pipe_through :admin_required
      resources "/", PostController, except: [:index, :show]
    end

    resources "/users", UserController, only: [:show, :new, :create]
    resources "/sessions", SessionController, only: [:new, :create, :delete]
    resources "/posts", PostController, only: [:index, :show]
  end
1 Like

Yes exactly ! that’s what I was looking for. I am new to Elixir/Phoenix and I thought maybe we could net scope inside one another. Great, thank you !!!