What is pipe_through :api doing for me?

Hi,

I’m playing around with Phoenix currently and am working with essentially the outcome of a mix phx.new.

In the router.ex file I have:

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

  # Other scopes may use custom stacks.
  scope "/api", HelloPhoenixWeb do
    pipe_through :api

    get "/show", PageController, :show
  end

This works just fine.

I send in my request to /api/show and I see my JSON output.

I also see a header:

content-type →application/json; charset=utf-8

All good.

If I remove pipe_through :api and send in my request, I still see that same content-type header added. I’m guessing this is now being done by Poison?

Even so, what’s the point of pipe_through :api then in this case?

I get the reasoning for pipe_through generally, I’m just confused about what this particular pipeline is giving me.

I can see it does something, because changing to pipe through :browser does indeed change the response headers.

What am I missing here?

Thanks,

Chris

Well, firstly the answer to your question: pipe_through :api just says that all routes inside this scope must pass through this pipeline’s plugs.

Now about the answer you might really want: the plug :accepts, ["json"] is a function plug that, according to it s own documentation:

Performs content negotiation based on the available formats.

It receives a connection, a list of formats that the server is capable of rendering and then proceeds to perform content negotiation based on the request information. If the client accepts any of the given formats, the request proceeds.

1 Like

It’s running the Conn through the plugs listed under pipeline :api. In this case, it’s checking the conn data structure has a content-type of json. I think it’s easier to understand when you add authentication. Here are two pipelines, one checks for basic authentication, the other for JWT.

  pipeline :api do
    plug :accepts, ["json", "json-api"]
    plug BasicAuth, use_config: {:auth, :basic_auth}
    plug JaSerializer.ContentTypeNegotiation
    plug JaSerializer.Deserializer
  end

  pipeline :api_auth do
    plug :accepts, ["json", "json-api"]
    plug JaSerializer.ContentTypeNegotiation
    plug JaSerializer.Deserializer
    plug Guardian.Plug.VerifyHeader, realm: "Bearer"
    plug Guardian.Plug.LoadResource
  end

Each of these plugs takes some action on the Conn before it is routed to the controller.

1 Like

Cheers both. I found some extra info in the docs that suggests that the outcome of piping through :api doesn’t do anything:

Conversely, if the request matches any of the routes defined by the resources/2 macro, the router will pipe it through the :api pipeline - which currently does nothing - before it dispatches further to the correct action of the HelloWeb.ReviewController.

I understand its value on more real world scenarios, and appreciate the examples.

It actually does: since the plug :accepts, ["json"] is inside the :api pipeline. If your browser/client does not accept the json response format, it will raise a Phoenix.NotAcceptableError according to the plug documentation:

This function raises Phoenix.NotAcceptableError, which is rendered with status 406, whenever the server cannot serve a response in any of the formats expected by the client.

You can think at what it does remove from normal browser pipeline…

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    
    plug CmsWeb.Locale, "fr"
  end

Going the api way will remove session, flash, forgery etc. plugs from the pipeline.

1 Like