RESTful Web Services
with Mojolicious and DBIx::Class
About me
●   Tudor Constantin


●   Perl Developer @ Evozon
●   http://programming.tudorconstantin.com/
●   http://stackoverflow.com/users/459233/tudor-constantin
●   https://github.com/tudorconstantin
●   http://www.linkedin.com/in/tudorconstantin
●   twitter: @tudorconstantin
●   gmail: tudorconstantin at gmail dot com
Content
●   Sample app overview - Expense Tracker
●   (Short) Intro to RESTful Web Services
●   DBIx::Class
●   Mojolicious
●   Routing in Mojo
●   Generic Mojo Controller for CRUD operations
●   Sample operation - update user
●   Steps for getting RESTful routes/operations for a table in DB
Sample app Expense
Tracker
VERY simple application - but usable (insert expenses, assign categories and
see reports)

●   Five tables
     ○ users
     ○ currencies
     ○ categories
     ○ operations
     ○ operations_categories

●   Relationships
     ○ 1 user has many categories
     ○ 1 user has many operations
     ○ 1 category has many sub categories
     ○ 1 category has many operations
     ○ 1 operation has many categories
     ○ 1 operation has a currency
1 operation belongs to a user
Intro to RESTful Web
Services
REST - REpresentational State Transfer
 ● concept introduced in 2000 by Roy Fielding in his academic dissertation,
    "Architectural Styles and the Design of Network-based Software Architectures"

Main ideas for REST:
● Resources are accessible through unique URLs:
     ○ /user/23
     ○ /operations
● Use HTTP methods explicitly
     ○ GET           - for Read
     ○ POST          - for Create
     ○ PUT           - for Update
     ○ DELETE        - for Delete
● Be stateless
     ○ the server does not know nor cares about the state of the client
        application
● Transfer Representations of resources (HTML, JSON, XML, etc)
DBIx::Class
●   An abstraction for working with DBs
●   More than an ORM (Object Relational Mapper) - It knows how to work on
    Result Sets
●   Components - simplified overview:
     ○ DBIx::Class::Schema - connection to DB
     ○ DBIx::Class::ResultSet - a query used for fetching a set of results
     ○ DBIx::Class::Row - objects returned from DBIx::Class::ResultSets
        using the create, find, next and all methods
●   Sample usage:
     my @rows = ExpenseTracker::Models->connect(
                          $config->{database}->{ $mode }->{dsn},
                          $config->{database}->{ $mode }->{user},
                          $config->{database}->{ $mode }->{password},
         )                                                       # DBIx::Class::Schema
       ->resultset( 'ExpenseTracker::Models::Result::User' )  # DBIx::Class::ResultSet
       ->search_rs(
           { id => 10 },
       )                                                         # DBIx::Class::ResultSet - only users
    with id = 10
       ->all();                                                  # The collection of DBIx::Class::Row
    instances
Mojolicious
●   Microframework inspired by Sinatra (Ruby)
●   Components of interest (for this app)
     ○ Router
     ○ Controller
●   Not (quite) interested in:
     ○ Views (since we render mainly json)
●   Not provided at all:
     ○ Models (we plug in and use DBIx::Class)
Routing in Mojolicious
In app context (ie - the startup routine):
my $r = $self->routes;
#sample route named 'login' for GET - executing method login from controller ExpenseTracker::Controllers::Login
$r->get('/login')->to('login#login')->name('login');



Shortcut, generic routing:
$params->{app_routes}->add_shortcut(resource => sub {
  my ($r, $name ) = @_;
  # Generate route for "/$name" - Controller is ExpenseTracker::Controller::camelize($name)
  my $resource = $r->route( "/$name" )->to("$name#");

  # Handle POST requests - will hit the create method in controller
  $resource->post->to('#create')->name("create_$name");
  # Handle GET requests - lists the collection of this resource - hits the list method in controller
  $resource->get->to('#list')->name("list_$name");

  $resource = $r->route( "/$name/:id" )->to("$name#");

  $resource->get->to('#show')->name("show_$name");
  $resource->delete->to('#remove')->name("delete_$name");
  $resource->put->to('#update')->name("update_$name");



  return $resource;
 });
