finch

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...

methodrouteresult
GET/api/v1/foolist all the foos
POST/api/v1/foomake 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"
}
}