Best practice for phoenix project with HTML and JSON endpoints

This is a more general response, but I believe that the predominant pattern in situations like this is to extract most/all logic from the controller-layer to an intermediate ‘domain’ layer, which is shared by the HTML- and JSON-controllers. This approach is known as ‘domain-driven-design’.

The only thing the controllers then do is to be ‘traffic cops’ redirecting the flow to/from the functions in these domain layer, and transform e.g. error responses from the domain layer to an appropriate response in the current format: for HTML you might show a flash-message whereas in the JSON-API you might want to replace the normal response with something akin {"status": "error", "message": "This was the problem"}.