Skip to content

inaka/cowboy-trails

Folders and files

NameName
Last commit message
Last commit date
Jun 7, 2024
Aug 25, 2023
Aug 25, 2023
Aug 25, 2023
Jan 6, 2016
Aug 6, 2023
Jul 28, 2021
Aug 6, 2023
Aug 6, 2023
Aug 6, 2023
Aug 23, 2023
Aug 6, 2023

Repository files navigation

cowboy-trails

build

Cowboy routes on steroids!

Contact Us

If you find any bugs or have a problem while using this library, please open an issue in this repo (or a pull request 😄).

And you can check out all of our open-source projects at inaka.github.io.

Why Cowboy Trails?

Cowboy-Trails enables you to:

  • add information to cowboy routes, which can later be used to interact with the server in a higher abstraction level,
  • define the server routes directly within the module that implements them.

How to Use it?

The most common use case for cowboy_trails is to compile cowboy routes.

Normally with cowboy you compile routes in the following way:

Routes = [{'_',
           [ {"/resource1", resource1_handler, []}
           , {"/resource2/[:id]", resource2_handler, []}
           ]
          }
         ],
cowboy_router:compile(Routes),

Trails is fully compatible with cowboy routes, so you can pass the same routes in order to be processed by Trails:

trails:compile(Routes),

So far it seems like there's no difference, right? But most of the time, with cowboy, you usually work with only a single host, but you're required to keep defining the host parameter within the routes ([{'_', [...]}]).

Well, with Trails you have another useful function to compile single host routes:

%% You only define the routes/paths
Routes = [ {"/resource1", resource1_handler, []}
         , {"/resource2/[:id]", resource2_handler, []}
         ],
trails:single_host_compile(Routes),

Now, let's suppose you want to add metadata to cowboy routes related with the semantics of each HTTP method.

You'd do something like:

Metadata = #{put => #{description => "PUT method"},
             post => #{description => "POST method"},
             get => #{description => "GET method"}},
Trail = trails:trail("/",
                     cowboy_static,
                     {private_file, "index2.html"},
                     Metadata,
                     []),
%% You can later retrieve the metadata:
Metadata = trails:metadata(Trail),

This can then be used to generate documentation related to each endpoint.

Also, when you work with cowboy, you have to define all routes in one place:

Routes =
  [{'_',
    [ {"/", cowboy_static, {file, "www/index.html"}}
    , {"/favicon.ico", cowboy_static, {file, "www/assets/favicon.ico"}}
    , {"/assets/[...]", cowboy_static, {dir, "www/assets"}}
    , {"/game/:game_id", cowboy_static, {file, "www/game.html"}}
    , {"/api/status", spts_status_handler,  []}
    , {"/api/games", spts_games_handler, []}
    , {"/api/games/:game_id", spts_single_game_handler, []}
    , {"/api/games/:game_id/serpents", spts_serpents_handler, []}
    , { "/api/games/:game_id/serpents/:token"
      , spts_single_serpent_handler, []
      }
    , {"/api/games/:game_id/news", lasse_handler, [spts_news_handler]}
    ]
   }
  ],
Dispatch = cowboy_router:compile(Routes),

But now, with trails, you're able to define the routes on each of your resource handlers, separately. These handlers must implement callback c:trails_handler:trails/0 or c:trails_handler:trails/1 and return the specific routes that define them. For a better understanding, you can check out the examples in the test folder (trails_test_handler).

Once you have implemented the c:trails_handler:trails/0 or c:trails_handler:trails/1 callback on your handlers, you can do something like this:

Handlers =
  [ spts_status_handler
  , spts_games_handler
  , spts_single_game_handler
  , spts_serpents_handler
  , spts_single_serpent_handler
  , spts_news_handler
  , {support_params_handler, #{key => value}}
  ],
Trails =
  [ {"/", cowboy_static, {file, "www/index.html"}}
  , {"/favicon.ico", cowboy_static, {file, "www/assets/favicon.ico"}}
  , {"/assets/[...]", cowboy_static, {dir, "www/assets"}}
  , {"/game/:game_id", cowboy_static, {file, "www/game.html"}}
  | trails:trails(Handlers)
  ],
trails:single_host_compile(Trails),

This way each handler maintains their own routes, as it should be, and you can merge them easily.

Example

For more information about cowboy_trails, how to use it and the different functions that it exposes, please check this example.