Skip to content

Instantly share code, notes, and snippets.

@alanpeabody
Last active August 29, 2015 14:23
Show Gist options
  • Save alanpeabody/1bb95b26b93ed8bee0f3 to your computer and use it in GitHub Desktop.
Save alanpeabody/1bb95b26b93ed8bee0f3 to your computer and use it in GitHub Desktop.
Relax DSL RFC

Relax DSL expansion.

Serialization has been moved to the JaSerializer library with the intent of improving the routing/resource DSL to provide more out of the box functionality.

Goals

  • Super easy to create simple jsonapi.org compliant RESTful APIs.
  • Standard Ecto models need little to no "wiring" to get working.
  • Improved DSL for working with non Ecto models (eg: Mnesia, ETS, Riak, etc)
  • Complete ability to override anything as needed.
  • Attribute and relationship filtering/normalization
# Proposal: Better Routing.
# One goal here is to reduce dependence on Plug.Router
defmodule Router do
use Relax.Router
use Plug.Router
plug :api_router
#for custom routes
plug :match
plug :dispatch
namespace :api do
version :v1 do
resource :posts, Resources.V1.Posts do
resource :comments, Resources.V1.Comments
resource :author, Resources.V1.Author
end
resource :comments, Resources.V1.Comments
end
end
end
# Proposal: EctoResource w/ plugs.
# Common options are defined as DSL, each 'action' is defined as a plug.
# Standard Ecto finding/creating handled for you.
defmodule Resources.V1.Post do
use Relax.EctoResource
model Models.Post
repo Repo
serializer Serializers.V1.Post
plug :index_resource, filters: [:year, :tag]
plug :show_resource
plug :update_resource, allow: [:title, :body, :tags]
plug :create_resource, allow: [:title, :body, :slug, :tags]
# To dispatch to custom routes we fall back to Plug.Router (included for us)
plug :match
plug :dispatch
# To customize/override what is returned
def index(params, conn) do
Models.Post
|> Ecto.Query.where([p], p.posted == true)
end
# To filter index action
def filter(:tag, queryable, filter_value) do
queryable
|> Ecto.Query.where([p], ^filter_value in p.tags)
end
# Custom route
put ":id/publish" do
# publish post
end
match _ do
send_resp(conn, 404, "nope")
end
end
# Proposal: Resource (not persistance specific).
# Common options are defined as DSL, each 'action' is defined as a plug.
# Assumes you will provide functions that find, create, or update then return your models.
defmodule Resources.V1.Post do
use Relax.Resource
serializer Serializers.V1.Post
plug :index_resource
plug :show_resource
plug :update_resource, only: [:title, :body]
plug :create_resource, only: [:title, :body, :slug]
# For custom routes
plug :match
plug :dispatch
# responds w/ serialized object or 404 if show returns nil
def show(_conn, id) do
Models.Post.find(id)
end
def index(_conn) do
Repo.all(Models.Post)
end
# TODO, how to handle relationships?
def create(_conn, attributes) do
# Attributes pre-filtered based on arguments passed in above.
Model.Post.create(attributes)
end
# Renders 422 with errors or 200 with object depending on if update works.
def update(_conn, id, attributes) do
Models.Post.find(id)
|> Models.Post.update(attributes)
end
put ":id/publish" do
# publish post
end
match _ do
send_resp(conn, 404, "nope")
end
end
@nurugger07
Copy link

Have you thought about adding scopes? I like to scope routes using something like default_scope and scope so you could have:

defmodule Resources.V1.Post do
  use Relax.Resource

  default_scope :api # or "api"
  version :v1 # or "v1"

  # Other stuff here :)

 scope "/posts" do
   put ":id/publish" do
     # publish post 
    end
  end
end

Urls would come out like: "/api/v1/posts/:id/publish"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment