Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

DDSameera's avatar

How to Call 3rd party API service in professional way (Best Practice)

I want to use 3rd party API service in my laravel web application. I know how to connect to the 3rd API service. So i wrote my code in controller. actually i don't want to make lengthy code in controller class.

I want to build up separate class for this . Eg : "VideoAPI" and i want to put all my API calling in there .after that i want to call them to my controller . is that best practice if , NO, please advise me .

Please suggest me your solution .

here is my controller code

...
....

public function getAllVideos()
    {
        $client = new Client();

        $token = "eyJhbGciOisImlzcyI6Imh0dHBzOi8vZHludHViZS5jb20iLCJhdWQiOiJNYW5hZ2UifQ.uiCkRLmKyigO282US3VOhZjqyg6P-4F5MH8snRpF2uU";

        $query = [
            'page' => 1,
            'size' => 100,
            'sort' => 'name:-1',


        ];

        $headers = [
            'Authorization' => 'Bearer ' . $token,

        ];

        $uri = "https://api.videomummy.com/v1/projects";
        $response = $client->request('GET', $uri, [
            'headers' => $headers,
            'query'=> $query
        ]);
        dd($response->getBody()->getContents());
    }


0 likes
22 replies
CorvS's avatar

I want to build up separate class for this . Eg : "VideoAPI" and i want to put all my API calling in there .after that i want to call them to my controller .

Sounds about right. You can make use of dependency injection and inject your VideoAPI class wherever you need (e.g. constructor). As you already said, having the code inside your controller function as shown here is not really what you want.

1 like
CorvS's avatar
CorvS
Best Answer
Level 27

@ddsameera Simply create a class "VideoAPI" somewhere in your project (e.g. App\Services\VideoAPI). Inside that class' constructor you do all necessary configuration like setting API keys/secrets or initializing your HTTP client and then you expose public functions that allow you to interact with your VideoAPI however you need.

class VideoAPI {

    public function __construct()
    {
        // Do all necessary configuration
        // ...
    }

    public function getProjects()
    {
        // ...
    }
}

Now if you need your VideoAPI inside one specific controller function only you can inject it there. If you need it in multiple places, inject it inside your controller's constructor.

class VideoController extends Controller {

    private VideoAPI $videoAPI;

    public function __construct(VideoAPI $videoAPI)
    {
        $this->videoAPI = $videoAPI;
    }

    public function getAllVideos()
    {
        return $this->videoAPI->getProjects();
    }
}

If you are new to dependency injection and curious how that works I recommend watching https://laracasts.com/series/laravel-6-from-scratch/episodes/38 onward.

2 likes
DDSameera's avatar

Thanks for your reply.

Normally I would like to create Trait and define the functions like this . then i m planning to call those methods from my controller.

Is that okay ? is that good way ? @corvs

martinbean's avatar

You’ve been given a suggestion: create a class and put methods in there for interacting with the video API.

Traits aren’t replacements for classes.

2 likes
DDSameera's avatar

@martinbean ,Agreed. I did it .

@corvs , Hope this is fine. Could you please confirm it.

Services\VideoAPI.php

<?php


namespace App\Services;


use GuzzleHttp\Client;

class VideoAPI
{

    const DYNTUBE_TOKEN = "eyJhbGciOiJIUzI1Ns";


    public function getProjects()
    {
        $client = new Client();


        $headers = [
            'Authorization' => 'Bearer ' . self::DYNTUBE_TOKEN,
        ];

        $query = [
            'page' => 1,
            'size' => 100,
            'sort' => 'name:-1',

        ];


        return $client->request('GET', self::DYNTUBE_API_URL, [
            'headers' => $headers,
            'query' => $query
        ]);
    }

}

LessonController Class

...
   public function getAllVideos()
    {
       dd($this->videoAPI->getProjects());

    }
...
CorvS's avatar

Is that okay ? is that good way ?

As Martin already said: No. In the first place traits were introduced to fight PHP's single inheritance. It doesn't make sense to add a trait to a controller in order to achieve what you want.

3 likes
CorvS's avatar

No, you don't want to initialize a client everytime getProjects() is called. Leave that to the constructor. Additionally you probably want to store your API token inside a config file and supply it to your application via your .env file, so it's not hard-coded.

2 likes
DDSameera's avatar

I modified it. please check now.

VideoAPI service class

<?php


namespace App\Services;


use GuzzleHttp\Client;

class VideoAPI
{

    const DYNTUBE_TOKEN = config('app.dyntube_token');
    const DYNTUBE_API_URL = config('app_dyntube_api_url');

