GithubHelp home page GithubHelp logo

trot's Introduction

trot

Hex.pm Build Status Inline docs

Trot is an Elixir web micro-framework based on Plug and Cowboy. The goal of Trot is to make common patterns in Plug easier to use, particularly when writing APIs, without sacrificing flexibility.

Usage

Add Trot as a dependency to your mix.exs file:

    defp deps do
      [{:trot, github: "hexedpackets/trot"}]
    end

The following configuration options are supported by the server:

config :trot, :port, 4000: port to listen on for incoming HTTP requests. Defaults to "4000".

config :trot, :router, MyApp.Router: module to route requests to. Defaults to "Trot.NotFound".

config :trot, :heartbeat, "/heartbeat": path to setup a heartbeat route. This will always return 200 with a body of "OK". Defaults to "/heartbeat". NOTE: This value will only have an effect when PlugHeartbeat is part of the plug list.

config :trot, :pre_routing, ["Elixir.CustomPlug": [plug_arg: value]]: Plugs that should be run before routing a request along with their arguments. Defaults to setting up "Trot.LiveReload", "Plug.Logger", "Plug.Parsers", and "PlugHeartbeat" in that order.

config :trot, :post_routing, ["Elixir.CustomPlug": [plug_arg: value]]: Plugs that should be run after routing a request along with their arguments. Defaults to "[]".

Finally, put use Trot.Router to the top of your module. This will add route macros and setup the plug pipeline at compile time.

Getting started

To get a basic devserver up and running, make sure you add a Router module in the config as described above, and then simply

$ mix trot.server

Note: You can also start the server as well as an iex shell by running iex -S mix

Routes

Routes are specified using one of the HTTP method macros: get/3, post/3, put/3, patch/3, delete/3, options/3. The first argument is a the path to route to, the second (optional) argument is a keyword list of any options to match against, and the last argument is the block of code to execute. Examples are below.

If @path_root is specified, it will be prefixed to all routes in that module.

Routes can be setup in different modules and imported into the main router with the import_routes/1 macro, which takes a module name as the only argument. Note that ordering matters as normal Elixir pattern matching rules apply to imported routes.

A default 404 response can be enabled by putting import_routes Trot.NotFound or use Trot.NotFound at the end of the module.

Responses

All of the following are valid return values from handlers and will be parsed into full HTTP responses:

  • String of response body
  • Status code, either numeric or an atom from Plug.Conn.Status
  • {code, body}
  • {code, body, headers}
  • JSONable object
  • {code, object}
  • {code, object, headers}
  • {:redirect, location}
  • {:badrpc, error}
  • %Plug.Conn{}

Example router application

    defmodule SoLoMoApp.Router do
      use Trot.Router

      # Setup a static route to priv/static/assets
      static "/css", "assets"

      # Returns an empty body with a status code of 400
      get "/bad" do
        :bad_request
      end

      # Sets the status code to 200 with a text body
      get "/text" do
        "Thank you for your question."
      end

      # Redirect the incoming request
      get "/text/body", headers: ["x-text-type": "question"] do
        {:redirect, "/text"}
      end

      # Sets the status code to 201 with a text body
      get "/text/body" do
        {201, "optimal tip-to-tip efficiency"}
      end

      # Sets status code to 200 with a JSON-encoded body
      get "/json" do
        %{"hyper" => "social"}
      end

      # Pattern match part of the path into a variable
      get "/presenter/:name" do
        "The presenter is #{name}"
      end

      import_routes Trot.NotFound
    end

Templating

To add templating in a router, add use Trot.Template and set @template_root to the top-level directory containing your templates. By default, @template_root is "priv/templates/".

Trot can be used to render EEx templates (the default engine include with Elixir), HAML templates through Calliope, or a combination of both. When the application is compiled a render_template/2 function is generated for every template under @template_root. render_template/2 expects the name of the template relative to @template_root as the first argument and a keyword list of variables to assign as the second argument.

When MIX_ENV=prod all of templates are loaded and pre-compiled for faster rendering.