Generic Controller for CRUD
Operations
●   Each resource needs a controller responsible for it
●   Each of those controllers will have to implement at least 7 actions:
     ○ create - POST /resource_name - creates a new resource
     ○ update - PUT /resource_name/:id - updates a resource
     ○ list      - GET /resource_name - show the collection of resources
     ○ show - GET /resource_name/:id - get the resource with id :id
     ○ remove - DELETE /resource_name/:id - annihilate resource :id
●   Possible approaches
     ○ create a Moose role that will expose all those methods and use this
        role (I guess)
     ○ implement a basic controller that will be inherited by all the other
        resource controller
Sample op - update user
In child controller (ExpenseTracker::Controller::User)
=head update
 sample of overriding a default update method
route here: PUT /user/:id
=cut
sub update{
  my $self = shift;

    return $self->render(status => 405, json => {message => 'You can only update your own profile!!!'} )
     if ( !defined $self->param('id') or !defined $self->app->user or $self->param('id') != $self->app->user->id );

    return $self->SUPER::update(@_);
}
Sample op - update user
In base controller ExpenseTracker::Controllers::Base - the one that
ExpenseTracker::Controllers::User inherits from:
sub update{
 my $self = shift;

    my $result_rs = $self->app->model
     ->resultset( $self->{resource} )
     ->search_rs(
        { id => $self->param('id') },
     );

    return $self->render_not_found if ( scalar( ( $result_rs->all ) ) == 0 );

    $result_rs->update_all( $self->req->json );

    $result_rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
    my @result = $result_rs->all();
    return $self->render_json( [ @result ] );
}
Steps for getting RESTful
routes/operations
●   Generate the DBIx::Class model based on the DB table, with DBIx::
    Class::Schema::Loader
●   Create a controller that inherits from ExpenseTracker::Controllers::Base
●   Add the resource name to the conf.yml
The End
Fork the sample app and play with it:



https://github.com/tudorconstantin/expense-tracker

