My current client project requires that all public facing APIs exposed by our Elixir Phoenix application be camel cased, rather than snake cased. Thanks to the help of the plugs provided by casex, this was no sweat to implement. However, the Phoenix router in this project makes heavy use of nesting the resource/4 macro, which poses a problem when combined with the open_api_spex package we’re using to build and serve our project’s Swagger documentation.

Imagine we’re defining a “Foos” resource with a nested “Bars” index route in our router like so:


resources "/foos", Web.FooController do
  get "/bars", Web.BarController, :foo_index
end

The open_api_spex package lets you document your path parameters following a camel case convention, if you choose to do so. We could document the :foo_index action in our Web.BarController like so:


operation :foo_index,
parameters: [
  fooId: [
    in: :path,
    type: :integer,
    ...
  ]
],
...

In our example, fooId will appear in our route’s list of documented parameters within our Swagger documentation, but the route itself will still look like this:


/foos/{foo_id}/bars

Not only is the route showing its parameter in snake case, but the corresponding documentation of that parameter is in camel case. This is confusing to say the least.

We need to change how open_api_spex displays the nested resource variables in our application’s API routes.

Out of the box, the open_api_spex library uses OpenApiSpex.Paths.from_router/1 to build a map from your application’s routes to OpenApiSpex.PathItem structs describing those routes.

The map containing our nested route might look something like this:


%{
  "/foos/{foo_id}/bars" => %OpenApiSpex.PathItem{
    ...
  },
  ...
}

The keys to this map are literally the string representations of the routes in your application. These keys are what’s displayed in Swagger UI, and they’re what we need to change in order to fix our problem.

To change the offending snake cased foo_id in our path, we simply need to map over the result of OpenApiSpex.Paths.from_router/1 and camel case every key in the map:


def spec do
  %OpenApi{
    ...
    # Populate the paths from a phoenix router
    paths:
      Paths.from_router(Router)
      |> camel_case_paths()
  }
  |> OpenApiSpex.resolve_schema_modules()
end

defp camel_case_paths(paths) do
  paths
  |> Enum.map(fn {key, value} -> {Recase.to_camel(key), value} end)
  |> Enum.into(%{})
end

In this example we’re using recase to handle our camel casing.

The resulting map looks something like this:


%{
  "/foos/{fooId}/bars" => %OpenApiSpex.PathItem{
    ...
  },
  ...
}

Opening up our Swagger documentation, we can see that the path parameter in our route’s URL is camel cased, as expected:


/foos/{fooId}/bars

Victory!