Example app using templates

    defmodule PiedPiper do
      use Trot.Router
      use Trot.Template
      @template_root "priv/templates/root"

      get "/compression/pied_piper" do
        render_template("compression_results.html.eex", [weissman_score: 5.2])
      end

      get "/compression/nucleus" do
        render_template("compression_results.html.haml", [weissman_score: 2.89])
      end
    end

    # compression_results.html.eex
    <html><body>Pied piper has a Weissman Score of <%= @weissman_score %></body></html>

    # compression_results.html.haml
    %html
      %body Nucleaus has a Weissman Score of <%= @weissman_score %>

Additional plugs

The plug/2 macro is available within a Trot router, allowing any plug to be inserted into the pipeline. Anything after Trot.Router will likely have a closed connection, so most uses cases will involve pulling in Plug.Builder first.

Example plug before Trot

    defmodule SoLoMoApp.Router do
      use Plug.Builder
      plug Plug.RequestId

      use Trot.Router
      get "/hello", do: "hello"
    end

API versioning

Adding use Trot.Versioning to your module will enable API version parsing and pattern matching. The first part of the path for all requests in the module is assumed to be the version. It is parsed into the conn[:assigns] dictionary, making it easy to access. Routes can also be configured to only match a particular version.

Example versioned app

    defmodule Nucleus do
      use Trot.Router
      use Trot.Versioning

      get "/version" do
        conn.assigns[:version]
      end

      get "/current", version: "v1" do
        :ok
      end

      get "/current" do
        :bad_request
      end
    end

In the above example, "/v1/version" will return "v1" as the response body. A request to "/v1/current" will return a 200 but "/v2/current" will return a 400.

trot's People

Contributors

ciaran avatar erik avatar hexedpackets avatar kazucocoa avatar kyuden avatar ma2gedev avatar robconery avatar rrrene avatar wkhere avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

trot's Issues

Add documentation for local devserver

Hi,
I'm starting to use trot for a side project. Love the concept.

I setup my project with everything, and found that the docs were missing a "getting started" aspect. Right now I'm running my local devserver by simply running

iex -S mix

Is this the best way? If so I'm happy to make a PR to improve the README

Implement graceful shutdown

How can I implement graceful shutdown of the server?

I would like to capture SIGINT and SIGHUP signals and then I would like to stop listening for new connections. I would still like to process pending connections and after some TIMEOUT I would like to kill the server even if there are still some pending connections.

Can you help me in this matter?

MatchError occurred when use plug 0.13.0

Hi,

When I tried https://github.com/hexedpackets/trot_example/ with iex -S mix then the following error occurred.

== Compilation error on file lib/trot_example/router/templates.ex ==
** (MatchError) no match of right hand side value: {"GET", ["compression", "pied_piper"], {:_, [], Plug.Router.Utils}, true, nil}
    lib/trot_example/router/templates.ex:5: (module)
    (stdlib) erl_eval.erl:657: :erl_eval.do_apply/6

I only changed version of plug in mix.lock to 0.13.0 which is latest version of plug .
https://github.com/hexedpackets/trot_example/blob/master/mix.lock#L4

Have you known this issue?

If with 0.12.2, trot works well :)

Please provide an example for configuring Websockets

I have seen examples that demonstrate that cowboy support Websockets, but it's not clear to me how to configure them using trot, or if this is even possible. If not, does it make sense to try to configure normal http routes using trot and set Websockets up directly with cowboy, or should I just user the normal cowboy route configuration for everything?

Don't use Application.gev_env

I need to run multiple Trot instances, and global configuration stamps on those dreams. Manually building a supervision tree works as a quick hack, but I still have to contend with pre_routing and post_routing being global.

Instead, configuration should be pulled via options passed into init/1.

Roadmap

Nice one - any plans for adding extra features for developing APIs? Things like:

  • versioning
  • REST-ful resources
  • nested routes

Update plug_heartbeat

