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


WebService::Client - A base role for quickly and easily creating web service clients


version 1.0001


        package WebService::Foo;
        use Moo;
        with 'WebService::Client';

        has auth_token  => ( is => 'ro', required => 1 );

        sub BUILD {
            my ($self) = @_;

            $self->ua->default_header('X-Auth-Token' => $self->auth_token);
            # or if the web service uses http basic/digest authentication:
            # $self->ua->credentials( ... );
            # or
            # $self->ua->default_headers->authorization_basic( ... );

        sub get_widgets {
            my ($self) = @_;
            return $self->get("/widgets");

        sub get_widget {
            my ($self, $id) = @_;
            return $self->get("/widgets/$id");

        sub create_widget {
            my ($self, $widget_data) = @_;
            return $self->post("/widgets", $widget_data);

    my $client = WebService::Foo->new(
        auth_token => 'abc',
        logger     => Log::Tiny->new('/tmp/foo.log'), # optional
        log_method => 'info', # optional, defaults to 'DEBUG'
        timeout    => 10, # optional, defaults to 10
        retries    => 0,  # optional, defaults to 0
    my $widget = $client->create_widget({ color => 'blue' });
    print $client->get_widget($widget->{id})->{color};

Minimal example which retrieves the current Bitcoin price:

    package CoinDeskClient;
    use Moo;
    with 'WebService::Client';

    my $client = CoinDeskClient->new(base_url => '');
    print $client->get('/bpi/currentprice.json')->{bpi}{USD}{rate_float};

Example using mode v2. When using mode v2, the client's http methods will always return a WebService::Client::Response response object.

    package CoinDeskClient;
    use Moo;
    with 'WebService::Client';

    my $client = CoinDeskClient->new(
        mode => 'v2',
        base_url => '',
    my $data = $client->get('/bpi/currentprice.json')->data;
    print $data->{bpi}{USD}{rate_float};


This module is a base role for quickly and easily creating web service clients. Every time I created a web service client, I noticed that I kept rewriting the same boilerplate code independent of the web service. This module does the boring boilerplate for you so you can just focus on the fun part - writing the web service specific code.


These are the methods this role composes into your class. The HTTP methods (get, post, put, and delete) will return the deserialized response data, if the response body contained any data. This will usually be a hashref. If the web service responds with a failure, then the corresponding HTTP response object is thrown as an exception. This exception is a HTTP::Response object that has the HTTP::Response::Stringable role so it can be easily logged. GET requests that respond with a status code of 404 or 410 will not throw an exception. Instead, they will simply return undef.

The http methods get/post/put/delete can all take the following optional named arguments:


A hashref of custom headers to send for this request. In the future, this may also accept an arrayref. The header values can be any format that HTTP::Headers recognizes, so you can pass content_type instead of Content-Type.


A coderef that does custom serialization for this request. Set this to undef if you don't want any serialization to happen for this request.


A coderef that does custom deserialization for this request. Set this to undef if you want the raw http response body to be returned.


        { color => 'blue' },
        headers      => { x_custom_header => 'blah' },
        serializer   => sub { ... },
        deserialized => sub { ... },


    $client->get('/foo', { query => 'params' });
    $client->get('/foo', { query => [qw(array params)] });

Makes an HTTP GET request.


    $client->post('/foo', { some => 'data' });
    $client->post('/foo', { some => 'data' }, headers => { foo => 'bar' });

Makes an HTTP POST request.


    $client->put('/foo', { some => 'data' });

Makes an HTTP PUT request.


    $client->patch('/foo', { some => 'data' });

Makes an HTTP PATCH request.



Makes an HTTP DELETE request.


    my $req = HTTP::Request->new(...);

This is called internally by the above HTTP methods. You will usually not need to call this explicitly. It is exposed as part of the public interface in case you may want to add a method modifier to it. Here is a contrived example:

    around req => sub {
        my ($orig, $self, $req) = @_;
        $req->authorization_basic($self->login, $self->password);
        return $self->$orig($req, @rest);


Logs a message using the provided logger.



This is the only attribute that is required. This is the base url that all request will be made against.


Optional. A proper default LWP::UserAgent will be created for you.


Optional. A proper default JSON object will be created via JSON::MaybeXS

You can also pass in your own custom JSON object to have more control over the JSON settings:

    my $client = WebService::Foo->new(
        json => JSON::MaybeXS->new(utf8 => 1, pretty => 1)


Optional. Default is 10.


Optional. Default is 0.




Optional. Default is 'application/json'.


Optional. A coderef that serializes the request content. Set this to undef if you don't want any serialization to happen.


Optional. A coderef that deserializes the response body. Set this to undef if you want the raw http response body to be returned.


Here are some examples of web service clients built with this role. You can view their source to help you get started.




Naveed Massjouni <>


This software is copyright (c) 2014 by Naveed Massjouni.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.