mihirpatel83's avatar

Third party api structure

I am looking to consume a third party api. I would use Guzzle to call third party urls and fetch the result. Now in each controller function I will have to use a sample code as below to make calls

$client = new \GuzzleHttp\Client([
            'base_uri' => 'http://xxx.xxx.xxx:3000',
            'headers' => [
                'Content-Type' => 'application/json',
                'customer_name' => 'test',
                'customer_key' => 's324ghe7cf77f652ef2f030b9f26'
            ]
        ]);
$url = 'actions/authenticate';
$client->post($url);

I tried to google and find a solution and understand but failed how can I create a wrapper for such commands so that I can easily just make a call in the controller function to fetch the data. I come across using singleton, service container but could not figure out how exactly to structure the code and execute it.

Would be great If someone can guide me how to achieve that?

0 likes
7 replies
jasonfrye's avatar

How I've done it is by creating a Trait called GuzzleTrait. It looks like this:

<?php

namespace App\Library;

use GuzzleHttp;

trait GuzzleTrait {

    /**
     * @param $url
     * @return GuzzleHttp\Message\FutureResponse|GuzzleHttp\Message\ResponseInterface|GuzzleHttp\Ring\Future\FutureInterface|null
     * Setup function for the guzzle requests going out to the live subscription site
     */
    private function guzzleRequest($url)
    {
        $client = new GuzzleHttp\Client();

        $subResponse = $client->get(env('API').$url,[
            'headers' => [
                'Content-Type' => 'application/json',
                'customer_name' => 'test',
                'customer_key' => 's324ghe7cf77f652ef2f030b9f26'
            ]]);

        return $subResponse;
    }

    private function GuzzlePostRequest($data, $url)
    {
        $client = new GuzzleHttp\Client();
        $client->setDefaultOption('headers', [
                'Content-Type' => 'application/json',
                'customer_name' => 'test',
                'customer_key' => 's324ghe7cf77f652ef2f030b9f26'
        ]);

        $subResponse = $client->post(env('API').$url,['form_params' => $data]);

        return $subResponse;
    }

    private function GuzzlePutRequest($data, $url)
    {
        $client = new GuzzleHttp\Client();
        $client->setDefaultOption('headers', [
                'Content-Type' => 'application/json',
                'customer_name' => 'test',
                'customer_key' => 's324ghe7cf77f652ef2f030b9f26'
        ]);

        $subResponse = $client->put(env('API').$url,['body' => $data]);

        return $subResponse;
    }
}

I can then use that trait on the controller and make calls like this:

$subResponse = $this->guzzleRequest("/orders/$order->id");

Obviously you can modify the trait to return what you need it to. In my case, I was working with subscriptions, so this made sense.

Hope that helps.

1 like
mihirpatel83's avatar

Thank you for taking out time to revert with the code. Haven't thought of this way. After checking out many references I opted to used singleton approach with a service container as below:

In AppServiceProvider's boot method

$this->app->singleton(ThirdPartyApi::class, function() {
    return new ThirdPartyApi(new \GuzzleHttp\Client([
        'base_uri' => config('services.baseurl'),
        'headers' => [
            'Content-Type' => 'application/json',
            'customer_name' => config('services.customername'),
            'customer_key' => config('services.customerkey')
        ]
    ]));
})

Class ThirdPartyApi which will have functions to consume an api

namespace App\Services;
use GuzzleHttp\Client;      
class ThirdPartyApi {

    public $client;

    public function __construct(Client $client) {
        $this->client = $client;
    }

    public function authentication() {
        $url = 'get_authn_url';
        $data = [];
        try {
            $response = $this->client->post($url);
            $statuscode = $response->getStatusCode();
            if ($statuscode == 200) {
                $responseData = json_decode($response->getBody()->getContents());                
            }
        } catch (\GuzzleHttp\Exception\ClientException $e) {
            $response = $e->getResponse();
            $responseBodyAsString = $response->getBody()->getContents();            
        } catch (\GuzzleHttp\Exception\ConnectException $e) {
            $response = $e->getResponse();            
        }
    }
}

Finally, using it in a controller function as below

$apiClient = app(\App\Services\ThirdPartyApi::class);
@apiClient->authentication();

I am not sure now which seems to be a correct way to go ahead.

bugsysha's avatar

I would go with your solution cause it can be tested compared to solution in a trait. It is a bit clunky but it will do for now.

mihirpatel83's avatar

Great... Thanks.

I was wondering if you can help with a reference url or something that guides in implementing third party api's in a much better or efficient manner.

bugsysha's avatar

Look at how Guzzle is written. It can give you general guidelines but your implementation should not be that flexible. Conform to an interface. Extend Guzzle just so you can mock what you own. Also checkout older videos here on Laracasts where Jeffrey integrated with Stripe and who knows what else. Besides that if you have any questions feel free to ask.

jasonfrye's avatar

Traits can be tested, but the solution you provided seems fine too. Good luck!

bugsysha's avatar

@jasonfrye yes you can but you have to add it to some class, while his approach you can new up that class and not have to include any other.

Please or to participate in this conversation.