Hey, author of plug_heartbeat here, which you're using as a dependency. Just wanted to give a heads up to update plug_heartbeat to 0.2 (since you're using ~> 0.1, a mix deps.update plug_heartbeat would be enough) because 0.1 breaks with older versions of Plug. This happens because of Plug.Conn.full_path/1, which plug_heartbeat was using and which was deprecated.

I noticed because of whatyouhide/plug_heartbeat#2.

Set correct content type for JSON responses

When returning a response with JSON as the body, Trot should set the "content-type" header to "application/json".

Right now it sets no content type and hence each request handler which returns an object must set the header too. This is cumbersome and there is no reason to do this. Also note that this functionality cannot be implemented as a post routing plug since the response is closed post routing and hence is unwritable.

Start Cowboy automatically

Currently any application using Trot has to add something like Plug.Adapters.Cowboy.child_spec(:http, Module.Router, [], [port: 4000]) to the supervisor. It would be nice to have this started automatically, with the port and module specified in the user's config.

Documentation on how to fetch request body data

Documentation on the intended way on how to fetch the request body data for PUT/POST/... requests is missing.

put "/" do
    IO.puts(inspect(conn.body_params))
    ""
end

# Prints:
# %Plug.Conn.Unfetched{aspect: :body_params}

(File.Error) could not read file VERSION: no such file or directory in 0.5.1

I tried with trot 0.5.1, then the following error caused.

==> trot_example
Error while loading project :trot at /Users/myname/Documents/github/trot_example/deps/trot
** (File.Error) could not read file VERSION: no such file or directory
    (elixir) lib/file.ex:245: File.read!/1
    mix.exs:4: (module)
    (stdlib) erl_eval.erl:657: :erl_eval.do_apply/6

Example App

Is there any example app? I can't get the Cowboy to start :'(

** (Mix) Could not start application trot_icip: TrotIcip.start(:normal, []) returned an error: shutdown: failed to start child: {:ranch_listener_sup, TrotIcip.Router.HTTP} ** (EXIT) exited in: :gen_server.call(:ranch_server, {:set_new_listener_opts, TrotIcip.Router.HTTP, 1024, [env: [dispatch: [{:_, [], [{:_, [], Plug.Adapters.Cowboy.Handler, {TrotIcip.Router, []}}]}]], compress: false]}) ** (EXIT) no process

Log output after start server

Is there way to output log after start server?
I want to know that the trot server has started successfully.

Now, trot output logs when it receives some http request. But no log on console when I run mix trot.start.

Running trot from the root of an umbrella project fails

I am using trot as a small application within an umbrella project here.
With no further config in apps/http_server/config/config.exs I am able to start the httpServer from the application directory mix trot.server or mix run --no-haltand the application works fine.

If I start the application from the root of the umbrella project using mix trot.server the application starts, but incoming request fail with

16:36:19.701 [error] #PID<0.302.0> running HTTPServer.Router terminated
Server: localhost:8888 (http)
Request: GET /
** (exit) an exception was raised:
    ** (RuntimeError) Trying to access Mix.Project.app_path for an umbrella project but umbrellas have no app
        (mix) lib/mix/project.ex:332: Mix.Project.app_path/1
        (mix) lib/mix/project.ex:353: Mix.Project.compile_path/1
        (mix) lib/mix/tasks/compile.elixir.ex:53: Mix.Tasks.Compile.Elixir.run/1
        (trot) lib/trot/live_reload.ex:20: Trot.LiveReload.call/2
        (http_server) lib/router.ex:1: HTTPServer.Router.plug_builder_call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

Looking at this I tried to disable LiveReloading in the config.exs

This seems to fix the issue, but sadly loosing live reload support.

`mix trot.server` fails when running in dev environment

I have installed trot in my elixir toy application. When I run mix trot.server with a ping endpoint in production environment it works fine, however when I do the same in dev environment then it fails.

The problem seems to lie with a compile time warning that in get in a separate module in my app. This feels bonkers, why would a warning fail my trot.server. Is this by design?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.