Using MongoDB with Elixir, Phoenix and Ecto
This is as much for my reference as anyone else's, so I hope everything is clear enough for newcomers.
Update: Refer here for an example project following the method below.
Due to having gotten a little bored with Node.js in recent times, and also a lack of engagement with the latest MongoDB releases, I felt it was time to undertake something new - a small side project I have been thinking about using Phoenix, which is written in Elixir. Having decided to use MongoDB for my datasource, I set about finding how to get started using it alongside Phoenix, but unfortunately it appears there is no official support (yet) for Mongo in Ecto, which is the ORM layer driving Phoenix. However, there are several unofficial solutions out there which seem to work reliably enough; that is what this post is going to quickly cover. Please note that the MongoDB Ecto lib we will use is still in development.
I imagine this guide will be simple enough for even those entirely new to Elixir, as I figured it out myself and I have only glanced at it up until this point. Having said that, please contact me in the comments/email/Twitter if anything remains unclear; I'll be happy to help.
This post assumes, at a minimum, that Elixir, Phoenix and Mongo are all installed. Ensure Hex is installed, too. Here are some installation resources:
Creating A Project
Phoenix has all the typical trimmings which make a framework truly nice to use. We can create a new project using the following syntax:
$ mix phoenix.new project_name
This will generate a directory named project_name in the current directory. You will be prompted to install any dependencies - hit no on this, because otherwise you'll have unneeded PostgreSQL stuff installed.
whitfin:/tmp$ mix phoenix.new project_name
* creating project_name/config/config.exs
* creating project_name/config/dev.exs
* creating project_name/config/prod.exs
* creating project_name/config/prod.secret.exs
* creating project_name/config/test.exs
* creating project_name/lib/project_name.ex
* creating project_name/lib/project_name/endpoint.ex
* creating project_name/test/controllers/page_controller_test.exs
* creating project_name/test/views/error_view_test.exs
* creating project_name/test/views/page_view_test.exs
* creating project_name/test/support/conn_case.ex
* creating project_name/test/support/channel_case.ex
* creating project_name/test/test_helper.exs
* creating project_name/web/controllers/page_controller.ex
* creating project_name/web/templates/layout/app.html.eex
* creating project_name/web/templates/page/index.html.eex
* creating project_name/web/views/error_view.ex
* creating project_name/web/views/layout_view.ex
* creating project_name/web/views/page_view.ex
* creating project_name/web/router.ex
* creating project_name/web/web.ex
* creating project_name/mix.exs
* creating project_name/README.md
* creating project_name/lib/project_name/repo.ex
* creating project_name/test/support/model_case.ex
* creating project_name/.gitignore
* creating project_name/brunch-config.js
* creating project_name/package.json
* creating project_name/web/static/css/app.scss
* creating project_name/web/static/js/app.js
* creating project_name/web/static/assets/robots.txt
* creating project_name/web/static/vendor/phoenix.js
* creating project_name/web/static/assets/images/phoenix.png
* creating project_name/web/static/assets/images/favicon.ico
Fetch and install dependencies? [Yn]
You should be greeted with the following, describing the following steps to take in order to get your project up and running.
Phoenix uses an optional assets build tool called brunch.io
that requires node.js and npm. Installation instructions for
node.js, which includes npm, can be found at http://nodejs.org.
After npm is installed, install your brunch dependencies by
running inside your app:
$ npm install
If you don't want brunch.io, you can re-run this generator
with the --no-brunch option.
We are all set! Run your Phoenix application:
$ cd project_name
$ mix deps.get
$ mix phoenix.server
You can also run it inside IEx (Interactive Elixir) as:
$ iex -S mix phoenix.server
And voila, your project is ready to go. Now just to set it up for Mongo. At this point, you should cd project_name.
Configuring Ecto
This section has a few moving parts, so let's start from the top-down.
Mix.exs
Open up this file, and you should be greeted with something like this:
defmodule ProjectName.Mixfile do
use Mix.Project
def project do
[app: :project_name,
version: "0.0.1",
elixir: "~> 1.0",
elixirc_paths: elixirc_paths(Mix.env),
compilers: [:phoenix] ++ Mix.compilers,
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps]
end
# Configuration for the OTP application
#
# Type `mix help compile.app` for more information
def application do
[mod: {ProjectName, []},
applications: [:phoenix, :phoenix_html, :cowboy, :logger,
:phoenix_ecto, :postgrex]]
end
# Specifies which paths to compile per environment
defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
defp elixirc_paths(_), do: ["lib", "web"]
# Specifies your project dependencies
#
# Type `mix help deps` for examples and options
defp deps do
[{:phoenix, "~> 0.14"},
{:phoenix_ecto, "~> 0.5"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 1.1"},
{:phoenix_live_reload, "~> 0.4", only: :dev},
{:cowboy, "~> 1.0"}]
end
end
We need to remove the references to postgrex as this is the PostgreSQL driver. So remove the entry from both deps and the applications list. We then need to add the references to the Mongo Ecto library, like so:
applications: [:phoenix, :phoenix_html, :cowboy, :logger,
:phoenix_ecto, :mongodb_ecto, :ecto]]
defp deps do
[{:phoenix, "~> 0.14"},
{:phoenix_ecto, "~> 0.5"},
{:phoenix_html, "~> 1.1"},
{:phoenix_live_reload, "~> 0.4", only: :dev},
{:cowboy, "~> 1.0"},
{:mongodb_ecto, github: "michalmuskala/mongodb_ecto"}]
end
lib/project_name/repo.ex
We need to add the Mongo adapter to your app Repo, so just add an extra value to this module:
defmodule ProjectName.Repo do
use Ecto.Repo, otp_app: :project_name, adapter: Mongo.Ecto
end
config/*.exs
We now need to go through all config files, starting with "dev.exs". Simply replace the PostgreSQL config with your MongoDB config.
config :project_name, ProjectName.Repo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
database: "project_name_dev",
size: 10 # The amount of database connections in the pool
# becomes
config :project_name, ProjectName.Repo,
database: "mongo_database_project",
username: "mongodb", # remove if unneeded
password: "mongosb", # remove if unneeded
hostname: "localhost"
Replace this section with the piece pasted above in all files, except "prod.exs".
Testing it out
$ npm install # optional
$ mix deps.get
$ mix phoenix.server
You should now see your application compiling, and then a message telling you your application is running on localhost:4000
. From this point onwards, you should be able to use the Mongo adapter like any regular Ecto adapter.
If you get an error at this point (I do), relax. This is an issue with the dependencies, which can be resolved by adding the following to your deps list in mix.exs (note; this may change, you should use the version specified in the error message):
{:ecto, git: "git://github.com/elixir-lang/ecto.git", ref: "0c530cdced6254b133bb6804908308d83b637326", override: true}
After that, run mix deps.get again, and retry mix phoenix.server.
Notes
Credit goes to Michał Muskała, for their awesome work on the Mongo Ecto adapter. If you find any issues whilst using the adapter, please files issues on the GitHub repo, as feedback is the only way these things can be improved :-).