Phoenix equivalent of Django's reusable apps (best practices)?

Hi folks,

I come from a mostly Python+Django background, and I’ve only played with Elixir+Phoenix a little on personal side projects. Phoenix is really appealing to me because it addresses a lot of the things that have pained me when working on large Django projects. There are some things I miss of course, like the Django admin and more straightforward auth story, but I’m happy to make that tradeoff.

But the one thing that is really important to me that I don’t know how to do in Phoenix is to create reusable apps:

In Django, you can create apps that have their own bundled resources, and import those apps into different projects. So there might be a wiki Django app (there are actually probably a hundred), and if I pip install it into my Python environment, I can then add that Django app to the list of installed apps in my Django project, hook it up with a base URL, and now I have an app that I didn’t write.

Sometimes I want to create apps that have small bits of relatively self contained functionality (like that wiki). Sometimes I want to create a whole set of apps that are meant to go together, but might plausibly be extended – say a learning management system that’s open to minor extensions, because Site A might want to add some new model that has a foreign key relation to Students.

I have no doubt that something like this is possible in Phoenix, but what are the best practices for doing so? Are there any particularly stand-out examples I should look at?

Thank you.



Hey ormsbee,

good to have you aboard! :slight_smile:

While I am not the most qualified to answer you architectural questions in depth, I think elixir provides multiple abstractions for what you want to achieve. The most straight forward way would be to use an umbrella application. An umbrella is essentially a mono repo for multiple elixir applications with a shared dependency folder. As far as I know it is not a 1:1 mapping for Django’s app concept, but it is actually pretty good and easy enough to understand. It also creates great encapsulation.

As per your remark regarding more straightforward auth story: this was hands down my biggest gripe with Phoenix until I discovered - just give it a try. I think it is fantastic! :slight_smile:


You may have noticed that the phoenix router and request pipeline is based on As you might have guessed from it’s name that API is very pluggable :slight_smile: It’s possible to mount plugs in the phoenix router for instance like this

defmodule MyProject.Router do
  use MyProject.Web, :router
  use ExAdmin.Router
  scope "/", MyProject do

  # setup the ExAdmin routes on /admin
  scope "/admin", ExAdmin do
    pipe_through :browser

This will mount the ExAdmin app/plug routes under /admin. (see for more about the library)

If you dig a bit you’ll see that the plug API is very small. Elixir being a functional language the abstractions tend to be far simpler than OO languages as the functional abstractions in the language do a lot of the work for you.

1 Like

Another example of adding self contained functionality to an existing Phoenix app is exq_ui

In that example the forward/4 function mounts one router plug at a path within the main app router.


Thanks folks. I’ll read through the links you posted. I was aware of Plug, but I’m not sure I really grok it. My Python mental mapping of Plug is “WSGI/ASGI”, which is integration at a lower level than I was thinking of. But you folks have given me a great set of starting points, and certainly enough for me to try things out and see what roadblocks I run into next.

Take care.

1 Like

Mix is your tool. Mix dependencies are just elixir apps.

The mix.exs is where you can include your reusable “apps” along with other dependencies. You do not have to pull a dependency from hex. You can write your own reusable elixir apps and include it with git or even a file system path.

To make a reusable app you can do mix new reusable_app (outside of an existing app) then include it in your main app’s mix.exs.

# published to
{:reusable_app, ">= 0.4.0"}
# git
{:reusable_app, git: ""}
# local
{:reusable_app, path: "path/to/local/reusable_app"}

The other posts have pointed out good examples of mounting routes. Overall the process is not much different between the two. Just different build tools and package managers.


1 Like