Build a simple api with Elixir, Plug MongoDB and Cowboy
As I’m currently working on a personnal project that involved building a service with Elixir without using Phoenix, I first struggled to find an example that was well enough explained but I eventually understood more about Plug and Cowboy so I decided to help you guys who are in the same position.
We will build a movie api that will implement all CRUD operations, the goal is not to build a full api with authentication and all other stuffs involved but just to show how to return json response, how to deal with Mongo integration, communicating with other processes and sharing the way I organize my projects
If you are just starting with Elixir, I’ll highly suggest to visit Elixir school, I think it is the best resource available for someone who’s willing to jump into the Elixir and BEAM world.
Let’s create a new project
For the project we’ll create a supervised project which will allow us to supervise all the processes within one parent process. In order to do that, we’ll need to create a new mix project with the sup option.
mix new movie_api --sup && cd movie_api
We then need to add the dependencies we need to work with. We’ll need to install Plug and Cowboy (plug_cowboy), mongodb, poolboy and then Jason for the json encoding. You’ll need to update your dependencies by adding them in the mix.exs file and in the extra_applications list of your project.
Adding the configuration files
I really love the mix configuration system, it’s so easy to understand it and configure. We first need to create a config folder in the root of our application. Then we’ll add three files in it: config.exs
dev.exs
prod.exs
test.exs
The dev, prod and test files represent the configuration files for each environment of development. Their content will be added to the config file depending on which environment we are on.
API structure
We won’t have all of our logic in the same file, we will split in two folders: services
repositories
. As our API is only required to manage the movies, each of this folders can be replaced with a single module but we’ll keep it general here.
The repositories
module will handle our interactions with the database which means that here is the only place we’ll call our database inside the project.
The services
module will handle the logic of the api, it will also make sure that all the data we receive from the client is safe and correct.
The endpoints
module will handle the http requests.
Let’s build the router
First we’ll add the router to the main supervisor. The supervisor is defined in lib/movie_api/application.ex
file.
Defining the routes
The routes will just receive an request that they’ll pass to the service which will return a response following this patter {:code, :message}
where the :code
represents an HTTP code such as 200, 404, 500 and the :message
represents the data we want to send back.
We will create a file named endpoints.ex
which will have the following content
The service module
Create a new module inside the services repository and name it movie_service.ex
Well! Now you’ve created a new module, let’s add its functions. We want it to implements the CRUD functions. Let’s add them:
The repository
As explained before, this module will handle all the database operations related to the movies. We first need to the connection with mongodb.
Then we create a new file name movie_repository.ex
in the repositories directory.
As you can see, we are using :database
to access to the mongo’s pid instead of using the pid directly.
At this point, the API should be working fine. Let’s add an explanation to what we just did.
The Repository
This is where all the database operations will be executed, we will just execute a query and return directly its results to the service that called it.
The service
The service is in charge of validating the data received and parsing the data into the correct format either in json or bson for the object ids. It will receive data from the router and will execute it by sending data in the right format to the repository which will send it back.
The endpoint
This is where we define our routes and will call the service to execute the client’s requests. It will analyze the response received from the service and will send a response depending on the result.
If we had to rename the repository by r
, the service by s
and the endpoint by e
, the execution process would look like this:
request -> e -> s -> r -> s -> e -> response