inaka/cowboy-trails

A couple of improvements over Cowboy Routes

Erlang

73

149 commits

updated Jun 10, 2024

See the code

README

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.

cowboy
erlang
hacktoberfest
trails

Contributors

elbrujohalcon

34 commits

cabol

20 commits

harenson

13 commits

inaka/cowboy-trails

A couple of improvements over Cowboy Routes

Erlang

73

149 commits

updated Jun 10, 2024

See the code

README

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.

cowboy
erlang
hacktoberfest
trails

Contributors

elbrujohalcon

34 commits

cabol

20 commits

harenson

13 commits

Languages

Erlang

99.7%