Skip to main content

Drupal 8: Consuming a Third-Party API with Drupal::httpClient(Guzzle) as a Service

For a recent project, i was asked to build a service that would consume the client's internal data from his API. I got lucky with this because it was a very well structured and documented API. The only thing that i had not tried before was to use the new Drupal::httpClient() functionality in combination with a raw body post of (form-params) data (it was really hard - for me - to find a well written tutorial for this). The API could be used as a resource for getting POIs (Point of Interest) but the admin could also "create - post" pois in demand from his back office.

This is the working custom module awesome_api_client with all the necessary files in order to work right.

So first we need a .info.yml file for the module

name: 'Awesome Api' type: module description: 'A REST API custom module for retrieving and updating data through the Awesome API' core: 8.x package: 'Pixelthis'

Then we need a .services.yml file, for registering the service.

services: awesome_client: class: Drupal\awesome_api\AwesomeClient arguments: - '@http_client_factory'

in awesome.module file i use hook_cron for registering the API - GET resources call in recursive 15' cron tasks

awesome.module

in web/custom/modules/awesome_api/src/AwesomeClient.php i added the actual PHP Client Class Service

client = $http_client_factory->fromOptions([ 'base_uri' => 'http://awesome_api_ip:15333/', // 'headers' => [ // 'Content-Type' => 'application/json', // ], ]); } catch (RequestException $e) { watchdog_exception('awesome_module', $e); } } /** * Get all available pois * * @param int $amount * * @return array */ public function pois() { try { $response = $this->client->get('pois', []); $data = Json::decode($response->getBody()); return $data; } catch (RequestException $e) { watchdog_exception('awesome_module', $e); } } /** * Get all available pois * * @param int $amount * * @return array */ public function poi(int $id) { try { $response = $this->client->get('poi/'.$id); $data = Json::decode($response->getBody()); return $data; } catch (RequestException $e) { watchdog_exception('awesome_module', $e); } } /** * Add a new Poi from website to API * * @param array $api_data * * @return object */ public function add_api_poi(array $api_data) { try { $headers = ['Content-Type' => 'application/json']; $options = [ 'body' => Json::encode($api_data), 'headers' => $headers, ]; return $response; } catch (RequestException $e) { watchdog_exception('awesome_module', $e); } } /** * Get all available poi crowdiness stats * * @param int $id * * @return array */ public function poi_crowdiness(int $id,$timestamp_start = '2020-01-01 00:00:00',$timestamp_end = '2020-12-01 00:00:00') { try { $options = [ 'form_params' => [ //%Y-%m-%d %H:%M:%S 'timestamp_start' => $timestamp_start, 'timestamp_end' => $timestamp_end, ], ]; $response = $this->client->get('poi/'.$id.'/stats',$options); $data = Json::decode($response->getBody()); return $data; } catch (RequestException $e) { watchdog_exception('awesome_module', $e); } } public function manage_poi($poi_api_data){ $poi_exists_id = $this->poi_exists_to_db($poi_api_data['id']); if (!$poi_exists_id){ $this->add_poi_to_db($poi_api_data); } else { $this->update_poi_to_db($poi_api_data,$poi_exists_id); } } private function poi_exists_to_db($poi_api_id){ $query = \Drupal::entityQuery('node') ->condition('type', 'poi') ->condition('field_api_id', $poi_api_id); $nids = $query->execute(); if (!empty($nids)){ return current($nids); } return false; } private function update_poi_to_db($poi_api_data,$id){ $node = Node::load($id); if ($node instanceof NodeInterface) { try { $lat_lng = explode(',',$poi_api_data['geo_data']); $node->set('title', $poi_api_data['name']); $node->set('field_max_poi_population', $poi_api_data['max_poi_population']); $node->set('field_poi_type_ref',$this->manage_term($poi_api_data['type'],'poi_type')); $node->set('field_poi_address',$poi_api_data['address']); $node->set('field_poi_category_ref',$this->manage_term($poi_api_data['category'],'poi_category')); $node->set('field_geo_point',['lat' => trim($lat_lng[0]), 'lng' => trim($lat_lng[1])]); $node->save(); } catch (\Exception $e) { watchdog_exception('Poi API ERROR UPDATE', $e); } } } private function add_poi_to_db($poi_api_data){ // 'address' => string(12) "Ormos Thiras" // 'category' => string(4) "Port" // 'geo_data' => string(20) "36.386122, 25.428413" // 'id' => integer1 // 'max_poi_population' => integer2500 // 'name' => string(14) "Santorini Port" // 'type' => string(20) "terminal_station_poi" $lat_lng = explode(',',$poi_api_data['geo_data']); $node = Node::create([ 'type' => 'poi', 'title' => $poi_api_data['name'], 'field_api_id' => $poi_api_data['id'], 'field_geo_point' => ['lat' => trim($lat_lng[0]), 'lng' => trim($lat_lng[1])], 'field_max_poi_population' => $poi_api_data['max_poi_population'], 'field_poi_type_ref' => $this->manage_term($poi_api_data['type'],'poi_type'), 'field_poi_address' => $poi_api_data['address'], 'field_poi_category_ref' => $this->manage_term($poi_api_data['category'],'poi_category'), ]); $node->save(); } private function manage_term($term_name,$vid){ $term = \Drupal::entityTypeManager()->getStorage('taxonomy_term') ->loadByProperties(['name' => $term_name, 'vid' => $vid]); $term = reset($term); if ($term){ return $term->id(); } $term =Term::create([ 'name' => $term_name, 'vid' => $vid, ]) ->save(); //Returns $term id value e.g 2,3,577

Now i just wanted to add a custom Block too just for testing how to apply the Service with a Dependency Injection. So i added a /web/custom/modules/src/Plugin/Block/AwesomePois.php

AwesomeClient = $awesome_client; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $configuration, $plugin_id, $plugin_definition, $container->get('awesome_client') ); } /** * {@inheritdoc} */ public function build() { //Get all available Pois $pois_list = $this->peopleFlowClient->pois(); return [ '#theme' => 'item_list', '#items' => $pois_list, ]; } } ?> And that's about it :)
service

Photography

Image
The Passenger
Image
pytheas, ultima thule #6
Image
pytheas, ultima thule #5
Image
summer '19
Image
pytheas, ultima thule #7
Image
pytheas, ultima thule #8