13
RESTful Web Services with Mojolicious and DBIx::Class

RESTful web services

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: RESTful web services

RESTful Web Services with Mojolicious and DBIx::Class

Page 2: RESTful web services

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

Page 3: RESTful web services

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

Page 4: RESTful web services

Sample app Expense TrackerVERY 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

Page 5: RESTful web services

Intro to RESTful Web ServicesREST - 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)

Page 6: RESTful web services

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

Page 7: RESTful web services

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)

Page 8: RESTful web services

Routing in MojoliciousIn 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; });

Page 9: RESTful web services

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

Page 10: RESTful web services

Sample op - update userIn child controller (ExpenseTracker::Controller::User)

=head update sample of overriding a default update methodroute here: PUT /user/:id=cutsub 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(@_);}

Page 11: RESTful web services

Sample op - update userIn 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 ] );}

Page 12: RESTful web services

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

Page 13: RESTful web services

The EndFork the sample app and play with it:

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