Symfony HTTP client

 I am currently developing a very simple app which offers you the possibility to create question-answer flipping cards, creating thus some kind of funny Trivia to practice on any topic you want to apply it to. As a user, you create first the question-aswear entities which also include  category field, and then you can practice them in the playground area, which shows you the question on a hoverable flipping card. It also offers you a textearea which disables 60 seconds after you start typing on it, so that you can write down your answer before actually flipping the card and getting to see the correct one. 

In this app, though simple, I wanted to keep a client-web service architecture from the very beginning. The web client contains the views and communicates with the API, which contains the data through Ajax calls. This was working consistently so far. An example of the creation and saving of new question-answer items is:

  • When I press on "Create new question" button, I am redirected to question-answer item edition page, where, on every blur event happenning on textareas where the user writes down the relevant data,  an ajax call makes a PATCH request to the API, which saves question, answer and category atributes of the entity entered by the user, together with an Auto-Increment Id and a Uuid.
And that is the flow that I intented to follow everytime I needed to make operations with the database. Nevertheless, when it came to designing the flow of random question retrieval, it became a little bit trickier:

  • From the index page, if I click on "I want to practice" button, an ajax call makes a GET request to the API randomizer web service, which randomly chooses an uuid from the database and returns it.

And here is where things get different. When using a Symfony coupled architecture, I could just manage all of this data retrieval through Doctrine within the same project and render a template with parameters. But in this case, I am managing redirects and renderings within the success of the ajax calls, as these redirections are the consequence of successful operations with the API, using the function: 

window.location.replace(url);

By defining the uuid within var url, I can pass it to the twig template, but how could I pass to the view all of the needed data from the database? 

My first attempt was making a second ajax call which would be triggered as the playground area was loaded. Said Ajax call would make a GET request sending the uuid and, on success it would receive a JSON response with the question, answer and category data, and would append to each relevant section of the flipping card the corresponding html elements with said question, answer and category data.

Despite working, it's easy to see why this would not work, its unsafe and it doesn't make much sense. What is the correct answer? We still need to make a request to the API as it holds the data, but how do we communicate with the API without an ajax call? The solution is The HTTP Client

This new HTTP component is part of Symfony 4.3 and works pretty much like a library.  Essentially, it allows you making a request to a web service without the need to use an Ajax call. 
First we will need to use composer to install the component as follows on our client side:


composer require symfony/http-client

We are ready to make our HTTP client request to an API. It is time to use the component in our class:



use Symfony\Contracts\HttpClient\HttpClientInterface;

class Example
{
    private $client;

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

Now we are going to need a url and a method to make our request. In case we want to pass any parameters to the web service we are making the request to, we can do it to, either through the url or with a query string. In this case, we will pass the entity uuid previously defined through the url
Request:

1
2
3
4
5
6
7
$apiDomain = $this->getParameter('api_host');
$getQuestionServiceUrl = $this->apiDomain.'/v1/Questions/'.$uuid
$response = $this->client->request(
            'GET',
            '$getQuestionServiceUrl'
)->getContent(); $jsonDecodedResponse = json_decode($response, true);

On line number 1 we are getting the apiDomain value defined on services.yaml and .env files. Next we define a url. As our API contains resources and collections of resources, our service url is defined accordingly. 
On line 6 getContent() method gets the response body as a string, which is why we need to json_decode method in order to be able to access the JSON values returned by our API web service:

On the API web service side we return:


1
2
3
4
5
6
return new JsonResponse([
            'uuid' => $uuid,
            'question' => $this->randomQuestionAndAnswerItem->getQuestion(),
            'answer' => $this->randomQuestionAndAnswerItem->getAnswer(),
            'category' => $this->randomQuestionAndAnswerItem->getCategory(),
        ]);

Which we will recover on the client side as: 


$question = $jsonDecodedResponse['question'];
$answer = $jsonDecodedResponse['answer'];
$category = $jsonDecodedResponse['category'];

There you have it! As simple and short as it has been described on Symfony Manual, it's way easier to understand it when you use it on your daily practice. Now it's your turn!

Happy coding everyone!

Comments

Popular posts from this blog

SOLID: The Single-Responsibility Principle (SRP)

Inheritance: Abstract classes and interfaces