The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Mojolicious::Plugin::Restify - Route shortcuts & helpers for REST collections

SYNOPSIS

# Mojolicious example (Mojolicious::Lite isn't supported)
package MyApp;
use Mojo::Base 'Mojolicious';

sub startup {
  my $self = shift;

  # imports the `collection' route shortcut and `restify' helpers
  $self->plugin('Restify');

  # add REST collection endpoints manually
  my $r = $self->routes;
  my $accounts = $r->collection('accounts');      # /accounts
  $accounts->collection('invoices');              # /accounts/:accounts_id/invoices

  # or add the equivalent REST routes using the restify helper
  # my $r = $self->routes;
  # $self->restify->routes($r, {accounts => {invoices => undef}});
}

# Restify controller depicting the REST actions for the /accounts collection.
# (The name of the controller is the Mojo::Util::camelized version of the
# collection path.)
package MyApp::Controller::Accounts;
use Mojo::Base 'Mojolicious::Controller';

sub under {
  my $c = shift;

  # To consistenly get the element's ID relative to the under action, use the
  # helper as shown below. If you need to access an element ID from a
  # collection further up the chain, you can access it from the stash.
  #
  # The naming convention is the name of the collection appended with '_id'.
  # E.g., $c->stash('accounts_id').
  my $account = lookup_account_resource($c->restify->current_id);

  # By stashing the $account here, it will now be available in the delete,
  # read, patch, and update actions. This under action is added to every
  # collection by default to help reduce your code, but can be disabled if
  # you wish.
  $c->stash(account => $account);

  # must return a positive value to continue the dispatch chain
  return 1 if $account;

  # inform the end user that this specific resource does not exist
  $c->reply->not_found and return 0;
}

sub create { ... }

sub delete { ... }

sub list { ... }

sub read {
  my $c = shift;

  # account was placed in the stash in the under method
  $c->render(json => $c->stash('account'));
}

sub patch { ... }

sub update { ... }

1;

DESCRIPTION

Mojolicious::Plugin::Restify is a Mojolicious::Plugin. It simplifies generating all of the Mojolicious::Routes for a typical REST collection endpoint (e.g., /accounts or /invoices) and maps the common HTTP verbs (DELETE, GET, PATCH, POST, PUT) to underlying controller class methods.

For example, creating a collection called /accounts would create the routes as shown below. N.B. The over option in the example below corresponds to the name of a route condition. See "conditions" in Mojolicious::Routes.

# The collection route shortcut below creates the following routes, and maps
# them to controllers of the camelized route's name.
#
# /accounts           *         accounts
#   +/                GET       "accounts_list"       Accounts::list
#   +/                POST      "accounts_create"     Accounts::create
#   +/:accounts_id    *         "accounts"
#     +/              *         "accounts_under"      Accounts::under
#       +/            DELETE    "accounts_delete"     Accounts::delete
#       +/            GET       "accounts_read"       Accounts::read
#       +/            PATCH     "accounts_patch"      Accounts::patch
#       +/            PUT       "accounts_update"     Accounts::update

# expects the element id (:accounts_id) for this collection to be a uuid
my $route = $r->collection('accounts', over => 'uuid');

Mojolicious::Plugin::Restify tries not to make too many assumptions, but the author's recent experience writing a REST-based API using Mojolicious has helped shaped this plugin, and might unwittingly express some of his bias.

HELPERS

Mojolicious::Plugin::Restify implements the following helpers.

restify->current_id

my $id = $c->restify->current_id;

Returns the element id at the current point in the dispatch chain.

This is the only way to guarantee the correct element's resource ID in a Mojolicious::Plugin::Restify action. The under action which is added by default in both "collection" and "restify-routes" is added at different positions of the dispatch chain. As such, the router might not have added the value of any placeholders to the Mojolicious::Controller::stash yet.

restify->routes

This helper allows you to create REST collections from a Perl HASH. It uses the key/values to invoke the "collection" route shortcut with any route- specific options. It automatically chains routes to each parent, and progressively builds a namespace as it traverses through every key.

See "collection" for more route-specific options.

my $restify_routes = {
  # /area-codes
  #   /area-codes/:area_codes_id/numbers
  'area-codes' => {
    'numbers' => undef
  },
  # /news
  'news' => undef,
  # /payments
  'payments' => [undef, {over => 'int'}],   # overrides default uuid route condition set below
  # /users
  #   /users/:users_id/messages
  #     /users/:users_id/messages/:messages_id/recipients
  'users' => {
    'messages' => {
      'recipients' => undef
    }
  },
};

$self->restify->routes($self->routes, $restify_routes, {over => 'uuid'});

METHODS

Mojolicious::Plugin::Restify inherits all methods from Mojolicious::Plugin and implements the following new ones.

register

$plugin->register(Mojolicious->new);

Register plugin in Mojolicious application.

ROUTE CONDITIONS

