Maverick

Web API framework with a need for speed

Use

Maverick builds on top of the Plug.Conn.Adapter for your supported webserver and creates the webserver handler by reading annotations on functions you want to expose as part of your API. To publish a function, add use Maverick to your module and the @route attribute to the relevant functions. Pass a keyword list of options to the route attribute including the :path and you're off to the races.

Once you add the Maverick.Api to your application supervision tree and start the app, Maverick compiles your routes into a handler module and sends incoming requests to your functions, taking care to wrap the return values accordingly.

Example

With Maverick added to your application's dependencies, create a module that implements the use Maverick.Api macro and pass it, at a minimum, the name of your application:

  defmodule CoolApp.Api do
    use Maverick.Api, otp_app: :cool_app
  end

Then, in your application supervision tree, add your Maverick Api to the list of children:

  children =
    [
      maybe_a_database,
      other_cool_stuff,
      {CoolApp.Api, name: :cool_app_web, port: 443},
      anything_else
    ]

Now it's time to annotate the functions you want served over HTTP. These can be anywhere within your project structure that make sense to you, Maverick will compile them all into your callback handler. While it's considered best practice to structure your code to separate domain concerns and maintain good abstractions, in practice this organization has no effect on what functions are available to be routed to by Maverick. Add use Maverick to a module that will be serving functions and then any public function (sorry, no macros) with the @route annotation will do:

  defmodule CoolApp.BusinessLogics do
    use Maverick, scope: "api/v1"

    @route path: "do/stuff"
    def do_stuff(%{"stuff" => what_needs_doing}) do
      what_needs_doing |> transform_process_etc
    end
  end

Once the app is started, you can reach your function at the path, method, etc. configured on the route annotation, such as: curl -XPOST -d '{\"stuff\":\"transform-me!\"}' "http://host:port/api/v1/do/stuff"

Route Options

The following options can configure functions annotated with the @route attribute:

Return Values

Maverick aims to be simple to use, passing some form of map to your function (see :args in the above "Route Options" section) and taking virtually any JSON-encodable term as a result to return to the client.

That said, the following are acceptable return values for your routed functions; use the simplest one that meets your needs and it will be converted accordingly to allow the webserver to hand it back to the client:

In the absence of a full-detailed response return value, Maverick will apply the :success_code for the status code of {:ok, response()} and implicitly successful response() results and the :error_code for the status code of {:error, response()} results.

When response headers are returned, they are expected to be in the form of a map, as this is the format in which the function will receive request headers. They will be converted to [{String.t(), String.t()}] before handing back to the webserver to return to the client. If no response headers are returned, Maverick will automatically add the {"Content-Type", "application/json"} header.

Exceptions

In the event an exception is raised during handling of a request, the Handler functions will automatically rescue and construct a response by calling functions from the the Maverick.Exception protocol on the exception. See the exception protocol module for implementing it for specific exceptions.

But Why?

A full web framework with support for live content updates and server-side rendering is great, but sometimes you just want a way to handle JSON-based service requests over HTTP without all the bells and whistles.

Maverick aims to fill the niche between a full Phoenix Framework just for providing a web-based API and hand-rolling a Plug API.

To Do

Installation

The package can be installed by adding maverick to your list of dependencies in mix.exs:

def deps do
  [
    {:maverick, "~> 0.2.0"}
  ]
end

The docs can be found at https://hexdocs.pm/maverick.