 config/install/jsonapi.resource_info.yml           |   1 -
 config/schema/jsonapi.schema.yml                   |   3 -
 jsonapi.services.yml                               |   7 +-
 ...JsonApiResource.php => JsonApiResourceType.php} |  33 ++--
 src/Configuration/ResourceConfig.php               | 196 ---------------------
 src/Configuration/ResourceConfigInterface.php      | 127 -------------
 src/Configuration/ResourceManagerInterface.php     |  56 ------
 src/EntityCollection.php                           |   4 +-
 src/EntityCollectionInterface.php                  |   5 +-
 .../Deriver/EntityDeriver.php}                     | 152 +++++++---------
 src/Plugin/Deriver/ResourceDeriver.php             |  82 ---------
 src/Plugin/JsonApiResourceInterface.php            |  14 --
 .../JsonApiResourceTypeInterface.php}              |  86 ++++-----
 ...eManager.php => JsonApiResourceTypeManager.php} |  29 ++-
 src/Plugin/jsonapi/BundleJsonApiResource.php       |  24 ---
 .../resource_type/EntityJsonApiResourceType.php}   |  56 +++---
 src/Routing/Routes.php                             | 114 ++++++------
 17 files changed, 228 insertions(+), 761 deletions(-)

diff --git a/config/install/jsonapi.resource_info.yml b/config/install/jsonapi.resource_info.yml
index b17ff5a..54fbb44 100644
--- a/config/install/jsonapi.resource_info.yml
+++ b/config/install/jsonapi.resource_info.yml
@@ -1,2 +1 @@
-prefix: api
 id_field: uuid
diff --git a/config/schema/jsonapi.schema.yml b/config/schema/jsonapi.schema.yml
index e7ddc88..3238540 100644
--- a/config/schema/jsonapi.schema.yml
+++ b/config/schema/jsonapi.schema.yml
@@ -1,9 +1,6 @@
 jsonapi.resource_info:
   type: config_object
   mapping:
-    prefix:
-      type: string
-      label: 'Prefix'
     id_field:
       type: string
       label: 'API field'
diff --git a/jsonapi.services.yml b/jsonapi.services.yml
index 99f5075..5f88434 100644
--- a/jsonapi.services.yml
+++ b/jsonapi.services.yml
@@ -50,9 +50,6 @@ services:
     class: Drupal\jsonapi\Encoder\JsonEncoder
     tags:
       - { name: encoder, priority: 21, format: 'api_json' }
-  jsonapi.resource.manager:
-    class: Drupal\jsonapi\Configuration\ResourceManager
-    arguments: ['@entity_type.manager', '@entity_type.bundle.info', '@config.factory', '@module_handler']
   jsonapi.route_enhancer:
     class: Drupal\jsonapi\Routing\RouteEnhancer
     tags:
@@ -85,8 +82,8 @@ services:
     arguments: ['@entity.manager']
   jsonapi.error_handler:
     class: Drupal\jsonapi\Error\ErrorHandler
-  plugin.manager.resource.processor:
-    class: Drupal\jsonapi\Plugin\JsonApiResourceManager
+  plugin.manager.jsonapi.resource_type:
+    class: Drupal\jsonapi\Plugin\JsonApiResourceTypeManager
     parent: default_plugin_manager
   jsonapi.exception_subscriber:
     class: Drupal\jsonapi\EventSubscriber\DefaultExceptionSubscriber
diff --git a/src/Annotation/JsonApiResource.php b/src/Annotation/JsonApiResourceType.php
similarity index 45%
rename from src/Annotation/JsonApiResource.php
rename to src/Annotation/JsonApiResourceType.php
index 277de71..a648df2 100644
--- a/src/Annotation/JsonApiResource.php
+++ b/src/Annotation/JsonApiResourceType.php
@@ -5,14 +5,19 @@ namespace Drupal\jsonapi\Annotation;
 use Drupal\Component\Annotation\Plugin;
 
 /**
- * Defines a JSON API Resource item annotation object.
+ * Defines a JSON API resource 'type' annotation object.
  *
- * @see \Drupal\jsonapi\Plugin\JsonApiResourceManager
+ * Plugin Namespace: Plugin\jsonapi\resource_type
+ *
+ * @see \Drupal\jsonapi\Plugin\JsonApiResourceTypeManager
+ * @see \Drupal\jsonapi\Plugin\JsonApiResourceTypeInterface
  * @see plugin_api
  *
+ * @see http://jsonapi.org/format/#document-resource-identifier-objects — 'type'
+ *
  * @Annotation
  */
