How to write your own Normalizer for REST resource in Drupal 8

I want to share a small example which shows how to add your own property into the response of the REST Resource. In my case, I'm adding property "preview_url" into the response for a Resource for creating a File entity.

Why do I need it? Each time when I'm uploading a new file through REST, I'm receiving a response with data about an entity of the file. It contains property "uri", but this uri has format like "public://some-path/filename.jpg". I want to show the preview of an uploaded file, but I cannot use this uri right in the browser. Would be nice, to have an ability to show the preview right after the uploading, without any additional requests.

So, let's do this :)

There are a lot of options to do this, but I think that in our case we have to use Plugin with type "Normalizer".

Step #1, define plugin "Normalizer". You have to create file "your_module.services.yml" and put there the next code:

services:
  serializer.normalizer.file_entity.your_module:
      class: Drupal\your_module\Normalizer\YourModuleFileEntityNormalizer
      tags:
        - { name: normalizer, priority: 30 }
      arguments: ['@entity.manager', '@rest.link_manager', '@module_handler', '@file_system']

Step #2, create your plugin in the directory "modules/custom/your_module/src/Normalizer/YourModuleFileEntityNormalizer.php" and put there the next code:

<?php

namespace Drupal\your_module\Normalizer;

use Drupal\hal\Normalizer\FileEntityNormalizer;
use Drupal\image\Entity\ImageStyle;

/**
 * Normalizer for File entity.
 */
class YourModuleFileEntityNormalizer extends FileEntityNormalizer {

  /**
   * {@inheritdoc}
   */
  protected $supportedInterfaceOrClass = 'Drupal\file\FileInterface';

  /**
   * {@inheritdoc}
   */
  public function normalize($entity, $format = NULL, array $context = array()) {
    $data = parent::normalize($entity, $format, $context);

    // If we need preview add a link for a preview.
    if (!empty($_GET['image_style'])) {
      // If we have an uri.
      if (!empty($data['uri'][0]['value'])) {
        if ($image_style = ImageStyle::load($_GET['image_style'])) {
          $data['preview_url'] = $image_style->buildUrl($data['uri'][0]['value']);
        }
      }
    }

    return $data;
  }
}

As you can see, I'm checking $_GET and each time, when I'm sending request by url DOMAIN/entity/file?_format=hal_json&image_style=thumbnail, I'll get an entity of file with my custom property "preview_url".

I hope that this note was useful for you :) Don't hesitate to ask for help.