 r.patch                                            | 224 +++++++++++++++++++++
 .../jsonapi_test_data_type.info.yml                |   5 +
 .../jsonapi_test_data_type.services.yml            |   6 +
 .../src/Normalizer/StringNormalizer.php            |  25 +++
 .../jsonapi_test_field_type.info.yml               |   5 +
 .../jsonapi_test_field_type.services.yml           |   6 +
 .../src/Normalizer/StringNormalizer.php            |  27 +++
 tests/src/Functional/ExternalNormalizersTest.php   | 149 ++++++++++++++
 yar                                                | 130 ++++++++++++
 9 files changed, 577 insertions(+)

diff --git a/r.patch b/r.patch
new file mode 100644
index 0000000..08744d7
--- /dev/null
+++ b/r.patch
@@ -0,0 +1,224 @@
+diff --git a/src/Normalizer/FieldItemNormalizer.php b/src/Normalizer/FieldItemNormalizer.php
+index abc59da..2bde162 100644
+--- a/src/Normalizer/FieldItemNormalizer.php
++++ b/src/Normalizer/FieldItemNormalizer.php
+@@ -2,6 +2,7 @@
+ 
+ namespace Drupal\jsonapi\Normalizer;
+ 
++use Drupal\Core\Cache\CacheableMetadata;
+ use Drupal\Core\Field\FieldItemInterface;
+ use Drupal\Core\TypedData\TypedDataInternalPropertiesHelper;
+ use Drupal\jsonapi\Normalizer\Value\FieldItemNormalizerValue;
+@@ -40,6 +41,10 @@ class FieldItemNormalizer extends NormalizerBase {
+     if (floatval(\Drupal::VERSION) >= 8.5) {
+       $field_item = TypedDataInternalPropertiesHelper::getNonInternalProperties($field_item);
+     }
++
++    // @todo Use the constant \Drupal\serialization\Normalizer\CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY instead of the 'cacheability' string when JSON API requires Drupal 8.5 or newer.
++    $context['cacheability'] = new CacheableMetadata();
++
+     foreach ($field_item as $property_name => $property) {
+       $values[$property_name] = $this->serializer->normalize($property, $format, $context);
+     }
+@@ -47,7 +52,10 @@ class FieldItemNormalizer extends NormalizerBase {
+     if (isset($context['langcode'])) {
+       $values['lang'] = $context['langcode'];
+     }
+-    return new FieldItemNormalizerValue($values);
++    // @todo Use the constant \Drupal\serialization\Normalizer\CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY instead of the 'cacheability' string when JSON API requires Drupal 8.5 or newer.
++    $value = new FieldItemNormalizerValue($values, $context['cacheability']);
++    unset($context['cacheability']);
++    return $value;
+   }
+ 
+   /**
+diff --git a/src/Normalizer/FieldNormalizer.php b/src/Normalizer/FieldNormalizer.php
+index bf5eaa6..097ddc8 100644
+--- a/src/Normalizer/FieldNormalizer.php
++++ b/src/Normalizer/FieldNormalizer.php
+@@ -75,12 +75,15 @@ class FieldNormalizer extends NormalizerBase {
+ 
+     $normalized_field = new FieldNormalizerValue($normalizer_items, $cardinality);
+ 
+-    // @todo Remove this when JSON API requires Drupal 8.5 or newer.
+-    if (floatval(\Drupal::VERSION) >= 8.5) {
+-      // @todo Use the constant \Drupal\serialization\Normalizer\CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY instead of the 'cacheability' string when JSON API requires Drupal 8.5 or newer.
+-      $normalized_field->addCacheableDependency($context['cacheability']);
+-      unset($context['cacheability']);
+-    }
++//    // @todo Remove this when JSON API requires Drupal 8.5 or newer.
++//    if (floatval(\Drupal::VERSION) >= 8.5) {
++//      // @todo Use the constant \Drupal\serialization\Normalizer\CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY instead of the 'cacheability' string when JSON API requires Drupal 8.5 or newer.
++//      $normalized_field->addCacheableDependency($context['cacheability']);
++//      unset($context['cacheability']);
++//    }
++//    else {
++//      $normalized_field = new FieldNormalizerValue($normalizer_items, $cardinality);
++//    }
+ 
+     return $normalized_field;
+   }
+diff --git a/src/Normalizer/RelationshipItemNormalizer.php b/src/Normalizer/RelationshipItemNormalizer.php
+index 93bc6af..f14fc8d 100644
+--- a/src/Normalizer/RelationshipItemNormalizer.php
++++ b/src/Normalizer/RelationshipItemNormalizer.php
+@@ -2,6 +2,7 @@
+ 
+ namespace Drupal\jsonapi\Normalizer;
+ 
++use Drupal\Core\Cache\CacheableMetadata;
+ use Drupal\Core\Entity\EntityInterface;
+ use Drupal\jsonapi\Normalizer\Value\RelationshipItemNormalizerValue;
+ use Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface;
+@@ -67,6 +68,7 @@ class RelationshipItemNormalizer extends FieldItemNormalizer {
+     }
+     $normalizer_value = new RelationshipItemNormalizerValue(
+       $values,
++      new CacheableMetadata(),
+       $relationship_item->getTargetResourceType()
+     );
+ 
+@@ -83,8 +85,8 @@ class RelationshipItemNormalizer extends FieldItemNormalizer {
+       // Add the cacheable dependency of the included item directly to the
+       // response cacheable metadata. This is similar to the flatten include
+       // data structure, instead of a content graph.
+-      if (!empty($context['cacheable_metadata'])) {
+-        $context['cacheable_metadata']->addCacheableDependency($normalizer_value);
++        if (!empty($context['cacheable_metadata'])) {
++          $context['cacheable_metadata']->addCacheableDependency($normalizer_value);
+       }
+     }
+     return $normalizer_value;
+diff --git a/src/Normalizer/Value/FieldItemNormalizerValue.php b/src/Normalizer/Value/FieldItemNormalizerValue.php
+index e242eac..fa6aec5 100644
+--- a/src/Normalizer/Value/FieldItemNormalizerValue.php
++++ b/src/Normalizer/Value/FieldItemNormalizerValue.php
+@@ -1,13 +1,17 @@
+ <?php
+ 
+ namespace Drupal\jsonapi\Normalizer\Value;
++use Drupal\Core\Cache\CacheableDependencyInterface;
++use Drupal\Core\Cache\CacheableDependencyTrait;
+ 
+ /**
+  * Helps normalize field items in compliance with the JSON API spec.
+  *
+  * @internal
+  */
+-class FieldItemNormalizerValue implements ValueExtractorInterface {
++class FieldItemNormalizerValue implements ValueExtractorInterface, CacheableDependencyInterface {
++
++  use CacheableDependencyTrait;
+ 
+   /**
+    * Raw values.
+@@ -28,9 +32,12 @@ class FieldItemNormalizerValue implements ValueExtractorInterface {
+    *
+    * @param array $values
+    *   The normalized result.
++   * @param \Drupal\Core\Cache\CacheableDependencyInterface $values_cacheability
++   *   The cacheability of the normalized result.
+    */
+-  public function __construct(array $values) {
++  public function __construct(array $values, CacheableDependencyInterface $values_cacheability) {
+     $this->raw = $values;
++    $this->setCacheability($values_cacheability);
+   }
+ 
+   /**
+diff --git a/src/Normalizer/Value/FieldNormalizerValue.php b/src/Normalizer/Value/FieldNormalizerValue.php
+index e43e4f2..0e79e9e 100644
+--- a/src/Normalizer/Value/FieldNormalizerValue.php
++++ b/src/Normalizer/Value/FieldNormalizerValue.php
+@@ -2,6 +2,9 @@
+ 
+ namespace Drupal\jsonapi\Normalizer\Value;
+ 
++use Drupal\Core\Cache\CacheableDependencyInterface;
++use Drupal\Core\Cache\CacheableDependencyTrait;
++use Drupal\Core\Cache\CacheableMetadata;
+ use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
+ 
+ /**
+@@ -11,7 +14,8 @@ use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
+  */
+ class FieldNormalizerValue implements FieldNormalizerValueInterface {
+ 
+-  use RefinableCacheableDependencyTrait;
++//  use RefinableCacheableDependencyTrait;
++  use CacheableDependencyTrait;
+ 
+   /**
+    * The values.
+@@ -51,6 +55,11 @@ class FieldNormalizerValue implements FieldNormalizerValueInterface {
+    */
+   public function __construct(array $values, $cardinality) {
+     $this->values = $values;
++    $values_cacheability = new CacheableMetadata();
++    array_walk($this->values, function ($value) use ($values_cacheability) {
++      $values_cacheability->addCacheableDependency($value);
++    });
++    $this->setCacheability($values_cacheability);
+     $this->includes = array_map(function ($value) {
+       if (!$value instanceof FieldItemNormalizerValue) {
+         return new NullFieldNormalizerValue();
+diff --git a/src/Normalizer/Value/FieldNormalizerValueInterface.php b/src/Normalizer/Value/FieldNormalizerValueInterface.php
+index c7ad5d9..1f8b7b9 100644
+--- a/src/Normalizer/Value/FieldNormalizerValueInterface.php
++++ b/src/Normalizer/Value/FieldNormalizerValueInterface.php
+@@ -2,6 +2,7 @@
+ 
+ namespace Drupal\jsonapi\Normalizer\Value;
+ 
++use Drupal\Core\Cache\CacheableDependencyInterface;
+ use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
+ 
+ /**
+@@ -9,7 +10,7 @@ use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
+  *
+  * @internal
+  */
+-interface FieldNormalizerValueInterface extends ValueExtractorInterface, RefinableCacheableDependencyInterface {
++interface FieldNormalizerValueInterface extends ValueExtractorInterface, CacheableDependencyInterface {
+ 
+   /**
+    * Gets the includes.
+diff --git a/src/Normalizer/Value/RelationshipItemNormalizerValue.php b/src/Normalizer/Value/RelationshipItemNormalizerValue.php
+index a1dbe9d..9271d1a 100644
+--- a/src/Normalizer/Value/RelationshipItemNormalizerValue.php
++++ b/src/Normalizer/Value/RelationshipItemNormalizerValue.php
+@@ -2,6 +2,8 @@
+ 
+ namespace Drupal\jsonapi\Normalizer\Value;
+ 
++use Drupal\Core\Cache\CacheableDependencyInterface;
++use Drupal\Core\Cache\CacheableDependencyTrait;
+ use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
+ use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
+ 
+@@ -10,9 +12,11 @@ use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
+  *
+  * @internal
+  */
+-class RelationshipItemNormalizerValue extends FieldItemNormalizerValue implements RefinableCacheableDependencyInterface {
++class RelationshipItemNormalizerValue extends FieldItemNormalizerValue implements CacheableDependencyInterface {
+ 
+-  use RefinableCacheableDependencyTrait;
++//  use RefinableCacheableDependencyTrait;
++
++//  use CacheableDependencyTrait;
+ 
+   /**
+    * Resource path.
+@@ -29,8 +33,8 @@ class RelationshipItemNormalizerValue extends FieldItemNormalizerValue implement
+    * @param string $resource
+    *   The resource type of the target entity.
+    */
+-  public function __construct(array $values, $resource) {
+-    parent::__construct($values);
++  public function __construct(array $values, CacheableDependencyInterface $values_cacheability, $resource) {
++    parent::__construct($values, $values_cacheability);
+     $this->resource = $resource;
+   }
+ 
diff --git a/tests/modules/jsonapi_test_data_type/jsonapi_test_data_type.info.yml b/tests/modules/jsonapi_test_data_type/jsonapi_test_data_type.info.yml
new file mode 100644
index 0000000..e61dde8
--- /dev/null
+++ b/tests/modules/jsonapi_test_data_type/jsonapi_test_data_type.info.yml
@@ -0,0 +1,5 @@
+name: 'JSON API test format-agnostic @DataType normalizers'
+type: module
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/tests/modules/jsonapi_test_data_type/jsonapi_test_data_type.services.yml b/tests/modules/jsonapi_test_data_type/jsonapi_test_data_type.services.yml
new file mode 100644
index 0000000..b8ad331
--- /dev/null
+++ b/tests/modules/jsonapi_test_data_type/jsonapi_test_data_type.services.yml
@@ -0,0 +1,6 @@
+services:
+  serializer.normalizer.string.jsonapi_test_data_type:
+    class: Drupal\jsonapi_test_data_type\Normalizer\StringNormalizer
+    tags:
+      # The priority must be higher than serializer.normalizer.primitive_data.
+      - { name: normalizer , priority: 1000 }
diff --git a/tests/modules/jsonapi_test_data_type/src/Normalizer/StringNormalizer.php b/tests/modules/jsonapi_test_data_type/src/Normalizer/StringNormalizer.php
new file mode 100644
index 0000000..abb6610
--- /dev/null
+++ b/tests/modules/jsonapi_test_data_type/src/Normalizer/StringNormalizer.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\jsonapi_test_data_type\Normalizer;
+
+use Drupal\Core\TypedData\Plugin\DataType\StringData;
+use Drupal\serialization\Normalizer\NormalizerBase;
+
+/**
+ * Normalizes string data, with a twist: it replaces 'super' with 'NOT'.
+ */
+class StringNormalizer extends NormalizerBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = StringData::class;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function normalize($object, $format = NULL, array $context = []) {
+    return str_replace('super', 'NOT', $object->getValue());
+  }
+
+}
diff --git a/tests/modules/jsonapi_test_field_type/jsonapi_test_field_type.info.yml b/tests/modules/jsonapi_test_field_type/jsonapi_test_field_type.info.yml
new file mode 100644
index 0000000..6a73aee
--- /dev/null
+++ b/tests/modules/jsonapi_test_field_type/jsonapi_test_field_type.info.yml
@@ -0,0 +1,5 @@
+name: 'JSON API test format-agnostic @FieldType normalizers'
+type: module
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/tests/modules/jsonapi_test_field_type/jsonapi_test_field_type.services.yml b/tests/modules/jsonapi_test_field_type/jsonapi_test_field_type.services.yml
new file mode 100644
index 0000000..ebb51ed
--- /dev/null
+++ b/tests/modules/jsonapi_test_field_type/jsonapi_test_field_type.services.yml
@@ -0,0 +1,6 @@
+services:
+  serializer.normalizer.string.jsonapi_test_field_type:
+    class: Drupal\jsonapi_test_field_type\Normalizer\StringNormalizer
+    tags:
+      # The priority must be higher than serialization.normalizer.field_item.
+      - { name: normalizer , priority: 1000 }
diff --git a/tests/modules/jsonapi_test_field_type/src/Normalizer/StringNormalizer.php b/tests/modules/jsonapi_test_field_type/src/Normalizer/StringNormalizer.php
new file mode 100644
index 0000000..0376ca6
--- /dev/null
+++ b/tests/modules/jsonapi_test_field_type/src/Normalizer/StringNormalizer.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\jsonapi_test_field_type\Normalizer;
+
+use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
+use Drupal\serialization\Normalizer\FieldItemNormalizer;
+
+/**
+ * Normalizes string fields, with a twist: it replaces 'super' with 'NOT'.
+ */
+class StringNormalizer extends FieldItemNormalizer {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = StringItem::class;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function normalize($object, $format = NULL, array $context = []) {
+    $data = parent::normalize($object, $format, $context);
+    $data['value'] = str_replace('super', 'NOT', $data['value']);
+    return $data;
+  }
+
+}
diff --git a/tests/src/Functional/ExternalNormalizersTest.php b/tests/src/Functional/ExternalNormalizersTest.php
new file mode 100644
index 0000000..8f4f21a
--- /dev/null
+++ b/tests/src/Functional/ExternalNormalizersTest.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace Drupal\Tests\jsonapi\Functional;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Url;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\Tests\BrowserTestBase;
+use Drupal\user\Entity\Role;
+use Drupal\user\RoleInterface;
+
+/**
+ * Asserts external normalizers are handled as expected by the JSON API module.
+ *
+ * @see jsonapi.normalizers
+ *
+ * @group jsonapi
+ */
+class ExternalNormalizersTest extends BrowserTestBase {
+
+  /**
+   * The original value for the test field.
+   *
+   * @var string
+   */
+  const VALUE_ORIGINAL = 'Llamas are super awesome!';
+
+  /**
+   * The expected overridden value for the test field.
+   *
+   * @see \Drupal\jsonapi_test_field_type\Normalizer\StringNormalizer
+   * @see \Drupal\jsonapi_test_data_type\Normalizer\StringNormalizer
+   */
+  const VALUE_OVERRIDDEN = 'Llamas are NOT awesome!';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'jsonapi',
+    'entity_test',
+  ];
+
+  /**
+   * The test entity.
+   *
+   * @var \Drupal\entity_test\Entity\EntityTest
+   */
+  protected $entity;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // This test is not about access control at all, so allow anonymous users to
+    // view the test entities.
+    Role::load(RoleInterface::ANONYMOUS_ID)
+      ->grantPermission('view test entity')
+      ->save();
+
+    FieldStorageConfig::create([
+      'field_name' => 'field_test',
+      'type' => 'string',
+      'entity_type' => 'entity_test',
+    ])
+      ->save();
+    FieldConfig::create([
+      'field_name' => 'field_test',
+      'entity_type' => 'entity_test',
+      'bundle' => 'entity_test',
+    ])
+      ->save();
+
+    $this->entity = EntityTest::create([
+      'name' => 'Llama',
+      'type' => 'entity_test',
+      'field_test' => static::VALUE_ORIGINAL,
+    ]);
+    $this->entity->save();
+  }
+
+  /**
+   * Tests a format-agnostic normalizer.
+   *
+   * @param string $test_module
+   *   The test module to install, which comes with a high-priority normalizer.
+   * @param string $expected_value_jsonapi_normalization
+   *   The expected JSON API normalization of the tested field. Must be either
+   *   - static::VALUE_ORIGINAL (normalizer IS NOT expected to override)
+   *   - static::VALUE_OVERRIDDEN (normalizer IS expected to override)
+   *
+   * @dataProvider providerTestFormatAgnosticNormalizers
+   */
+  public function testFormatAgnosticNormalizers($test_module, $expected_value_jsonapi_normalization) {
+    assert(in_array($expected_value_jsonapi_normalization, [static::VALUE_ORIGINAL, static::VALUE_OVERRIDDEN], TRUE));
+
+    // Asserts the entity contains the value we set.
+    $this->assertSame(static::VALUE_ORIGINAL, $this->entity->field_test->value);
+
+    // Asserts normalizing the entity using core's 'serializer' service DOES
+    // yield the value we set.
+    $core_normalization = $this->container->get('serializer')->normalize($this->entity);
+    $this->assertSame(static::VALUE_ORIGINAL, $core_normalization['field_test'][0]['value']);
+
+    // Install test module that contains a high-priority alternative normalizer.
+    $this->container->get('module_installer')->install([$test_module]);
+    $this->rebuildContainer();
+
+    // Asserts normalizing the entity using core's 'serializer' service DOES NOT
+    // ANYMORE yield the value we set.
+    $core_normalization = $this->container->get('serializer')->normalize($this->entity);
+    $this->assertSame(static::VALUE_OVERRIDDEN, $core_normalization['field_test'][0]['value']);
+
+    // Asserts that this does NOT affect the JSON API normalization.
+    // @todo Remove line below in favor of commented line in https://www.drupal.org/project/jsonapi/issues/2878463.
+    $url = Url::fromRoute('jsonapi.entity_test--entity_test.individual', ['entity_test' => $this->entity->uuid()]);
+    /* $url = $this->entity->toUrl('jsonapi'); */
+    $client = $this->getSession()->getDriver()->getClient()->getClient();
+    $response = $client->request('GET', $url->setAbsolute(TRUE)->toString());
+    $document = Json::decode((string) $response->getBody());
+    $this->assertSame($expected_value_jsonapi_normalization, $document['data']['attributes']['field_test']);
+  }
+
+  /**
+   * Data provider.
+   *
+   * @return array
+   *   Test cases.
+   */
+  public function providerTestFormatAgnosticNormalizers() {
+    return [
+      'Format-agnostic @FieldType-level normalizers SHOULD NOT be able to affect the JSON API normalization' => [
+        // \Drupal\jsonapi_test_field_type\Normalizer\StringNormalizer::normalize()
+        'jsonapi_test_field_type',
+        static::VALUE_ORIGINAL,
+      ],
+      'Format-agnostic @DataType-level normalizers SHOULD be able to affect the JSON API normalization' => [
+        // \Drupal\jsonapi_test_data_type\Normalizer\StringNormalizer::normalize()
+        'jsonapi_test_data_type',
+        static::VALUE_OVERRIDDEN,
+      ],
+    ];
+  }
+
+}
diff --git a/yar b/yar
new file mode 100644
index 0000000..9adf766
--- /dev/null
+++ b/yar
@@ -0,0 +1,130 @@
+ src/EntityToJsonApi.php                              |  6 +++---
+ src/ResourceType/ResourceType.php                    | 10 ++++++----
+ src/ResourceType/ResourceTypeRepository.php          |  4 ++--
+ src/ResourceType/ResourceTypeRepositoryInterface.php |  8 +++++---
+ src/Routing/Routes.php                               | 10 +++++-----
+ tests/src/Unit/Routing/RoutesTest.php                |  2 +-
+ 6 files changed, 22 insertions(+), 18 deletions(-)
+
+diff --git a/src/EntityToJsonApi.php b/src/EntityToJsonApi.php
+index 30bcd3f..99ba536 100644
+--- a/src/EntityToJsonApi.php
++++ b/src/EntityToJsonApi.php
+@@ -98,13 +98,13 @@ class EntityToJsonApi {
+    */
+   protected function calculateContext(EntityInterface $entity) {
+     // TODO: Supporting includes requires adding the 'include' query string.
+-    $path_prefix = $this->resourceTypeRepository->getPathPrefix();
++    $path_prefix = $this->resourceTypeRepository->getPrefixPathSegment();
+     $resource_type = $this->resourceTypeRepository->get(
+       $entity->getEntityTypeId(),
+       $entity->bundle()
+     );
+-    $resource_path = $resource_type->getPath();
+-    $path = sprintf('/%s/%s/%s', $path_prefix, $resource_path, $entity->uuid());
++    $resource_path = $resource_type->getPathSegments();
++    $path = sprintf('%s%s/%s', $path_prefix, $resource_path, $entity->uuid());
+     $request = Request::create($path, 'GET');
+     return [
+       'account' => $this->currentUser,
+diff --git a/src/ResourceType/ResourceType.php b/src/ResourceType/ResourceType.php
+index f2257dd..ff06954 100644
+--- a/src/ResourceType/ResourceType.php
++++ b/src/ResourceType/ResourceType.php
+@@ -244,13 +244,15 @@ class ResourceType {
+   }
+ 
+   /**
+-   * Get the resource path.
++   * Gets the resource type path segments.
+    *
+    * @return string
+-   *   The path to access this resource type. Defaults to entity_type_id/bundle.
++   *   The path segments to access this resource type, without path prefix.
++   *
++   * @see \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface::getPrefixPathSegment()
+    */
+-  public function getPath() {
+-    return sprintf('%s/%s', $this->getEntityTypeId(), $this->getBundle());
++  public function getPathSegments() {
++    return sprintf('/%s/%s', $this->getEntityTypeId(), $this->getBundle());
+   }
+ 
+ }
+diff --git a/src/ResourceType/ResourceTypeRepository.php b/src/ResourceType/ResourceTypeRepository.php
+index c536925..7ead907 100644
+--- a/src/ResourceType/ResourceTypeRepository.php
++++ b/src/ResourceType/ResourceTypeRepository.php
+@@ -76,8 +76,8 @@ class ResourceTypeRepository implements ResourceTypeRepositoryInterface {
+   /**
+    * {@inheritdoc}
+    */
+-  public function getPathPrefix() {
+-    return 'jsonapi';
++  public function getPrefixPathSegment() {
++    return '/jsonapi';
+   }
+ 
+   // @codingStandardsIgnoreStart
+diff --git a/src/ResourceType/ResourceTypeRepositoryInterface.php b/src/ResourceType/ResourceTypeRepositoryInterface.php
+index b44f3d6..7d33016 100644
+--- a/src/ResourceType/ResourceTypeRepositoryInterface.php
++++ b/src/ResourceType/ResourceTypeRepositoryInterface.php
+@@ -42,11 +42,13 @@ interface ResourceTypeRepositoryInterface {
+   public function getByTypeName($type_name);
+ 
+   /**
+-   * Gets the path prefix for routes managed by JSON API.
++   * Gets the JSON API route prefix path segment.
+    *
+    * @return string
+-   *   The route prefix in the JSON API route paths.
++   *   The prefix path segment in the JSON API route paths.
++   *
++   * @see \Drupal\jsonapi\ResourceType\ResourceType::getPathSegments()
+    */
+-  public function getPathPrefix();
++  public function getPrefixPathSegment();
+ 
+ }
+diff --git a/src/Routing/Routes.php b/src/Routing/Routes.php
+index 66d7537..7c3c5f7 100644
+--- a/src/Routing/Routes.php
++++ b/src/Routing/Routes.php
+@@ -80,8 +80,8 @@ class Routes implements ContainerInjectionInterface {
+   public function entryPoint() {
+     $collection = new RouteCollection();
+ 
+-    $path_prefix = $this->resourceTypeRepository->getPathPrefix();
+-    $route_collection = (new Route('/' . $path_prefix, [
++    $path_prefix = $this->resourceTypeRepository->getPrefixPathSegment();
++    $route_collection = (new Route($path_prefix, [
+       RouteObjectInterface::CONTROLLER_NAME => '\Drupal\jsonapi\Controller\EntryPoint::index',
+     ]))
+       ->setRequirement('_permission', 'access jsonapi resource list')
+@@ -105,9 +105,9 @@ class Routes implements ContainerInjectionInterface {
+         continue;
+       }
+ 
+-      $path_prefix = $this->resourceTypeRepository->getPathPrefix();
+-      $resource_path = $resource_type->getPath();
+-      $route_base_path = sprintf('/%s/%s', $path_prefix, $resource_path);
++      $path_prefix = $this->resourceTypeRepository->getPrefixPathSegment();
++      $resource_path = $resource_type->getPathSegments();
++      $route_base_path = sprintf('%s%s', $path_prefix, $resource_path);
+       $build_route_name = function ($key) use ($resource_type) {
+         return sprintf('jsonapi.%s.%s', $resource_type->getTypeName(), $key);
+       };
+diff --git a/tests/src/Unit/Routing/RoutesTest.php b/tests/src/Unit/Routing/RoutesTest.php
+index 67f3c1a..de3c6ed 100644
+--- a/tests/src/Unit/Routing/RoutesTest.php
++++ b/tests/src/Unit/Routing/RoutesTest.php
+@@ -47,7 +47,7 @@ class RoutesTest extends UnitTestCase {
+     $type_3->setRelatableResourceTypes([]);
+     $resource_type_repository = $this->prophesize(ResourceTypeRepository::class);
+     $resource_type_repository->all()->willReturn([$type_1, $type_2, $type_3]);
+-    $resource_type_repository->getPathPrefix()->willReturn('jsonapi');
++    $resource_type_repository->getPrefixPathSegment()->willReturn('/jsonapi');
+     $container = $this->prophesize(ContainerInterface::class);
+     $container->get('jsonapi.resource_type.repository')->willReturn($resource_type_repository->reveal());
+     $auth_collector = $this->prophesize(AuthenticationCollectorInterface::class);
