Nithin Bekal About

Writing and publishing an Elixir library

13 Jun 2016

Recently, I wrote some code to query Google Books API, and extracted it into a Hex package. In this article, we will walk through the process of publishing an Elixir package to hex.pm.

Let’s start by generating a new project called google_books using mix:

mix new google_books

The files we will be working with are mix.exs and lib/google_books.ex, which contains the GoogleBooks module where we will write our code.

Adding dependencies

Since we’re working with an API, we will need to depend on other libraries for HTTP requests and JSON parsing. Let’s add httpoison and poison libraries in mix.exs, by editing the deps and application functions.

# mix.exs
def application do
  [application: [:httpoison, :poison]]
end

# ...

defp deps do
  [
    {:httpoison, "~> 0.8.0"},
    {:poison, ">= 1.0.0"},
  ]
end

Writing the code

Our library will have just one module, GoogleBooks, which provides the find_by_isbn/1 function. This will be the code for our initial version of the package:

defmodule GoogleBooks do
  @api_url "https://www.googleapis.com/books/v1/volumes"

  def find_by_isbn(isbn) do
    isbn
    |> remove_dashes
    |> build_url
    |> get_json
    |> decode_response
    |> extract_volume_info
  end

  defp remove_dashes(isbn), do: String.replace(isbn, "-", "")
  defp build_url(isbn), do: "#{@api_url}?q=isbn:#{isbn}"
  defp get_json(url), do: HTTPoison.get!(url).body
  defp decode_response(json), do: Poison.decode!(json)

  defp extract_volume_info(%{"totalItems" => 0}), do: :not_found

  defp extract_volume_info(response) do
    hd(response["items"])["volumeInfo"]
  end
end

This is a simple module, so we won’t walk through the code itself, and instead focus on publishing the package to Hex. You can find the latest version of the source code on Github at nithinbekal/google_books.ex.

Adding package metadata

Before we publish the package to Hex, we need to add a description and package metadata for the package. Make the following changes to the mix file.

# mix.exs

def project do
  [app: :google_books,
    version: "0.0.1",
    elixir: "~> 1.2",
    description: "A simple wrapper for Google Books API",
    package: package,
    deps: deps]
end

def package do
  [ name: :google_books,
    files: ["lib", "mix.exs"],
    maintainers: ["Nithin Bekal"],
    licenses: ["MIT"],
    links: %{"Github" => "https://github.com/nithinbekal/google_books.ex"},
  ]
end

Registering on Hex

You can register as a user on Hex by using the mix hex.user register command. Skip this part if you already have registered a hex.pm account.

mix hex.user register

You will be prompted to enter username, email and password. A confirmation email will be sent to you, and you need to click on the confirmation URL before continuing.

Publishing the package to Hex

Finally, we’re ready to publish the package on Hex. We can do this using the mix hex.publish command.

$ mix hex.publish
Publishing google_books 0.0.1
  Dependencies:
    httpoison ~> 0.8.0
    poison ~> 2.0
  Files:
    lib/google_books.ex
    mix.exs
  App: google_books
  Name: google_books
  Description: A simple wrapper for Google Books API
  Version: 0.0.1
  Build tools: mix
  Licenses: MIT
  Maintainers: Nithin Bekal
  Links:
    Github: https://github.com/nithinbekal/google_books.ex
  Elixir: ~> 1.2
Before publishing, please read Hex Code of Conduct: https://hex.pm/policies/codeofconduct
Proceed? [Yn]
[#########################] 100%
Published at https://hex.pm/packages/google_books/0.0.1
Don't forget to upload your documentation with `mix hex.docs`

Now the package is available at https://hex.pm/packages/google_books. Before we wind up, we need to add one final piece to our package - documentation.

Add documentation

First, let’s add ex_doc and earmark as development dependencies in the mix.exs deps list. ExDoc lets you generate documentation for your project. Earmark markdown parser that ExDoc depends on to parse inline documentation.

# mix.exs
{:earmark, ">= 0.0.0", only: :dev},
{:ex_doc, ">= 0.0.0", only: :dev},

Next, let’s add some documentation for the GoogleBooks module using the @moduledoc module attribute.

defmodule GoogleBooks do
  @moduledoc """
  Provides a wrapper for the Google Books API.

  Currently only supports finding a book by ISBN.
  """

  # ...
end

We will also add documentation to the find_by_isbn/1 like this:

  @doc """
  Searches for a book by the given ISBN.
  Returns a map containing the book information if found.

  Returns the atom `:not_found` if there is no result.

      iex> GoogleBooks.find_by_isbn("978-1338099133")
  """
  def find_by_isbn(isbn) do
    # ...
  end

Now generate the docs by using mix docs. You can also push the docs to https://hexdocs.pm by using the mix hex.docs command.

You might also want some other files, like README, included as part of the generated docs. To do this, add this line to include README file in the docs:

# mix.exs
def project do
  [ #...
    docs: [extras: ["README.md"]],
    # ...
  ]
end

If you generate the docs again, you will see the README file in the docs.

That’s all we will cover in this post. As you can see, it’s very easy to package your Elixir code into a hex package. If you’ve followed this tutorial to extract out libraries, I’d love to hear about them - please do leave a comment here.

Hi, I’m Nithin! This is my blog about programming. Ruby is my programming language of choice and the topic of most of my articles here, but I occasionally also write about Elixir, and sometimes about the books I read. You can use the atom feed if you wish to subscribe to this blog or follow me on Mastodon.