RESTful web services

  • 1.
    RESTful Web Services withMojolicious and DBIx::Class
  • 2.
    About me ● Tudor Constantin ● Perl Developer @ Evozon ● http://programming.tudorconstantin.com/ ● http://stackoverflow.com/users/459233/tudor-constantin ● https://github.com/tudorconstantin ● http://www.linkedin.com/in/tudorconstantin ● twitter: @tudorconstantin ● gmail: tudorconstantin at gmail dot com
  • 3.
    Content ● Sample app overview - Expense Tracker ● (Short) Intro to RESTful Web Services ● DBIx::Class ● Mojolicious ● Routing in Mojo ● Generic Mojo Controller for CRUD operations ● Sample operation - update user ● Steps for getting RESTful routes/operations for a table in DB
  • 4.
    Sample app Expense Tracker VERYsimple application - but usable (insert expenses, assign categories and see reports) ● Five tables ○ users ○ currencies ○ categories ○ operations ○ operations_categories ● Relationships ○ 1 user has many categories ○ 1 user has many operations ○ 1 category has many sub categories ○ 1 category has many operations ○ 1 operation has many categories ○ 1 operation has a currency 1 operation belongs to a user
  • 5.
    Intro to RESTfulWeb Services REST - REpresentational State Transfer ● concept introduced in 2000 by Roy Fielding in his academic dissertation, "Architectural Styles and the Design of Network-based Software Architectures" Main ideas for REST: ● Resources are accessible through unique URLs: ○ /user/23 ○ /operations ● Use HTTP methods explicitly ○ GET - for Read ○ POST - for Create ○ PUT - for Update ○ DELETE - for Delete ● Be stateless ○ the server does not know nor cares about the state of the client application ● Transfer Representations of resources (HTML, JSON, XML, etc)
  • 6.
    DBIx::Class ● An abstraction for working with DBs ● More than an ORM (Object Relational Mapper) - It knows how to work on Result Sets ● Components - simplified overview: ○ DBIx::Class::Schema - connection to DB ○ DBIx::Class::ResultSet - a query used for fetching a set of results ○ DBIx::Class::Row - objects returned from DBIx::Class::ResultSets using the create, find, next and all methods ● Sample usage: my @rows = ExpenseTracker::Models->connect( $config->{database}->{ $mode }->{dsn}, $config->{database}->{ $mode }->{user}, $config->{database}->{ $mode }->{password}, ) # DBIx::Class::Schema ->resultset( 'ExpenseTracker::Models::Result::User' ) # DBIx::Class::ResultSet ->search_rs( { id => 10 }, ) # DBIx::Class::ResultSet - only users with id = 10 ->all(); # The collection of DBIx::Class::Row instances
  • 7.
    Mojolicious ● Microframework inspired by Sinatra (Ruby) ● Components of interest (for this app) ○ Router ○ Controller ● Not (quite) interested in: ○ Views (since we render mainly json) ● Not provided at all: ○ Models (we plug in and use DBIx::Class)
  • 8.
    Routing in Mojolicious Inapp context (ie - the startup routine): my $r = $self->routes; #sample route named 'login' for GET - executing method login from controller ExpenseTracker::Controllers::Login $r->get('/login')->to('login#login')->name('login'); Shortcut, generic routing: $params->{app_routes}->add_shortcut(resource => sub { my ($r, $name ) = @_; # Generate route for "/$name" - Controller is ExpenseTracker::Controller::camelize($name) my $resource = $r->route( "/$name" )->to("$name#"); # Handle POST requests - will hit the create method in controller $resource->post->to('#create')->name("create_$name"); # Handle GET requests - lists the collection of this resource - hits the list method in controller $resource->get->to('#list')->name("list_$name"); $resource = $r->route( "/$name/:id" )->to("$name#"); $resource->get->to('#show')->name("show_$name"); $resource->delete->to('#remove')->name("delete_$name"); $resource->put->to('#update')->name("update_$name"); return $resource; });
  • 9.
    Generic Controller forCRUD Operations ● Each resource needs a controller responsible for it ● Each of those controllers will have to implement at least 7 actions: ○ create - POST /resource_name - creates a new resource ○ update - PUT /resource_name/:id - updates a resource ○ list - GET /resource_name - show the collection of resources ○ show - GET /resource_name/:id - get the resource with id :id ○ remove - DELETE /resource_name/:id - annihilate resource :id ● Possible approaches ○ create a Moose role that will expose all those methods and use this role (I guess) ○ implement a basic controller that will be inherited by all the other resource controller
  • 10.
    Sample op -update user In child controller (ExpenseTracker::Controller::User) =head update sample of overriding a default update method route here: PUT /user/:id =cut sub update{ my $self = shift; return $self->render(status => 405, json => {message => 'You can only update your own profile!!!'} ) if ( !defined $self->param('id') or !defined $self->app->user or $self->param('id') != $self->app->user->id ); return $self->SUPER::update(@_); }
  • 11.
    Sample op -update user In base controller ExpenseTracker::Controllers::Base - the one that ExpenseTracker::Controllers::User inherits from: sub update{ my $self = shift; my $result_rs = $self->app->model ->resultset( $self->{resource} ) ->search_rs( { id => $self->param('id') }, ); return $self->render_not_found if ( scalar( ( $result_rs->all ) ) == 0 ); $result_rs->update_all( $self->req->json ); $result_rs->result_class('DBIx::Class::ResultClass::HashRefInflator'); my @result = $result_rs->all(); return $self->render_json( [ @result ] ); }
  • 12.
    Steps for gettingRESTful routes/operations ● Generate the DBIx::Class model based on the DB table, with DBIx:: Class::Schema::Loader ● Create a controller that inherits from ExpenseTracker::Controllers::Base ● Add the resource name to the conf.yml
  • 13.
    The End Fork thesample app and play with it: https://github.com/tudorconstantin/expense-tracker