• Stars
    star
    189
  • Rank 197,994 (Top 5 %)
  • Language
    Elixir
  • License
    MIT License
  • Created over 9 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A REST toolkit for building highly-scalable and fault-tolerant HTTP APIs with Elixir

Placid

Build Status Coverage Status Hex.pm Version

A REST toolkit for building highly-scalable and fault-tolerant HTTP APIs with Elixir.

Configuration

HTTP

Options

  • port - Port to listen for HTTP requests.

HTTPS

By default, connecting to a Placid-based API will require all requests to be made over HTTPS, responding to HTTP requests with a 403 Forbidden. If desired, the https_only option may be set allow HTTP requests to be served by your application. Take a look at the kitchen sink example to see this in action.

Options

  • certfile - Path to the certificate file.
  • keyfile - Path to the certificate key file.
  • otp_app - If present, certfile and keyfile can be relative paths with respect to otp_app's priv directory.
  • port - Port to listen for HTTPS requests.

Note: Create a self-signed certificate for easy testing.

# Generate a keyfile
$ openssl genrsa -out key.pem 2048

# Create a CSR
$ openssl req -new -key key.pem -out request.pem

# Generate a certfile that expires in $NUM_DAYS
$ openssl x509 -req -days $NUM_DAYS -in request.pem -signkey key.pem -out cert.pem

Routing

defmodule Router do
  use Placid.Router

  # Define one of the versions of the API
  # with a simple version number "1"
  # or following semver "1.0.0"
  # or date of release "2014-09-06"
  version "1" do
    # Define your routes here
    get  "/",               Handlers.V1.Pages, :index
    get  "/pages",          Handlers.V1.Pages, :create
    post "/pages",          Handlers.V1.Pages, :create
    put  "/pages/:page_id" when id == 1,
                            Handlers.V1.Pages, :update_only_one
    get  "/pages/:page_id", Handlers.V1.Pages, :show

    # Auto-create a full set of routes for resources
    #
    resource :users,        Handlers.V1.User, arg: :user_id
    #
    # Generates:
    #
    # get     "/users",           Handlers.V1.User, :index
    # post    "/users",           Handlers.V1.User, :create
    # get     "/users/:user_id",  Handlers.V1.User, :show
    # put     "/users/:user_id",  Handlers.V1.User, :update
    # patch   "/users/:user_id",  Handlers.V1.User, :patch
    # delete  "/users/:user_id",  Handlers.V1.User, :delete
    #
    # options "/users",           "HEAD,GET,POST"
    # options "/users/:_user_id", "HEAD,GET,PUT,PATCH,DELETE"
  end

  # An updated version of the AP
  version "2" do
    get  "/",               Handlers.V2.Pages,  :index
    post "/pages",          Handlers.V2.Pages,  :create
    get  "/pages/:page_id", Handlers.V2.Pages,  :show
    put  "/pages/:page_id", Handlers.V2.Pages,  :update

    raw :trace, "/trace",   Handlers.V2.Tracer, :trace

    resource :users,        Handlers.V2.User
    resource :groups,       Handlers.V2.Group
  end
end

get/3, post/3, put/3, patch/3, delete/3, options/2, and any/3 are already built-in as described. resource exists but will need modifications to create everything as noted.

raw/4 allows for using custom HTTP methods, allowing your application to be HTTP spec compliant.

version/2 will need to be created outright. Will allow requests to contained endpoints when version exists in either Accepts header or URL (which ever is defined in app config).

Extra routes will need to be added for *.json, *.xml, etc. requests for optionally specifying desired content type without the use of the Accepts header. These should match parsing/rendering abilities of Placid.

Should required/optional params be gathered for matching purposes? Only return a matched route when all required params are present?

Handlers

