How do I consume REST data with an external service?
I actually have implemented a similar thing in my project.

I created a module structure like as follow.

 my_module [directory]
        products [directory]
          products.info.yml
          products.module
          products.routing.yml
          src[directory]
            Controller[directory]
              - ProductsController.php
          templates[directory]
              - products-list.html.twig
        my_module.info.yml    
        my_module.module
        my_module.routing.yml    


products.routing.yml

 products.list:
    path: '/products'
    defaults:
      _controller: '\Drupal\products\Controller\ProductsController::getList'
      _title: 'Get List'
    requirements:
      _permission: 'access content'  


src/Controller/ProductsController.php

    /**
     * @file
     * Contains \Drupal\products\Controller\ProductsController.
     */
    namespace Drupal\products\Controller;

    use Drupal\Core\Controller\ControllerBase;

    /**
     * Controller routines for products routes.
     */
    class ProductsController extends ControllerBase {

      /**
       * Callback function to get the data from REST API
       */
      public function getList() {

        $data = array();
        $response = $data = $result = null;

        if (function_exists('my_module_reponse')) {
          $response = my_module_reponse('http://webservice.com/products/list', 'GET');
        }

        if ($response) {

          $result = json_decode($response);
          $data = array();

          // # add all the data in one multiple dim array
          $data['title'] = 'products to consume REST API';
          $data['users'] = $result;

          // display the content in the middle section of the page
          $build = array(
            '#theme' =&gt; 'products_list', // assign the theme [products-list.html.twig]
            '#title' =&gt; 'products to consume REST API', // assign the page title
            '#pagehtml' =&gt; 'data is coming from : products/list ', // assign the string message like this
            '#data' =&gt; $data
          ); // assign the array like this to access in twig file
        }
        return $build;
      }
    }


templates/products-list.html.twig

&lt;div class="container"&gt;
  &lt;h1&gt; {{ data.title }} &lt;/h1&gt;
  &lt;div&gt;&lt;strong&gt; {{ pagehtml }} &lt;/strong&gt;&lt;/div&gt;

  {% for item in data.users %}
    &lt;div&gt;{{ item.yourproductnamekey}}&lt;/div&gt;
  {% endfor %}

&lt;/div&gt;


my_module.module

      /**
       * @file
       * Test API module.
       */
      function my_module_reponse($url, $method = 'GET', $params = array()) {
        $methods = [
          'POST',
          'GET',
          'PUT',
          'DELETE'
        ];
        $result = array();

        try {
          if (empty($url)) {
            throw new Exception("Invalid url");
          }
          if (in_array(strtoupper($method), $methods) === FALSE) {
            throw new Exception("Invalid method");
          }
          else {
            // will need a request call for GET, POST, PUT
            switch (strtoupper($method)) {
              case 'GET':
              case 'POST':
              case 'PUT':
              case 'DELETE':
                if (function_exists('my_module_call_request')) {
                  $result = my_module_call_request($url, $method, $params);
                }
                break;
              default:
                throw new Exception("Invalid method");
                break;
            }
          }
        }
        catch (Exception $e) {
          echo $e-&gt;getMessage();
        }

        return $result;
      }

      function my_module_call_request($requesturl, $method, $params = array()) {

        // request url
        $url = $requesturl;

        try {

          // initialized curl
          $ch = curl_init();

          // set header for curl request
          $headers = array(
            "Cache-Control: no-cache",
            "Pragma: no-cache"
          );

          // set required setting with curl configuration
          curl_setopt($ch, CURLOPT_URL, $url);
          curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
          curl_setopt($ch, CURLOPT_FRESH_CONNECT, TRUE);
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
          curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
          curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);

          // pass the additional values
          curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));

          // close the curl connection
          $result = curl_exec($ch);
          curl_close($ch);

          return $result;
        }
        catch (Exception $e) {
          echo $e-&gt;getMessage();
        }
      }


products.module

    /**
     * implements hook_theme();
     * description: create the file as per below
     * eg : products-list.html.twig  for "products_list"
     */
    function example_theme($existing, $type, $theme, $path) {
      return array(
        'products_list' =&gt; array(
          'variables' =&gt; array(
            'pagehtml' =&gt; NULL,
            'data' =&gt; NULL
          )
        ),
      );
    }


Each submodule will contain the individual routing file and function related to each page. It will make easy for you to maintain the code as well.