    protected $client;

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

    }

    public function getProjects()
    {


        $headers = [
            'Authorization' => 'Bearer ' . self::DYNTUBE_TOKEN,
        ];

        $query = [
            'page' => 1,
            'size' => 100,
            'sort' => 'name:-1',

        ];


        return $this->client->request('GET', self::DYNTUBE_API_URL, [
            'headers' => $headers,
            'query' => $query
        ])->getBody()->getContents();
    }

}

Controller Class

public function getAllVideos()
    {
       dd($this->videoAPI->getProjects());

    }
CorvS's avatar

Do everything that has to be done once in the constructor, like setting the Authorization header.

$this->client = new Client([
    'defaults' => [
        'headers' => ['Authorization' => 'Bearer ' . self::DYNTUBE_TOKEN],
    ]
]);

Using an expression as the value for a class constant (const) does not work, your IDE should already tell you that. Fetch your config values inside the constructor as well.

1 like
DDSameera's avatar

Modified

<?php


namespace App\Services;


use GuzzleHttp\Client;

class VideoAPI
{


    protected $client;

    public function __construct(Client $client)
    {

        $token = config('app.dyntube_token');
       $apiUrl = config('app.dyntube_api_url');


        $headers = [
            'Authorization' => 'Bearer ' . $token,
        ];

        $query = [
            'page' => 1,
            'size' => 100,
            'sort' => 'name:-1',

        ];


        $client = $client->request('GET', $apiUrl, [
            'headers' => $headers,
            'query' => $query
        ]);

        $this->client = $client;


    }

    public function getProjects()
    {
        return $this->client->getBody()->getContents();
    }

}

bugsysha's avatar

This is not good.

  1. Your VideoAPI class shouldn't be tied to Laravel.
  2. Your class knows too much about making HTTP requests while the class itself is not an HTTP client
  3. You are breaking the Law Of Demeter

If all of this makes sense feel free to ask questions.

martinbean's avatar

Why are you doing a HTTP request in your constructor? That’s going to run every time you resolve or instantiate that class now.

1 like
DDSameera's avatar

I m confused. I m newer person for laravel. could you please provide me code for this @martinbean

I modified my code like this

<?php


namespace App\Services;


use GuzzleHttp\Client;

class VideoAPI
{


    protected $token;
    protected $apiUrl;
    protected $headers;
    protected $query;


    public function __construct()
    {

        $token =  config('app.token');
        $apiUrl = config('app.apiUrl');


        $headers = [
            'Authorization' => 'Bearer ' . $token,
        ];

        $query = [
            'page' => 1,
            'size' => 100,
            'sort' => 'name:-1',

        ];

        $this->apiUrl = $apiUrl;
        $this->token = $token;
        $this->headers= $headers;
        $this->query = $query;



    }

    public function getProjects()
    {

        $client = new Client();

        $client = $client->request('GET', $this->apiUrl, [
            'headers' => $this->headers,
            'query' => $this->query
        ]);



        return $client->getBody()->getContents();
    }

}

martinbean's avatar

@ddsameera Stop asking people to “provide the code”. We’re not here to write code blocks for you to then copy and paste.

1 like
DDSameera's avatar

@martinbean , sorry for the misunderstanding. Honestly I wrote my code.You can see it in above reply. I want to ensure that i m in right track.

bugsysha's avatar

First, decide what this class needs to do. Is it an orchestrating class or an HTTP client class? To me, it looks more like an HTTP client class. If that is correct then remove everything from the constructor. You should only pass an interface to the constructor so you are not tied to a single implementation and assign it to the instance variable/property. Configuration should be passed through the constructor or have a protected method that will provide it. Headers and query params can be a protected property, method, or constant, depending on what you need. You are breaking the Law Of Demeter with $client->getBody()->getContents(). That should be replaced with getBodyContents() or something that will exist on the client implementations for the interface you've type-hinted in the constructor. And so on...

drubie87's avatar

DDSameera, what kind of project is this? Are you getting paid to do this? Are you developing this for your own site / business? Is this just a learning project? Are you working on it solo or will you be working on it with other people?

Depending on how you plan on using this, perhaps you are over thinking it? If you're still learning how Laravel and PHP programming works, it might be a little pre-mature to expect yourself to be writing 100% professional code on your first try.

If you watch Jeffrey Way, he doesn't even write 100% professional code right off the bat! First he prototypes by writing it the 'wrong way', and then he refactors it into a more correct way of writing code once he's proven the basic concepts. And sometimes, context changes quickly and what was 'right' 30 minutes ago becomes clumsy and in need of refactor just 30 minutes later as you discover needed features.

So basically just keep learning, and I think the 'right way' will reveal itself in time.

2 likes
vivdroid's avatar

I read all thread of suggestions. Yet i m not expert in laravel but i can suggest to @ddsameera you can make your helper function in helper.php then add in composers.json file. when you sure about performance make own package to call professional web service.

Please or to participate in this conversation.