Mojolicious::Plugin::Restify implements the following route conditions. These conditions can be used with the over option in the "collection" shortcut.

Checks are made for the existence of the int, standard and uuid conditions before adding them. This allows you to replace them with your own conditions of the same name by creating them before registering this plugin.

See "Adding-conditions" in Mojolicious::Guides::Routing to add your own.

int

# /numbers/1        # GOOD
# /numbers/0        # GOOD
# /numbers/one      # BAD
# /numbers/-1       # BAD
# /numbers/0.114    # BAD (the standard :placeholder notation doesn't allow a '.')

my $r = $self->routes;
$r->collection('numbers', over => 'int');

A Mojolicious route condition (see "conditions" in Mojolicious::Routes) which restricts a route's collection's element id to whole positive integers which are >= 0.

standard

my $r = $self->routes;
$r->collection('numbers', over => 'standard');

A collection's element resource ID is captured using "Standard-placeholders" in Mojolicious::Guides::Routing. This route condition allows everything the standard placeholder allows, which is similar to the regular expression ([^/.]+).

This is the default over option for a "collection".

uuid

# /uuids/8ebef0d0-d6cf-11e4-8830-0800200c9a66     GOOD
# /uuids/8EBEF0D0-D6CF-11E4-8830-0800200C9A66     GOOD
# /uuids/8ebef0d0d6cf11e488300800200c9a66         GOOD
# /uuids/malformed-uuid                           BAD

my $r = $self->routes;
$r->collection('uuids', over => 'uuid');

A Mojolicious route condition (see "conditions" in Mojolicious::Routes) which restricts a route's collection's element id to UUIDs only (with or without the separating hyphens).

ROUTE SHORTCUTS

Mojolicious::Plugin::Restify implements the following route shortcuts.

collection

my $r = $self->routes;
$r->collection('accounts');

A Mojolicious routes shortcut which helps create the most common REST routes for a collection endpoint and its associated element.

A collection endpoint (e.g., /accounts) supports list (GET) and create (POST) actions. The collection's element (e.g., /accounts/:accounts_id) supports delete (DELETE), read (GET), patch (PATCH), and update (PUT) actions.

By default, every HTTP request to a collection's element is routed through an under action (see "under" in Mojolicious::Routes::Route). This helps reduce the process of looking up a resource to a single location. See "SYNOPSIS" for an example of its use.

options

The following options allow a collection to be fine-tuned.

controller
# collection doesn't build a namespace for subroutes by default
my $accounts = $r->collection('accounts');    # MyApp::Controller::Accounts
$accounts->collection('invoices');            # MyApp::Controller::Invoices

# collection can build namespaces, but can be difficult to keep track of. Use
# the restify helper if namespaces are important to you.
#
# MyApp::Controller::Accounts
my $accounts = $r->collection('accounts');
# MyApp::Controller::Accounts::Invoices
my $invoices = $accounts->collection('invoices', controller => 'accounts');
# MyApp::Controller::Accounts::Invoices::Foo
$invoices->collection('foo', controller => 'accounts-invoices');

Prepends the controller name (which is automatically generated based on the path name) with this option value if present. Used internally by "restify" to build a perlish namespace from the paths. "collection" does not build a namespace by default.

element
# GET,POST                      /messages     200
# DELETE,GET,PATCH,PUT,UPDATE   /messages/1   200
$r->collection('messages');     # element routes are created by default

# GET,POST                      /messages     200
# DELETE,GET,PATCH,PUT,UPDATE   /messages/1   404
$r->collection('messages', element => 0);

Enables or disables adding an element to the collection. Disabling the element portion of a collection means that only the create and list actions will be created.

over
$r->collection('accounts', over => 'uuid');
$r->collection('invoices', over => 'int');

Allows a collection's element to be restricted to a specific data type using Mojolicious' route conditions. "int", "standard" and "uuid" are added automatically if they don't already exist.

prefix
# without a prefix
$r->collection('invoices');
say $c->url_for('invoices', invoices_id => 1);

# with a prefix
$r->collection('invoices', prefix => 'v1');
say $c->url_for('v1_invoices', invoices_id => 1);

Adds a prefix to the automatically generated route name for each collection and element action.

under
$r->collection('nounder', under => 0);

Enables or disables adding an under action to the element of the collection.

element

my $r    = $self->routes;
my $news = $r->get('/news')->to('foo#news');
$news->element('news');

A Mojolicious route shortcut called internally by "collection" to add the element routes to a collection. You shouldn't need to call this shortcut directly.

When an element is added to a collection's route, the resource ID is captured using a standard placeholder.

CREDITS

In alphabetical order:

    Dragoș-Robert Neagu

COPYRIGHT AND LICENSE

Copyright (C) 2015-2016, Paul Williams.

This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version 2.0.

AUTHOR

Paul Williams <kwakwa@cpan.org>

SEE ALSO

Mojolicious, Mojolicious::Plugin::REST, Mojolicious::Plugin::RESTRoutes.