defmodule Handlers.V2.Pages do
  use Placid.Handler

  @doc """
  List all available pages
  """
  def index(conn, []) do
    # Somehow get our content
    pages = Queries.Page.all
    render conn, pages
  end

  @doc """
  Show an individual page
  """
  def show(conn, args) do
    result = case Integer.parse args["page_id"] do
        :error ->
          %Error{ id: "no_page_id",
                  message: "A valid page_id is required." }
        {i, _} ->
          Queries.Page.get i
      end

    render conn, result
  end

  @doc """
  Create a new page
  """
  def create(conn, args) do
    render conn, Queries.Page.create(args), status: :created
  end

  @doc """
  Update an individual page
  """
  def update(conn, args) do
    result = case Integer.parse args["page_id"] do
        :error ->
          %Error{ id: "no_page_id",
                  message: "A valid page_id is requried." }
        {i, _} ->
          Queries.Page.update i, args
      end

    render conn, result
  end
end

Actions in handler modules are responsible for handling a request once it has been routed. These actions typically generate a response, whether that be an error, a result, or a result set, so that it can be rendered to the client with the correct content type further up the stack.

CORS

Should have an option to respect Cross-origin resource sharing (CORS) when desired.

Main response headers:

  • Access-Control-Allow-Origin: *|[list of allowed hosts]
  • Access-Control-Allow-Credentials: true (or ignore header)
  • Access-Control-Allow-Methods: [list of allowed methods]
  • Access-Control-Allow-Headers: [list of allowed beyond simple]
    • Cache-Control
    • Content-Language
    • Content-Type
    • Expires
    • Last-Modified
    • Pragma
  • Access-Control-Max-Age: [# of seconds]

Should check over the HTML5 Rocks CORS flowchart as much as possible.

Is JSON-P still a thing? Should it be supported? What happens with non-GET requests?

Request Parsing

Parsing request bodies from their content type to Elixir terms allows the handler actions to easily use that data in responding to the client. There should be one parser for each supported response content type, with an additional parser for form encoded data.

Current Parsers:

  • JSON - Encoded into standard map
  • XML - Encoded into a list of maps, each containing a representation of XML nodes from the request body
  • WWW-Encoded and Multipart form data - Encoded into a standard map

Rendering

Render layer serializes/encodes data based on the requested content type unless overridden for whatever reason in the response stack.

Rendering engine behavior:

defmodule Placid.Response.Rendering.Engine do
  use Behaviour

  @type data :: Keyword | Map | List

  defcallback serialize(data, type, subtype) :: { :ok, binary } | :next
end

defmodule Placid.Response.Rendering.JSON do
  @behaviour Placid.Response.Rendering.Engine

  @types ["application", "text"]

  def serialize(data, type, "json") when type in @types do
    { :ok, data |> Poison.encode!(string: true) }
  end
  def serialize(_, _, _), do: :next
end

Internationalization

I18n should always be considered when producing an API.

Linguist is already a part of the project's dependencies. Need to think of ways to make translations seemless as possible, similar to rendering.

TODO

  • Respects HTTP specifications (7230, 7231, 7232, 7233, 7234, 7235)
  • Compatibility with web frameworks via umbrella projects.
    • Would be nice to offer tight integration when available, e.g. Phoenix.Topic notifications
  • Foundations
    • Prefer TLS. Require clients to use TLS when enabled in server
    • Version with Accept header. Fallback to URL versioning
    • Support caching with Etags
    • Trace requests with Request-Ids
    • Paginate with ranges. Mostly lies on end-developer, but we should provide facility to easily set headers
  • Requests
    • Return appropriate status codes. Mostly lies on end-developer, but we should return them when appropriate
    • Provide full resources where available
    • Accept serialized JSON/XML in request bodies
    • Downcase paths and attributes
    • Support non-id dereferencing for convenience. No type checking occurs on parameters
    • Minimize path nesting. Mostly lies on end-developer, but we should ensure generated route paths are as simple as posisble
  • Responses
    • Provide resource (UU)IDs
    • Provide standard timestamps
    • Use UTC times formatted in ISO8601
    • Nest foreign key relations
    • Generate structured errors
    • Show rate limit status
    • Keep JSON minified in all responses
  • Artifacts
    • Provide machine-readable JSON schema
    • Provide human-readable docs
    • Provide executable examples
    • Describe stability

This list comes primarily from the HTTP API Design Guide by @interagent and friends but will be updated to fit the needs of the project.

License

Placid is released under the MIT License.

See LICENSE for details.

More Repositories

1

elixir-reverse-proxy

A Plug based, reverse proxy server written in Elixir.
Elixir
145
star
2

vagrant-phalcon

a template for using Vagrant for developing PHP applications with Phalcon
Ruby
73
star
3

plug-web-socket

An exploration into a stand-alone library for Plug applications to easily adopt WebSockets.
Elixir
61
star
4

mandrill-elixir

a Mandrill wrapper for Elixir
Elixir
51
star
5

elixir-control

An exploratory look into functors, applicatives, and monads for Elixir
Elixir
24
star
6

parcel-plugin-fable

Parcel asset type plugin for Fable
JavaScript
21
star
7

web-development-using-elixir

17
star
8

acvte

a blog written in go, based off of obtvse2 / svbtle
JavaScript
15
star
9

stripe-elixir

Elixir
15
star
10

php-web-component-ssr

Provides server-side rendering support for native web components / custom elements
PHP
15
star
11

b

A static-ish blog application. Can be run as a standalone applicaiton or be used to manage and deploy posts to a remote server.
Go
14
star
12

http

HTTP server for Elixir
Elixir
12
star
13

Fable.Template.JavaScriptLibrary

dotnet new template for building JavaScript projects with Fable + FSharp
F#
9
star
14

staticwp

Converts your WordPress blog into a static site
PHP
8
star
15

vagrant-weber

a template for using Vagrant for developing Elixir applications with Weber
Shell
7
star
16

sap

A toolkit for Plug applications to accept and respond to HTTP requests by using a decision tree built with combinators
Elixir
7
star
17

nuxt-demo

JavaScript
6
star
18

elixir_playground

Elixir
6
star
19

javascript-web-component-hydration

Thin wrapper around HTMLElement to support hydration of server-side rendered custom elements
JavaScript
6
star
20

pool

Socket acceptor pool for Elixir
Elixir
6
star
21

elixir-raft

A Raft implementation
Elixir
5
star
22

elixir-nicta-course

Functional Programming Course for Elixir, modeled after the NICTA course for Haskell
Elixir
5
star
23

slate-for-jekyll

Beautiful static documentation for your API
JavaScript
5
star
24

jsx-for-web-components

WIP: A basic JSX factory for use in projects leveraging web components
TypeScript
4
star
25

presentation-functional-programming-in-elixir

Elixir
4
star
26

dotfiles

Perl
3
star
27

develop-on-docker

A set of Docker images to aid development in containers
3
star
28

webapi-angular2-demo

A demo for using ASP.NET 5, WebAPI, and Angular 2 in harmony
C#
3
star
29

php-flat-file

Fast static-site generator / flat-file CMS
PHP
3
star
30

IntroToJavascript

A primer into programming in Javascript
2
star
31

EncodingStatus

Batch FFmpeg administration
PHP
2
star
32

elixir-hex-deps-checker

Check your Hex dependencies for updates
Elixir
2
star
33

reasonably-reactive-rust

Demo/starter project using Reason + React and Rust + Rocket
OCaml
2
star
34

chicagoboss_angular

A ChicagoBoss template with Brunch
Erlang
1
star
35

docker-build-service

Provides a build service for code using docker containers
Go
1
star
36

php-fp-web

test bed for a functional PHP web toolkit
PHP
1
star
37

presentation-plug-friend-of-web-developers

Elixir
1
star
38

sugarcookie

Generate and verify value signatures for secure cookies.
Go
1
star
39

bsb-native-starter

Quick/simple starter project for bsb-native and Reason/OCaml
OCaml
1
star
40

gp-node-sdk

JavaScript
1
star
41

editor

Elixir
1
star
42

language-web-shootout

Web shootout between some recently used languages
Clojure
1
star
43

now-static-demo

A minimal example for using now.sh for deploying static sites
HTML
1
star
44

globalpayments-gateway-provider-for-woocommerce

PHP
1
star
45

php-fluent-interfaces

some ways to create Fluent Interfaces in PHP
PHP
1
star
46

rust-wasm-demo

Demo/starter project using Rust and WebAssembly
Rust
1
star
47

global-elixir

Elixir
1
star
48

php-flat-file-starter

Starter project for flat-file
Dockerfile
1
star
49

test-the-planet

Code for an Elixir workshop covering testing with ExUnit.
Elixir
1
star