-class JsonApiResource extends Plugin {
+class JsonApiResourceType extends Plugin {
 
   /**
    * The plugin ID.
@@ -31,31 +36,17 @@ class JsonApiResource extends Plugin {
   public $label;
 
   /**
-   * The entity type ID.
+   * The JSON API resource type.
    *
    * @var string
    */
-  public $entityType;
+  public $type;
 
   /**
-   * The bundle ID.
+   * The serialization class to deserialize serialized data into.
    *
    * @var string
    */
-  public $bundle;
-
-  /**
-   * Information about the data resources.
-   *
-   * @var array
-   */
-  public $data;
-
-  /**
-   * TRUE if the plugin is enabled.
-   *
-   * @var bool
-   */
-  public $enabled;
+  public $serialization_class;
 
 }
diff --git a/src/Configuration/ResourceConfig.php b/src/Configuration/ResourceConfig.php
deleted file mode 100644
index d3b9f55..0000000
--- a/src/Configuration/ResourceConfig.php
+++ /dev/null
@@ -1,196 +0,0 @@
-<?php
-
-namespace Drupal\jsonapi\Configuration;
-
-use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-
-/**
- * Class ResourceConfig.
- *
- * This object contains all the information needed to generate all the routes
- * associated with a JSON API type. In the future this is going to be
- * constructed (maybe?) from a configuration entity.
- *
- * @package Drupal\jsonapi\Configuration
- */
-class ResourceConfig implements ResourceConfigInterface {
-
-  /**
-   * Holds the configuration that is global to all the JSON API types.
-   *
-   * @var object
-   */
-  protected $globalConfig;
-
-  /**
-   * Holds the entity type manager.
-   *
-   * @var EntityTypeManagerInterface
-   */
-  protected $entityTypeManager;
-
-  /**
-   * The entity type ID.
-   *
-   * @var string
-   */
-  protected $entityTypeId;
-
-  /**
-   * The bundle ID.
-   *
-   * @var string
-   */
-  protected $bundleId;
-
-  /**
-   * The type name.
-   *
-   * @var string
-   */
-  protected $typeName;
-
-  /**
-   * The base resource path.
-   *
-   * @var string
-   */
-  protected $path;
-
-  /**
-   * The class to which a payload converts to.
-   *
-   * @var string
-   */
-  protected $deserializationTargetClass;
-
-  /**
-   * Is enabled?
-   *
-   * @var bool
-   */
-  protected $isEnabled = TRUE;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getGlobalConfig() {
-    return $this->globalConfig;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getEntityTypeId() {
-    return $this->entityTypeId;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setEntityTypeId($entity_type_id) {
-    $this->entityTypeId = $entity_type_id;
-    $this->deserializationTargetClass = $this->entityTypeManager
-      ->getDefinition($entity_type_id)
-      ->getClass();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getTypeName() {
-    return $this->typeName;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setTypeName($type_name) {
-    $this->typeName = $type_name;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPath() {
-    return $this->path;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setPath($path) {
-    $this->path = $path;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getBundleId() {
-    return $this->bundleId;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setBundleId($bundle_id) {
-    $this->bundleId = $bundle_id;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getStorage() {
-    return $this->entityTypeManager->getStorage($this->entityTypeId);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDeserializationTargetClass() {
-    return $this->deserializationTargetClass;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getIdKey() {
-    return $this->getGlobalConfig()->get('id_field');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isEnabled() {
-    return $this->isEnabled;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function enable() {
-    $this->isEnabled = TRUE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function disable() {
-    $this->isEnabled = FALSE;
-  }
-
-  /**
-   * Instantiates a ResourceConfig object.
-   *
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The configuration factory.
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity type manager.
-   */
-  public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager) {
-    $this->globalConfig = $config_factory->get('jsonapi.resource_info');
-    $this->entityTypeManager = $entity_type_manager;
-  }
-
-}
diff --git a/src/Configuration/ResourceConfigInterface.php b/src/Configuration/ResourceConfigInterface.php
deleted file mode 100644
index 95af84c..0000000
--- a/src/Configuration/ResourceConfigInterface.php
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-
-
-namespace Drupal\jsonapi\Configuration;
-
-/**
- * Class ResourceConfigInterface.
- *
- * @package Drupal\jsonapi\Configuration
- */
-interface ResourceConfigInterface {
-
-  /**
-   * Gets the entity type id.
-   *
-   * @return string
-   *   The entity type id.
-   */
-  public function getEntityTypeId();
-
-  /**
-   * Sets the entity type id.
-   *
-   * @param string $entity_type_id
-   *   The entityTypeId to set.
-   */
-  public function setEntityTypeId($entity_type_id);
-
-  /**
-   * Gets the type name.
-   *
-   * @return string
-   *   The type name.
-   */
-  public function getTypeName();
-
-  /**
-   * Sets the type name.
-   *
-   * @param string $type_name
-   *   The type name to set.
-   */
-  public function setTypeName($type_name);
-
-  /**
-   * Gets the path.
-   *
-   * @return string
-   *   The path.
-   */
-  public function getPath();
-
-  /**
-   * Sets the path.
-   *
-   * @param string $path
-   *   The path to set.
-   */
-  public function setPath($path);
-
-  /**
-   * Gets the bundle ID.
-   *
-   * @return string
-   *   The bundleId.
-   */
-  public function getBundleId();
-
-  /**
-   * Sets the bundle ID.
-   *
-   * @param string $bundle_id
-   *   The bundleId to set.
-   */
-  public function setBundleId($bundle_id);
-
-  /**
-   * Gets the global configuration.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The global configuration.
-   */
-  public function getGlobalConfig();
-
-  /**
-   * Gets the underlying entity storage for the resource.
-   *
-   * @return \Drupal\Core\Entity\EntityStorageInterface
-   *   The appropriate entity storage interface.
-   */
-  public function getStorage();
-
-  /**
-   * Gets the deserialization target class.
-   *
-   * @return string
-   *   The deserialization target class.
-   */
-  public function getDeserializationTargetClass();
-
-  /**
-   * Get the entity key used for the ID.
-   *
-   * @return string
-   *   The key.
-   */
-  public function getIdKey();
-
-  /**
-   * Is the resource enabled?
-   *
-   * @return bool
-   *   TRUE if the resource is enabled. FALSE otherwise.
-   */
-  public function isEnabled();
-
-  /**
-   * Enable the resource.
-   */
-  public function enable();
-
-  /**
-   * Disable the resource.
-   */
-  public function disable();
-
-}
diff --git a/src/Configuration/ResourceManagerInterface.php b/src/Configuration/ResourceManagerInterface.php
deleted file mode 100644
index 74d1737..0000000
--- a/src/Configuration/ResourceManagerInterface.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-
-namespace Drupal\jsonapi\Configuration;
-
-/**
- * Class ResourceManagerInterface.
- *
- * @package Drupal\jsonapi
- */
-interface ResourceManagerInterface {
-
-  /**
-   * Get all the resource configuration objects.
-   *
-   * @param bool $include_disabled
-   *   TRUE to return disabled resources as well.
-   *
-   * @return ResourceConfigInterface[]
-   *   The list of resource configs representing JSON API types.
-   */
-  public function all($include_disabled = FALSE);
-
-  /**
-   * Finds a resource config.
-   *
-   * @param string $entity_type_id
-   *   The entity type id.
-   * @param string $bundle_id
-   *   The id for the bundle to find.
-   *
-   * @return ResourceConfigInterface
-   *   The resource config found. NULL if none was found.
-   */
-  public function get($entity_type_id, $bundle_id);
-
-  /**
-   * Get the entity type manager.
-   *
-   * @return \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity type manager.
-   */
-  public function getEntityTypeManager();
-
-  /**
-   * Entity type has a bundle.
-   *
-   * @param string $entity_type_id
-   *   The entity type ID.
-   *
-   * @return bool
-   *   TRUE if the provided entity type has a bundle.
-   */
-  public function hasBundle($entity_type_id);
-
-}
diff --git a/src/EntityCollection.php b/src/EntityCollection.php
index 88223fa..917a300 100644
--- a/src/EntityCollection.php
+++ b/src/EntityCollection.php
@@ -3,9 +3,7 @@
 namespace Drupal\jsonapi;
 
 /**
- * Class EntityCollection.
- *
- * @package Drupal\jsonapi
+ * @internal
  */
 class EntityCollection implements EntityCollectionInterface {
 
diff --git a/src/EntityCollectionInterface.php b/src/EntityCollectionInterface.php
index 2069cc7..d7b1a8f 100644
--- a/src/EntityCollectionInterface.php
+++ b/src/EntityCollectionInterface.php
@@ -4,9 +4,8 @@
 namespace Drupal\jsonapi;
 
 /**
- * Class EntityCollectionInterface.
- *
- * @package Drupal\jsonapi
+ * @internal
+ * @todo Make this non-entity specific.
  */
 interface EntityCollectionInterface extends \IteratorAggregate, \Countable {
 
diff --git a/src/Configuration/ResourceManager.php b/src/Plugin/Deriver/EntityDeriver.php
similarity index 22%
rename from src/Configuration/ResourceManager.php
rename to src/Plugin/Deriver/EntityDeriver.php
index 106efc1..552bc91 100644
--- a/src/Configuration/ResourceManager.php
+++ b/src/Plugin/Deriver/EntityDeriver.php
@@ -1,33 +1,34 @@
 <?php
 
-namespace Drupal\jsonapi\Configuration;
+namespace Drupal\jsonapi\Plugin\Deriver;
 
-use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Component\Plugin\Derivative\DeriverBase;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\jsonapi\Configuration\ResourceManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * Class ResourceManager.
+ * Provides a resource plugin definition for every entity type and bundle.
  *
- * @package Drupal\jsonapi
+ * @see \Drupal\jsonapi\Plugin\jsonapi\resource_type\EntityJsonApiResourceType
  */
-class ResourceManager implements ResourceManagerInterface {
+class EntityDeriver extends DeriverBase implements ContainerDeriverInterface {
 
   /**
-   * The entity type manager.
+   * List of derivative definitions.
    *
-   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   * @var array
    */
-  protected $entityTypeManager;
+  protected $derivatives;
 
   /**
-   * The configuration factory.
+   * The entity type manager.
    *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
    */
-  protected $configFactory;
+  protected $entityTypeManager;
 
   /**
    * The bundle manager.
@@ -37,98 +38,81 @@ class ResourceManager implements ResourceManagerInterface {
   protected $bundleManager;
 
   /**
-   * The module handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
-   * The loaded resource config objects.
-   *
-   * @var \Drupal\jsonapi\Configuration\ResourceConfigInterface[]
-   */
-  protected $all = [];
-
-  /**
-   * Instantiates a ResourceManager object.
+   * Constructs an EntityDeriver object.
    *
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
    *   The entity type manager.
    * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_manager
    *   The bundle manager.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The config factory interface.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   The module handler service.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_manager, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_manager) {
     $this->entityTypeManager = $entity_type_manager;
     $this->bundleManager = $bundle_manager;
-    $this->configFactory = $config_factory;
-    $this->moduleHandler = $module_handler;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function all($include_disabled = FALSE) {
-    if (!$this->all) {
-      $entity_type_ids = array_keys($this->entityTypeManager->getDefinitions());
-      foreach ($entity_type_ids as $entity_type_id) {
-        // Add a ResourceConfig per bundle.
-        $this->all = array_merge($this->all, array_map(function ($bundle) use ($entity_type_id) {
-          $resource_config = new ResourceConfig($this->configFactory, $this->entityTypeManager);
-          $resource_config->setEntityTypeId($entity_type_id);
-          $resource_config->setBundleId($bundle);
-          $resource_config->setPath(sprintf('/%s/%s', $entity_type_id, $bundle));
-          $resource_config->setTypeName(sprintf('%s--%s', $entity_type_id, $bundle));
-          return $resource_config;
-        }, array_keys($this->bundleManager->getBundleInfo($entity_type_id))));
-      }
-      // Allow altering the resource configuration. This is used, among other, to
-      // disable resources.
-      $this->moduleHandler->alter('jsonapi_resources', $this->all);
-    }
-    // Filter out the disabled resources if necessary.
-    return $include_disabled ?
-      $this->all :
-      array_filter($this->all, function ($resource) {
-        /* @var \Drupal\jsonapi\Configuration\ResourceConfigInterface $resource */
-        return $resource->isEnabled();
-      });
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('entity_type.bundle.info')
+    );
   }
 
   /**
    * {@inheritdoc}
    */
-  public function get($entity_type_id, $bundle_id) {
-    if (empty($entity_type_id)) {
-      throw new PreconditionFailedHttpException('Server error. The current route is malformed.');
-    }
-    foreach ($this->all(TRUE) as $resource) {
-      if ($resource->getEntityTypeId() == $entity_type_id && $resource->getBundleId() == $bundle_id) {
-        return $resource;
-      }
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    if (isset($this->derivatives)) {
+      return $this->derivatives;
     }
-    return NULL;
-  }
+    $this->derivatives = [];
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getEntityTypeManager() {
-    return $this->entityTypeManager;
-  }
+    $entity_types = $this->entityTypeManager->getDefinitions();
+    foreach ($entity_types as $entity_type_id => $entity_type) {
+      $plugin_definition = [
+        'serialization_class' => $entity_type->getClass(),
+        'route_options' => [
+          'parameters' => [
+            $entity_type_id => [
+              'type' => 'entity:' . $entity_type_id,
+            ],
+          ],
+        ],
+        'route_path_part_for_individual_resource' => $entity_type_id,
+      ] + $base_plugin_definition;
 
-  /**
-   * {@inheritdoc}
-   */
-  public function hasBundle($entity_type_id) {
-    return (bool) $this->getEntityTypeManager()
-      ->getDefinition($entity_type_id)
-      ->getBundleEntityType();
-  }
+      // First derive a JSON API Resource Type for this entity type (this covers
+      // all bundles).
+      $id = sprintf('%s', $entity_type_id);
+      $this->derivatives[$id] = [
+        'id' => $id,
+        'type_hierarchy' => [$entity_type_id],
+        'route_requirements' => [
+          '_entity_type' => $entity_type_id,
+        ],
+      ] + $plugin_definition;
 
+      // Now derive an additional JSON API Resource Type for every bundle that
+      // exists for this entity type.
+      // @todo ensure new bundles are picked up immediately, see \Drupal\Core\Entity\EntityTypeBundleInfo::clearCachedBundles().
+      if ($entity_type->getBundleEntityType() !== NULL) {
+        $bundles = array_keys($this->bundleManager->getBundleInfo($entity_type_id));
+        foreach ($bundles as $bundle) {
+          $id = sprintf('%s.%s', $entity_type_id, $bundle);
+          $this->derivatives[$id] = [
+            'id' => $id,
+            'type_hierarchy' => [$entity_type_id, $bundle],
+            'route_requirements' => [
+              '_entity_type' => $entity_type_id,
+              '_bundle' => $bundle,
+            ],
+          ] + $plugin_definition;
+        }
+      }
+    }
+    return $this->derivatives;
+  }
 
 }
diff --git a/src/Plugin/Deriver/ResourceDeriver.php b/src/Plugin/Deriver/ResourceDeriver.php
deleted file mode 100644
index 3048b62..0000000
--- a/src/Plugin/Deriver/ResourceDeriver.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-namespace Drupal\jsonapi\Plugin\Deriver;
-
-use Drupal\Component\Plugin\Derivative\DeriverBase;
-use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
-use Drupal\jsonapi\Configuration\ResourceManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a resource plugin definition for every entity type's bundle.
- *
- * @see \Drupal\jsonapi\Plugin\jsonapi\resource\BundleResource
- */
-class ResourceDeriver extends DeriverBase implements ContainerDeriverInterface {
-
-  /**
-   * List of derivative definitions.
-   *
-   * @var array
-   */
-  protected $derivatives;
-
-  /**
-   * The resource manager.
-   *
-   * @var \Drupal\jsonapi\Configuration\ResourceManagerInterface
-   */
-  protected $resourceManager;
-
-  /**
-   * Constructs an ResourceDeriver object.
-   *
-   * @param \Drupal\jsonapi\Configuration\ResourceManagerInterface $resource_manager
-   *   The entity manager.
-   */
-  public function __construct(ResourceManagerInterface $resource_manager) {
-    $this->resourceManager = $resource_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, $base_plugin_id) {
-    /* @var \Drupal\jsonapi\Configuration\ResourceManagerInterface $resource_manager */
-    $resource_manager = $container->get('jsonapi.resource.manager');
-    return new static($resource_manager);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDerivativeDefinitions($base_definition) {
-    if (isset($this->derivatives)) {
-      return $this->derivatives;
-    }
-    $this->derivatives = [];
-    // Add in the default plugin configuration and the resource type.
-    /* @var \Drupal\jsonapi\Configuration\ResourceConfigInterface[] $resource_configs */
-    $resource_configs = $this->resourceManager->all();
-    foreach ($resource_configs as $resource) {
-      $global_config = $resource->getGlobalConfig();
-      $prefix = $global_config->get('prefix');
-      $id = sprintf('%s.dynamic.%s', $prefix, $resource->getTypeName());
-      $this->derivatives[$id] = [
-        'id' => $id,
-        'entityType' => $resource->getEntityTypeId(),
-        'bundle' => $resource->getBundleId(),
-        'hasBundle' => $this->resourceManager->hasBundle($resource->getEntityTypeId()),
-        'type' => $resource->getTypeName(),
-        'data' => [
-          'prefix' => $prefix,
-          'partialPath' => '/' . $prefix . $resource->getPath()
-        ]
-      ];
-
-      $this->derivatives[$id] += $base_definition;
-    }
-    return $this->derivatives;
-  }
-
-}
diff --git a/src/Plugin/JsonApiResourceInterface.php b/src/Plugin/JsonApiResourceInterface.php
deleted file mode 100644
index 560b703..0000000
--- a/src/Plugin/JsonApiResourceInterface.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Drupal\jsonapi\Plugin;
-
-use Drupal\Component\Plugin\PluginInspectionInterface;
-
-/**
- * Defines an interface for JSON API Resource plugins.
- */
-interface JsonApiResourceInterface extends PluginInspectionInterface {
-
-  // Add get/set methods for your plugin type here.
-
-}
diff --git a/src/Resource/EntityResourceInterface.php b/src/Plugin/JsonApiResourceTypeInterface.php
similarity index 55%
rename from src/Resource/EntityResourceInterface.php
rename to src/Plugin/JsonApiResourceTypeInterface.php
index 4c42bd4..965bb0d 100644
--- a/src/Resource/EntityResourceInterface.php
+++ b/src/Plugin/JsonApiResourceTypeInterface.php
@@ -1,23 +1,23 @@
 <?php
 
+namespace Drupal\jsonapi\Plugin;
 
-namespace Drupal\jsonapi\Resource;
-
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Component\Plugin\PluginInspectionInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
- * Class EntityResourceInterface.
- *
- * @package Drupal\jsonapi\Resource
+ * Defines an interface for JSON API Resource Type plugins.
  */
-interface EntityResourceInterface {
+interface JsonApiResourceTypeInterface extends PluginInspectionInterface {
+
+  public function getDeserializationTargetClass();
+
 
   /**
-   * Gets the individual entity.
+   * Gets an individual resource object of this resource type.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The loaded entity.
+   * @param $object
+   *   A loaded resource object of this type, to return.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The request object.
    * @param int $response_code
@@ -26,51 +26,51 @@ interface EntityResourceInterface {
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function getIndividual(EntityInterface $entity, Request $request, $response_code = 200);
+  public function getIndividual($object, Request $request, $response_code = 200);
 
   /**
-   * Creates an individual entity.
+   * Creates an individual resource object of this resource type.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The loaded entity.
+   * @param $object
+   *   A loaded resource object of this type, to create.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The request object.
    *
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function createIndividual(EntityInterface $entity, Request $request);
+  public function createIndividual($object, Request $request);
 
   /**
-   * Patches an individual entity.
+   * Patches an individual resource object of this resource type.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The loaded entity.
-   * @param \Drupal\Core\Entity\EntityInterface $parsed_entity
-   *   The entity with the new data.
+   * @param $object
+   *   A loaded resource object of this type, to patch.
+   * @param $received_object
+   *   A loaded resource object of this type, to patch with.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The request object.
    *
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function patchIndividual(EntityInterface $entity, EntityInterface $parsed_entity, Request $request);
+  public function patchIndividual($object, $received_object, Request $request);
 
   /**
-   * Deletes an individual entity.
+   * Deletes an individual resource object of this resource type.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The loaded entity.
+   * @param $object
+   *   A loaded resource object of this type, to delete.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The request object.
    *
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function deleteIndividual(EntityInterface $entity, Request $request);
+  public function deleteIndividual($object, Request $request);
 
   /**
-   * Gets the collection of entities.
+   * Gets the collection of resource objects of this resource type.
    *
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The request object.
@@ -83,8 +83,8 @@ interface EntityResourceInterface {
   /**
    * Gets the related resource.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The requested entity.
+   * @param $object
+   *   A loaded resource object of this type, to return a related resource for.
    * @param string $related_field
    *   The related field name.
    * @param \Symfony\Component\HttpFoundation\Request $request
@@ -93,13 +93,13 @@ interface EntityResourceInterface {
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function getRelated(EntityInterface $entity, $related_field, Request $request);
+  public function getRelated($object, $related_field, Request $request);
 
   /**
-   * Gets the relationship of an entity.
+   * Gets the relationship of an object.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The requested entity.
+   * @param $object
+   *   A loaded resource object of this type, to return a relationship for.
    * @param string $related_field
    *   The related field name.
    * @param \Symfony\Component\HttpFoundation\Request $request
@@ -110,13 +110,13 @@ interface EntityResourceInterface {
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function getRelationship(EntityInterface $entity, $related_field, Request $request, $response_code = 200);
+  public function getRelationship($object, $related_field, Request $request, $response_code = 200);
 
   /**
    * Adds a relationship to a to-many relationship.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The requested entity.
+   * @param $object
+   *   A loaded resource object of this type, to create a relationship for.
    * @param string $related_field
    *   The related field name.
    * @param mixed $parsed_field_list
@@ -128,13 +128,13 @@ interface EntityResourceInterface {
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function createRelationship(EntityInterface $entity, $related_field, $parsed_field_list, Request $request);
+  public function createRelationship($object, $related_field, $parsed_field_list, Request $request);
 
   /**
-   * Updates the relationship of an entity.
+   * Updates the relationship of an object.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The requested entity.
+   * @param $object
+   *   A loaded resource object of this type, to patch a relationship for.
    * @param string $related_field
    *   The related field name.
    * @param mixed $parsed_field_list
@@ -146,13 +146,13 @@ interface EntityResourceInterface {
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function patchRelationship(EntityInterface $entity, $related_field, $parsed_field_list, Request $request);
+  public function patchRelationship($object, $related_field, $parsed_field_list, Request $request);
 
   /**
    * Deletes the relationship of an entity.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The requested entity.
+   * @param $object
+   *   A loaded resource object of this type, to delete a relationship for.
    * @param string $related_field
    *   The related field name.
    * @param mixed $parsed_field_list
@@ -164,6 +164,6 @@ interface EntityResourceInterface {
    * @return \Drupal\jsonapi\ResourceResponse
    *   The response.
    */
-  public function deleteRelationship(EntityInterface $entity, $related_field, $parsed_field_list, Request $request);
+  public function deleteRelationship($object, $related_field, $parsed_field_list, Request $request);
 
 }
diff --git a/src/Plugin/JsonApiResourceManager.php b/src/Plugin/JsonApiResourceTypeManager.php
similarity index 49%
rename from src/Plugin/JsonApiResourceManager.php
rename to src/Plugin/JsonApiResourceTypeManager.php
index 47e0559..ebed15b 100644
--- a/src/Plugin/JsonApiResourceManager.php
+++ b/src/Plugin/JsonApiResourceTypeManager.php
@@ -5,27 +5,14 @@ namespace Drupal\jsonapi\Plugin;
 use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\jsonapi\Resource\DocumentWrapperInterface;
-use Drupal\jsonapi\Routing\Routes;
 
 /**
- * Provides the JSON API Resource plugin manager.
+ * Provides the JSON API Resource Type plugin manager.
  */
-class JsonApiResourceManager extends DefaultPluginManager {
+class JsonApiResourceTypeManager extends DefaultPluginManager {
 
   /**
-   * Default values for the plugin definition.
-   *
-   * @var array
-   */
-  protected $defaults = [
-    'permission' => 'access content',
-    'controller' => Routes::FRONT_CONTROLLER,
-    'enabled' => TRUE,
-  ];
-
-  /**
-   * Constructor for JsonApiResourceManager objects.
+   * Constructor for JsonApiResourceTypeManager objects.
    *
    * @param \Traversable $namespaces
    *   An object that implements \Traversable which contains the root paths
@@ -36,10 +23,14 @@ class JsonApiResourceManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/jsonapi', $namespaces, $module_handler, 'Drupal\jsonapi\Plugin\JsonApiResourceInterface', 'Drupal\jsonapi\Annotation\JsonApiResource');
+    // For now, only allow the JSON API module to provide JSON API Resource Type
+    // plugins. The API is not yet finalized, and it's not yet clear whether it
+    // would make sense to allow additional types beyond entity types and entity
+    // type bundles.
+    $limited_namespaces = new \ArrayObject(['\Drupal\jsonapi' => $namespaces['Drupal\jsonapi']]);
 
-    $this->alterInfo('jsonapi_resource_info');
-    $this->setCacheBackend($cache_backend, 'jsonapi_resource_plugins');
+    parent::__construct('Plugin/jsonapi', $limited_namespaces, $module_handler, 'Drupal\jsonapi\Plugin\JsonApiResourceInterface', 'Drupal\jsonapi\Annotation\JsonApiResourceType');
+    $this->setCacheBackend($cache_backend, 'jsonapi_resource_type_plugins');
   }
 
 }
diff --git a/src/Plugin/jsonapi/BundleJsonApiResource.php b/src/Plugin/jsonapi/BundleJsonApiResource.php
deleted file mode 100644
index bf97da3..0000000
--- a/src/Plugin/jsonapi/BundleJsonApiResource.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Drupal\jsonapi\Plugin\jsonapi;
-
-use Drupal\Core\Plugin\ContextAwarePluginBase;
-use Drupal\jsonapi\Plugin\JsonApiResourceBase;
-use Drupal\jsonapi\Plugin\JsonApiResourceInterface;
-
-/**
- * Class BundleJsonApiResource
- *
- * Represents bundles as JSON API resources.
- *
- * @see \Drupal\jsonapi\Plugin\Deriver\ResourceDeriver
- *
- * @JsonApiResource(
- *   id = "bundle",
- *   label = @Translation("Bundle"),
- *   deriver = "Drupal\jsonapi\Plugin\Deriver\ResourceDeriver",
- * )
- *
- * @package Drupal\jsonapi\Plugin\jsonapi
- */
-class BundleJsonApiResource extends ContextAwarePluginBase implements JsonApiResourceInterface {}
diff --git a/src/Resource/EntityResource.php b/src/Plugin/jsonapi/resource_type/EntityJsonApiResourceType.php
similarity index 91%
rename from src/Resource/EntityResource.php
rename to src/Plugin/jsonapi/resource_type/EntityJsonApiResourceType.php
index 6067824..5e0ea64 100644
--- a/src/Resource/EntityResource.php
+++ b/src/Plugin/jsonapi/resource_type/EntityJsonApiResourceType.php
@@ -1,25 +1,23 @@
 <?php
 
-namespace Drupal\jsonapi\Resource;
+namespace Drupal\jsonapi\Plugin\jsonapi\resource_type;
 
-use Drupal\Component\Serialization\Json;
 use Drupal\Core\Access\AccessibleInterface;
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
-use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
-use Drupal\jsonapi\Configuration\ResourceConfigInterface;
+use Drupal\Core\Plugin\PluginBase;
 use Drupal\jsonapi\EntityCollection;
 use Drupal\jsonapi\EntityCollectionInterface;
 use Drupal\jsonapi\Error\SerializableHttpException;
-use Drupal\jsonapi\Query\QueryBuilderInterface;
-use Drupal\jsonapi\Context\CurrentContextInterface;
+use Drupal\jsonapi\Plugin\JsonApiResourceBase;
+use Drupal\jsonapi\Plugin\JsonApiResourceInterface;
+use Drupal\jsonapi\Plugin\JsonApiResourceTypeInterface;
+use Drupal\jsonapi\Resource\DocumentWrapper;
 use Drupal\jsonapi\ResourceResponse;
 use Drupal\jsonapi\Routing\Param\JsonApiParamBase;
 use Drupal\jsonapi\Routing\Param\OffsetPage;
@@ -27,11 +25,17 @@ use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 
 /**
- * Class EntityResource.
+ * Represents entity types (and their bundles) as JSON API resource types.
  *
- * @package Drupal\jsonapi\Resource
+ * @see \Drupal\jsonapi\Plugin\Deriver\ResourceDeriver
+ *
+ * @JsonApiResourceType(
+ *   id = "entity",
+ *   label = @Translation("Entity"),
+ *   deriver = "Drupal\jsonapi\Plugin\Deriver\EntityDeriver",
+ * )
  */
-class EntityResource implements EntityResourceInterface {
+class EntityJsonApiResourceType extends PluginBase implements JsonApiResourceTypeInterface  {
 
   /**
    * The resource config.
@@ -103,7 +107,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function getIndividual(EntityInterface $entity, Request $request, $response_code = 200) {
+  public function getIndividual($entity, Request $request, $response_code = 200) {
+    assert($entity instanceof EntityInterface);
     $entity_access = $entity->access('view', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
       throw new SerializableHttpException(403, 'The current user is not allowed to GET the selected resource.');
@@ -121,7 +126,8 @@ class EntityResource implements EntityResourceInterface {
    * @throws \Drupal\jsonapi\Error\SerializableHttpException
    *   If validation errors are found.
    */
-  protected function validate(EntityInterface $entity) {
+  protected function validate($entity) {
+    assert($entity instanceof EntityInterface);
     if (!$entity instanceof FieldableEntityInterface) {
       return;
     }
@@ -148,7 +154,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function createIndividual(EntityInterface $entity, Request $request) {
+  public function createIndividual($entity, Request $request) {
+    assert($entity instanceof EntityInterface);
     $entity_access = $entity->access('create', NULL, TRUE);
 
     if (!$entity_access->isAllowed()) {
@@ -162,7 +169,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function patchIndividual(EntityInterface $entity, EntityInterface $parsed_entity, Request $request) {
+  public function patchIndividual($entity, EntityInterface $parsed_entity, Request $request) {
+    assert($entity instanceof EntityInterface);
     $entity_access = $entity->access('update', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
       throw new SerializableHttpException(403, 'The current user is not allowed to GET the selected resource.');
@@ -192,7 +200,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function deleteIndividual(EntityInterface $entity, Request $request) {
+  public function deleteIndividual($entity, Request $request) {
+    assert($entity instanceof EntityInterface);
     $entity_access = $entity->access('delete', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
       throw new SerializableHttpException(403, 'The current user is not allowed to DELETE the selected resource.');
@@ -242,7 +251,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function getRelated(EntityInterface $entity, $related_field, Request $request) {
+  public function getRelated($entity, $related_field, Request $request) {
+    assert($entity instanceof EntityInterface);
     /* @var $field_list \Drupal\Core\Field\FieldItemListInterface */
     if (!($field_list = $entity->get($related_field)) || !$this->isRelationshipField($field_list)) {
       throw new SerializableHttpException(404, sprintf('The relationship %s is not present in this resource.', $related_field));
@@ -266,7 +276,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function getRelationship(EntityInterface $entity, $related_field, Request $request, $response_code = 200) {
+  public function getRelationship($entity, $related_field, Request $request, $response_code = 200) {
+    assert($entity instanceof EntityInterface);
     if (!($field_list = $entity->get($related_field)) || !$this->isRelationshipField($field_list)) {
       throw new SerializableHttpException(404, sprintf('The relationship %s is not present in this resource.', $related_field));
     }
@@ -277,7 +288,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function createRelationship(EntityInterface $entity, $related_field, $parsed_field_list, Request $request) {
+  public function createRelationship($entity, $related_field, $parsed_field_list, Request $request) {
+    assert($entity instanceof EntityInterface);
     if ($parsed_field_list instanceof Response) {
       // This usually means that there was an error, so there is no point on
       // processing further.
@@ -312,7 +324,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function patchRelationship(EntityInterface $entity, $related_field, $parsed_field_list, Request $request) {
+  public function patchRelationship($entity, $related_field, $parsed_field_list, Request $request) {
+    assert($entity instanceof EntityInterface);
     if ($parsed_field_list instanceof Response) {
       // This usually means that there was an error, so there is no point on
       // processing further.
@@ -371,7 +384,8 @@ class EntityResource implements EntityResourceInterface {
   /**
    * {@inheritdoc}
    */
-  public function deleteRelationship(EntityInterface $entity, $related_field, $parsed_field_list, Request $request) {
+  public function deleteRelationship($entity, $related_field, $parsed_field_list, Request $request) {
+    assert($entity instanceof EntityInterface);
     if ($parsed_field_list instanceof Response) {
       // This usually means that there was an error, so there is no point on
       // processing further.
diff --git a/src/Routing/Routes.php b/src/Routing/Routes.php
index 2cd1413..606163c 100644
--- a/src/Routing/Routes.php
+++ b/src/Routing/Routes.php
@@ -4,8 +4,10 @@ namespace Drupal\jsonapi\Routing;
 
 use Drupal\Core\Authentication\AuthenticationCollectorInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Field\EntityReferenceFieldItemList;
-use Drupal\jsonapi\Plugin\JsonApiResourceManager;
+use Drupal\jsonapi\Plugin\JsonApiResourceTypeManager;
 use Drupal\jsonapi\Resource\DocumentWrapperInterface;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -27,11 +29,23 @@ class Routes implements ContainerInjectionInterface {
   const FRONT_CONTROLLER = '\Drupal\jsonapi\RequestHandler::handle';
 
   /**
-   * The resource manager interface.
+   * @var \Drupal\jsonapi\Plugin\JsonApiResourceTypeManager
+   */
+  protected $jsonApiResourceTypeManager;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The bundle manager.
    *
-   * @var \Drupal\jsonapi\Plugin\JsonApiResourceManager
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
    */
-  protected $resourcePluginManager;
+  protected $bundleManager;
 
   /**
    * The authentication collector.
@@ -41,7 +55,7 @@ class Routes implements ContainerInjectionInterface {
   protected $authCollector;
 
   /**
-   * List of providers.
+   * List of authentication provider IDs.
    *
    * @var string[]
    */
@@ -50,13 +64,17 @@ class Routes implements ContainerInjectionInterface {
   /**
    * Instantiates a Routes object.
    *
-   * @param \Drupal\jsonapi\Plugin\JsonApiResourceManager $resource_plugin_manager
-   *   The resource manager.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_manager
+   *   The bundle manager.
    * @param \Drupal\Core\Authentication\AuthenticationCollectorInterface $auth_collector
-   *   The resource manager.
+   *   The authentication collector.
    */
-  public function __construct(JsonApiResourceManager $resource_plugin_manager, AuthenticationCollectorInterface $auth_collector) {
-    $this->resourcePluginManager = $resource_plugin_manager;
+  public function __construct(JsonApiResourceTypeManager $json_api_resource_type_manager, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_manager, AuthenticationCollectorInterface $auth_collector) {
+    $this->jsonApiResourceTypeManager = $json_api_resource_type_manager;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->bundleManager = $bundle_manager;
     $this->authCollector = $auth_collector;
   }
 
@@ -64,12 +82,12 @@ class Routes implements ContainerInjectionInterface {
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    /* @var \Drupal\jsonapi\Plugin\JsonApiResourceManager $resource_plugin_manager */
-    $resource_plugin_manager = $container->get('plugin.manager.resource.processor');
-    /* @var \Drupal\Core\Authentication\AuthenticationCollectorInterface $auth_collector */
-    $auth_collector = $container->get('authentication_collector');
-
-    return new static($resource_plugin_manager, $auth_collector);
+    return new static(
+      $container->get('plugin.manager.jsonapi.resource_type'),
+      $container->get('entity_type.manager'),
+      $container->get('entity_type.bundle.info'),
+      $container->get('authentication_collector')
+    );
   }
 
   /**
@@ -77,19 +95,14 @@ class Routes implements ContainerInjectionInterface {
    */
   public function routes() {
     $collection = new RouteCollection();
-    foreach ($this->resourcePluginManager->getDefinitions() as $plugin_id => $plugin_definition) {
-      if (empty($plugin_definition['enabled'])) {
-        continue;
-      }
-      $entity_type = $plugin_definition['entityType'];
-      // For the entity type resources the bundle is NULL.
-      $bundle = $plugin_definition['bundle'];
-      $partial_path = $plugin_definition['data']['partialPath'];
-      $route_keys = explode(':', $plugin_id);
-      $route_key = end($route_keys) . '.';
+
+    foreach ($this->jsonApiResourceTypeManager->getDefinitions() as $id => $definition) {
+      $route_name_prefix = 'jsonapi.' . implode('.', $definition['type_hierarchy']);
+      $path_prefix = '/api/' . implode('/', $definition['type_hierarchy']);
+
       // Add the collection route.
       $defaults = [
-        RouteObjectInterface::CONTROLLER_NAME => $plugin_definition['controller'],
+        RouteObjectInterface::CONTROLLER_NAME => static::FRONT_CONTROLLER,
       ];
       // Options that apply to all routes.
       $options = [
@@ -98,70 +111,53 @@ class Routes implements ContainerInjectionInterface {
       ];
 
       // Collection endpoint, like /api/file/photo.
-      $route_collection = (new Route($partial_path))
+      $route_collection = (new Route($path_prefix))
         ->addDefaults($defaults)
-        ->setRequirement('_entity_type', $entity_type)
-        ->setRequirement('_permission', $plugin_definition['permission'])
+        ->addRequirements($definition['route_requirements'])
         ->setRequirement('_format', 'api_json')
         ->setRequirement('_custom_parameter_names', 'TRUE')
         ->setOption('serialization_class', DocumentWrapperInterface::class)
         ->setMethods(['GET', 'POST']);
-      if ($bundle) {
-        $route_collection->setRequirement('_bundle', $bundle);
-      }
       $route_collection->addOptions($options);
-      $collection->add($route_key . 'collection', $route_collection);
+      $collection->add($route_name_prefix . '.collection', $route_collection);
 
       // Individual endpoint, like /api/file/photo/123.
-      $parameters = [$entity_type => ['type' => 'entity:' . $entity_type]];
-      $route_individual = (new Route(sprintf('%s/{%s}', $partial_path, $entity_type)))
+      $route_individual = (new Route(sprintf('%s/{%s}', $path_prefix, $definition['route_path_part_for_individual_resource'])))
         ->addDefaults($defaults)
-        ->setRequirement('_entity_type', $entity_type)
-        ->setRequirement('_permission', $plugin_definition['permission'])
+        ->addRequirements($definition['route_requirements'])
         ->setRequirement('_format', 'api_json')
         ->setRequirement('_custom_parameter_names', 'TRUE')
-        ->setOption('parameters', $parameters)
+        ->addOptions($definition['route_options'])
         ->setOption('_auth', $this->authProviderList())
         ->setOption('serialization_class', DocumentWrapperInterface::class)
         ->setMethods(['GET', 'PATCH', 'DELETE']);
-      if ($bundle) {
-        $route_individual->setRequirement('_bundle', $bundle);
-      }
       $route_individual->addOptions($options);
-      $collection->add($route_key . 'individual', $route_individual);
+      $collection->add($route_name_prefix . '.individual', $route_individual);
 
       // Related resource, like /api/file/photo/123/comments.
-      $route_related = (new Route(sprintf('%s/{%s}/{related}', $partial_path, $entity_type)))
+      $route_related = (new Route(sprintf('%s/{%s}/{related}', $path_prefix, $definition['route_path_part_for_individual_resource'])))
         ->addDefaults($defaults)
-        ->setRequirement('_entity_type', $entity_type)
-        ->setRequirement('_permission', $plugin_definition['permission'])
+        ->addRequirements($definition['route_requirements'])
         ->setRequirement('_format', 'api_json')
         ->setRequirement('_custom_parameter_names', 'TRUE')
-        ->setOption('parameters', $parameters)
+        ->addOptions($definition['route_options'])
         ->setOption('_auth', $this->authProviderList())
         ->setMethods(['GET']);
-      if ($bundle) {
-        $route_related->setRequirement('_bundle', $bundle);
-      }
       $route_related->addOptions($options);
-      $collection->add($route_key . 'related', $route_related);
+      $collection->add($route_name_prefix . '.related', $route_related);
 
       // Related endpoint, like /api/file/photo/123/relationships/comments.
-      $route_relationship = (new Route(sprintf('%s/{%s}/relationships/{related}', $partial_path, $entity_type)))
+      $route_relationship = (new Route(sprintf('%s/{%s}/relationships/{related}', $path_prefix, $definition['route_path_part_for_individual_resource'])))
         ->addDefaults($defaults + ['_on_relationship' => TRUE])
-        ->setRequirement('_entity_type', $entity_type)
-        ->setRequirement('_permission', $plugin_definition['permission'])
+        ->addRequirements($definition['route_requirements'])
         ->setRequirement('_format', 'api_json')
         ->setRequirement('_custom_parameter_names', 'TRUE')
-        ->setOption('parameters', $parameters)
+        ->addOptions($definition['route_options'])
         ->setOption('_auth', $this->authProviderList())
         ->setOption('serialization_class', EntityReferenceFieldItemList::class)
         ->setMethods(['GET', 'POST', 'PATCH', 'DELETE']);
-      if ($bundle) {
-        $route_relationship->setRequirement('_bundle', $bundle);
-      }
       $route_relationship->addOptions($options);
-      $collection->add($route_key . 'relationship', $route_relationship);
+      $collection->add($route_name_prefix . '.relationship', $route_relationship);
     }
 
     return $collection;
