finch

don't use this because it's going to change to use the plug spec

this is a little thing that sits in between phoenix and ecto that makes building CRUDy REST APIs really simple. It does not offer many features. It aims to be really small and easy to understand.

Things it will do:

Things it won't do:

usage



defmodule MyCoolApp.Models.Foo do
  use Finch.Model

  schema "foos" do
    field :title, :string
    field :text, :string
  end

end

defmodule MyCoolApp.Resources.Foo do
  use Finch.Resource
  
  def repo, do: MyCoolApp.Repo
  def model, do: MyCoolApp.Models.Foo

end


defmodule MyCoolApp.Router do
  use Phoenix.Router

  scope path: "/api" do
    scope path: "/v1" do
      resources "/foo", MyCoolApp.Resources.Foo
    end
  end
end

The code above will grant you the following powers...

method route result
GET /api/v1/foo list all the foos
POST /api/v1/foo make a foo
GET /api/v1/foo/#{id} get a foo with id
PUT /api/v1/foo/#{id} update a foo with id
DELETE /api/v1/foo/#{id} delete a foo with id

filtering, paging, and ordering

paging

By default, a GET request to an index endpoint will page the models. The default page size is 40, but you can implement page_size/0 to override that. Adding the ?page=some_number url parameter will fetch the page you specify. index endpoints return a meta object that gives you information relevant to paging.

filtering

You can filter on fields as well, which just does a case insensitive like query. you can change how the filtering happens by overriding apply_filters/2 in your resource.

Adding ?filter=field_name:value will select the models where field_name is like value

ordering

You can order the index endpoint. Add ?order=field_name to get models sorted by field_name. To reverse the order, add a - (minus sign) in front of the field_name

Paging, filtering, ordering all can be combined in a request. All of them can be overridden for custom behavior.

middleware

you can add middleware that runs before and after the request to your resources.

Validators

using the same router and Foo model as above, you could so something like this


defmodule MyCoolApp.FooValidator do
  use Finch.Middleware.ModelValidator

  def validate_field(_, :title, val) do
    if val == "can you" do
      throw {:bad_request, %{:errors => %{:title => "can you not"}}}
    end
    {:title, val}
  end

  def validate_field(verb, name, val), do: super(verb, name, val)

end


defmodule MyCoolApp.Resources.Foo do

  def repo, do: MyCoolApp.Repo
  def model, do: MyCoolApp.Models.Foo

  use Finch.Resource, [
    before: [MyCoolApp.FooValidator]
  ]
end

POSTing the following json

{
  "title" : 1,
  "text": "some text"
}

to the /api/v1/foo endpoint would result in a 400 BadRequest with the following message

{
  "errors" : {
    "title": "This needs to be a string"
  }
}
custom validation

you can also implement custom validation on fields. the validate_field/3 function gets run for each field. in this case posting the following

{
  "title" : "can you",
  "text": "some text"
}

to /api/v1/foo would result in a 400 BadRequest with the following message

{
  "errors" : {
    "title": "can you not"
  }
}