diff --git a/core/modules/page_cache/src/Tests/PageCacheTest.php b/core/modules/page_cache/src/Tests/PageCacheTest.php
index 44c94e1..c3080f3 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTest.php
@@ -130,7 +130,7 @@ function testQueryParameterFormatRequests() {
     $this->assertRaw('{"content":"oh hai this is json"}', 'The correct Json response was returned.');
 
     // Enable REST support for nodes and hal+json.
-    \Drupal::service('module_installer')->install(['node', 'rest', 'hal']);
+    \Drupal::service('module_installer')->install(['node', 'rest', 'hal', 'basic_auth']);
     $this->drupalCreateContentType(['type' => 'article']);
     $node = $this->drupalCreateNode(['type' => 'article']);
     $node_uri = $node->urlInfo();
diff --git a/core/modules/rest/config/install/rest.settings.yml b/core/modules/rest/config/install/rest.settings.yml
index d3aa82b..2d8185e 100644
--- a/core/modules/rest/config/install/rest.settings.yml
+++ b/core/modules/rest/config/install/rest.settings.yml
@@ -1,50 +1,3 @@
-# Enable all methods on nodes.
-# You must install Hal and Basic_auth modules for this to work. Also, if you are
-# going to use Basic_auth in a production environment then you should consider
-# setting up SSL.
-# There are alternatives to Basic_auth in contrib such as OAuth module.
-resources:
-  entity:node:
-    GET:
-      supported_formats:
-        - hal_json
-      supported_auth:
-        - basic_auth
-    POST:
-      supported_formats:
-        - hal_json
-      supported_auth:
-        - basic_auth
-    PATCH:
-      supported_formats:
-        - hal_json
-      supported_auth:
-        - basic_auth
-    DELETE:
-      supported_formats:
-        - hal_json
-      supported_auth:
-        - basic_auth
-
-# Multiple formats and multiple authentication providers can be defined for a
-# resource:
-#
-# resources:
-#   entity:node:
-#     GET:
-#       supported_formats:
-#         - json
-#         - hal_json
-#         - xml
-#       supported_auth:
-#         - cookie
-#         - basic_auth
-#
-# hal_json is the only format supported for POST and PATCH methods.
-#
-# The full documentation is located at
-# https://www.drupal.org/documentation/modules/rest.
-
 # Set the domain for REST type and relation links.
 # If left blank, the site's domain will be used.
 link_domain: ~
diff --git a/core/modules/rest/config/optional/rest.resource.entity__node.yml b/core/modules/rest/config/optional/rest.resource.entity__node.yml
new file mode 100644
index 0000000..4561a2f
--- /dev/null
+++ b/core/modules/rest/config/optional/rest.resource.entity__node.yml
@@ -0,0 +1,28 @@
+id: entity__node
+plugin_id: 'entity:node'
+configuration:
+  GET:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  POST:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  PATCH:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  DELETE:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+dependencies:
+  module:
+    - node
+    - basic_auth
+    - hal
diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml
index 8014b31..215f9b9 100644
--- a/core/modules/rest/config/schema/rest.schema.yml
+++ b/core/modules/rest/config/schema/rest.schema.yml
@@ -1,15 +1,8 @@
 # Schema for the configuration files of the REST module.
-
 rest.settings:
   type: config_object
   label: 'REST settings'
   mapping:
-    resources:
-      type: sequence
-      label: 'Resources'
-      sequence:
-        type: rest_resource
-        label: 'Resource'
     link_domain:
       type: string
       label: 'Domain of the relation'
@@ -45,3 +38,17 @@ rest_request:
       sequence:
         type: string
         label: 'Authentication'
+
+rest.resource.*:
+  type: config_entity
+  label: 'REST resource config'
+  mapping:
+    id:
+      type: string
+      label: 'REST resource config ID'
+    plugin_id:
+      type: string
+      label: 'REST resource plugin id'
+    configuration:
+      type: rest_resource
+      label: 'REST resource configuration'
diff --git a/core/modules/rest/rest.install b/core/modules/rest/rest.install
index 4bca69b..4a153f9 100644
--- a/core/modules/rest/rest.install
+++ b/core/modules/rest/rest.install
@@ -5,6 +5,8 @@
  * Install, update and uninstall functions for the rest module.
  */
 
+use Drupal\rest\Entity\RestResourceConfig;
+
 /**
  * Implements hook_requirements().
  */
@@ -21,3 +23,20 @@ function rest_requirements($phase) {
   }
   return $requirements;
 }
+
+/**
+ * Install the REST config entity type and convert old settings-based config.
+ */
+function rest_update_8100() {
+  \Drupal::entityDefinitionUpdateManager()->installEntityType(\Drupal::entityTypeManager()->getDefinition('rest_resource_config'));
+  foreach (\Drupal::config('rest.settings')->get('resources') as $key => $resource) {
+    $resource = RestResourceConfig::create([
+      'id' => str_replace(':', '__', $key),
+      'configuration' => $resource,
+    ]);
+    $resource->save();
+  }
+  \Drupal::configFactory()->getEditable('rest.settings')
+    ->clear('resources')
+    ->save();
+}
diff --git a/core/modules/rest/rest.permissions.yml b/core/modules/rest/rest.permissions.yml
index 2ab7154..171a284 100644
--- a/core/modules/rest/rest.permissions.yml
+++ b/core/modules/rest/rest.permissions.yml
@@ -1,2 +1,5 @@
 permission_callbacks:
   - Drupal\rest\RestPermissions::permissions
+
+administer rest resources:
+  title: 'Administer REST resource configuration'
diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml
index 6b613e3..c702719 100644
--- a/core/modules/rest/rest.services.yml
+++ b/core/modules/rest/rest.services.yml
@@ -24,7 +24,7 @@ services:
     arguments: ['@cache.default', '@entity.manager', '@module_handler', '@config.factory', '@request_stack']
   rest.resource_routes:
     class: Drupal\rest\Routing\ResourceRoutes
-    arguments: ['@plugin.manager.rest', '@config.factory', '@logger.channel.rest']
+    arguments: ['@plugin.manager.rest', '@entity_type.manager', '@logger.channel.rest']
     tags:
       - { name: 'event_subscriber' }
   logger.channel.rest:
diff --git a/core/modules/rest/src/Entity/RestResourceConfig.php b/core/modules/rest/src/Entity/RestResourceConfig.php
new file mode 100644
index 0000000..a377858
--- /dev/null
+++ b/core/modules/rest/src/Entity/RestResourceConfig.php
@@ -0,0 +1,380 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\rest\Entity\RestResourceConfig.
+ */
+
+namespace Drupal\rest\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Entity\Entity;
+use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
+use Drupal\rest\RestResourceConfigInterface;
+use Symfony\Component\DependencyInjection\ContainerAwareTrait;
+
+/**
+ * Defines a RestResourceConfig configuration entity class.
+ *
+ * @ConfigEntityType(
+ *   id = "rest_resource_config",
+ *   label = @Translation("REST resource config"),
+ *   config_prefix = "resource",
+ *   admin_permission = "administer rest resources",
+ *   label_callback = "getLabelFromPlugin",
+ *   entity_keys = {
+ *     "id" = "id"
+ *   },
+ *   config_export = {
+ *     "id",
+ *     "plugin_id",
+ *     "configuration"
+ *   }
+ * )
+ */
+class RestResourceConfig extends ConfigEntityBase implements RestResourceConfigInterface {
+
+  use ContainerAwareTrait;
+
+  /**
+   * The REST resource config id.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The REST resource plugin id.
+   *
+   * @var string
+   */
+  protected $plugin_id;
+
+  /**
+   * The REST resource configuration.
+   *
+   * @var array
+   */
+  protected $configuration;
+
+  /**
+   * The plugin manager used by this entity type.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerBase
+   */
+  protected $pluginManager;
+
+  /**
+   * A logger instance.
+   *
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $values, $entity_type) {
+    $this->pluginManager = \Drupal::service('plugin.manager.rest');
+    $this->logger = \Drupal::service('logger.factory')->get('rest');
+    $this->setContainer(\Drupal::getContainer());
+    parent::__construct($values, $entity_type);
+    // The config entity id looks like the plugin id but uses __ instead of :
+    // because : is not valid for config entities.
+    if (!isset($this->plugin_id) && isset($this->id)) {
+      // Generate plugin_id on first entity creation.
+      $this->plugin_id = str_replace('__', ':', $this->id);
+    }
+  }
+
+  /**
+   * The label callback for this configuration entity.
+   *
+   * @return string The label.
+   */
+  protected function getLabelFromPlugin() {
+    $plugin_definition = $this->pluginManager
+      ->getDefinition(['id' => $this->getResourcePluginID()]);
+    return $plugin_definition['label'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getResourcePluginID() {
+    return $this->plugin_id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getResourcePlugin() {
+    return $this->getPluginCollections()['resource']->get($this->getResourcePluginID());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isRequestMethodEnabled($method) {
+    $method = $this->normaliseRestMethod($method);
+    return isset($this->configuration[$method]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSupportedAuthenticationProviders($method) {
+    $method = $this->normaliseRestMethod($method);
+    if ($this->isRequestMethodEnabled($method) && isset($this->configuration[$method]['supported_auth'])) {
+      return $this->configuration[$method]['supported_auth'];
+    }
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasSupportForAuthenticationProvider($method, $auth) {
+    $method = $this->normaliseRestMethod($method);
+    return $this->supportsAuthenticationProviders($method)
+           && in_array($auth, $this->configuration[$method]['supported_auth']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function supportsAuthenticationProviders($method) {
+    $method = $this->normaliseRestMethod($method);
+    return $this->isRequestMethodEnabled($method)
+           && isset($this->configuration[$method]['supported_auth'])
+           && !empty($this->configuration[$method]['supported_auth']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addSupportedAuthenticationProvider($method, $auth) {
+    $method = $this->normaliseRestMethod($method);
+    if (!$this->isRequestMethodEnabled($method)) {
+      $this->configuration[$method] = ['supported_auth' => []];
+    }
+    if (!isset($this->configuration[$method]['supported_auth'])){
+      $this->configuration[$method]['supported_auth'] = [];
+    }
+    if (!in_array($auth, $this->configuration[$method]['supported_auth'])) {
+      $this->configuration[$method]['supported_auth'][] = $auth;
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeSupportedAuthenticationProvider($method, $auth) {
+    $method = $this->normaliseRestMethod($method);
+    if ($this->supportsAuthenticationProviders($method)) {
+      $new_auth = array_filter($this->configuration[$method]['supported_auth'], function ($val) use ($auth) {
+        return ($val != $auth);
+      });
+      $this->configuration[$method]['supported_auth'] = $new_auth;
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSupportedFormats($method) {
+    $method = $this->normaliseRestMethod($method);
+    if ($this->isRequestMethodEnabled($method) && isset($this->configuration[$method]['supported_formats'])) {
+      return $this->configuration[$method]['supported_formats'];
+    }
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function supportsFormat($method, $format) {
+    $method = $this->normaliseRestMethod($method);
+    return $this->hasSupportedFormats($method)
+           && in_array($format, $this->configuration[$method]['supported_formats']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasSupportedFormats($method) {
+    $method = $this->normaliseRestMethod($method);
+    return $this->isRequestMethodEnabled($method)
+           && isset($this->configuration[$method]['supported_formats'])
+           && !empty($this->configuration[$method]['supported_formats']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addSupportedFormat($method, $format) {
+    $method = $this->normaliseRestMethod($method);
+    if (!$this->isRequestMethodEnabled($method)) {
+      $this->configuration[$method] = ['supported_formats' => []];
+    }
+    if (!isset($this->configuration[$method]['supported_formats'])){
+      $this->configuration[$method]['supported_formats'] = [];
+    }
+    if (!in_array($format, $this->configuration[$method]['supported_formats'])) {
+      $this->configuration[$method]['supported_formats'][] = $format;
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeSupportedFormat($method, $format) {
+    $method = $this->normaliseRestMethod($method);
+    if ($this->hasSupportedFormats($method)){
+      $new_auth = array_filter($this->configuration[$method]['supported_formats'], function ($val) use ($format) {
+        return ($val != $format);
+      });
+      $this->configuration[$method]['supported_formats'] = $new_auth;
+    }
+    return $this;
+  }
+
+  /**
+   * Returns the plugin collections used by this entity.
+   *
+   * @return \Drupal\Component\Plugin\LazyPluginCollection[]
+   *   An array of plugin collections, keyed by the property name they use to
+   *   store their configuration.
+   */
+  public function getPluginCollections() {
+    return [
+      'resource' => new DefaultSingleLazyPluginCollection($this->pluginManager, $this->getResourcePluginID(), [])
+    ];
+  }
+
+  /**
+   * (@inheritdoc)
+   */
+  public function calculateDependencies() {
+    parent::calculateDependencies();
+    // The dependency lists for authentication providers and formats
+    // generated on container build.
+    $auth_dependencies_list = $this->container->getParameter('rest.dependencies.auth');
+    $format_dependencies_list = $this->container->getParameter('rest.dependencies.format');
+    foreach (array_keys($this->configuration) as $request_method) {
+      // Add dependencies based on the supported authentication providers.
+      foreach ($this->getSupportedAuthenticationProviders($request_method) as $auth) {
+        if (isset($auth_dependencies_list[$auth])) {
+          $module_name = $auth_dependencies_list[$auth];
+          $this->addDependency('module', $module_name);
+        }
+      }
+      // Add dependencies based on the supported authentication formats.
+      foreach ($this->getSupportedFormats($request_method) as $format) {
+        if (isset($format_dependencies_list[$format])) {
+          $module_name = $format_dependencies_list[$format];
+          $this->addDependency('module', $module_name);
+        }
+      }
+    }
+    return $this->dependencies;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onDependencyRemoval(array $dependencies) {
+    $parent = parent::onDependencyRemoval($dependencies);
+    $changed = FALSE;
+    $format_dependencies_list = $this->container->getParameter('rest.dependencies.format');
+    $auth_dependencies_list = $this->container->getParameter('rest.dependencies.auth');
+    // Only module-related dependencies can be fixed. All other types of
+    // dependencies cannot, because they were not generated based on supported
+    // authentication providers or formats.
+    if (isset($dependencies['module'])) {
+      // Try to fix dependencies.
+      $removed_auth = [];
+      $removed_formats = [];
+      foreach ($dependencies['module'] as $dep_module) {
+        // Check if the removed dependency module contained an authentication
+        // provider.
+        foreach ($auth_dependencies_list as $auth => $auth_module) {
+          if ($dep_module != $auth_module) {
+            continue;
+          }
+          $removed_auth[] = $auth;
+        }
+        // Check if the removed dependency module contained a format.
+        foreach ($format_dependencies_list as $format => $format_module) {
+          if ($dep_module != $format_module) {
+            continue;
+          }
+          $removed_formats[] = $format;
+        }
+      }
+      if (!empty($removed_auth) || !empty($removed_formats)) {
+        // Try to fix dependency problems by removing affected
+        // authentication providers and formats.
+        foreach (array_keys($this->configuration) as $request_method) {
+          foreach ($removed_formats as $format) {
+            if ($this->supportsFormat($request_method, $format)) {
+              $this->removeSupportedFormat($request_method, $format);
+            }
+          }
+          foreach ($removed_auth as $auth) {
+            if ($this->hasSupportForAuthenticationProvider($request_method, $auth)) {
+              $this->removeSupportedAuthenticationProvider($request_method, $auth);
+            }
+          }
+          if (!$this->supportsAuthenticationProviders($request_method)) {
+            // Remove the key if there are no more authentication providers
+            // supported by this request method.
+            unset($this->configuration[$request_method]['supported_auth']);
+          }
+          if (!$this->hasSupportedFormats($request_method)) {
+            // Remove the key if there are no more formats supported by this
+            // request method.
+            unset($this->configuration[$request_method]['supported_formats']);
+          }
+          if (empty($this->configuration[$request_method])) {
+            // Remove the request method altogether if it no longer has any
+            // supported authentication providers or formats.
+            unset($this->configuration[$request_method]);
+          }
+        }
+      }
+      if (!empty($this->configuration)) {
+        // Only mark the dependencies problems as fixed if there is any
+        // configuration left.
+        $changed = TRUE;
+      }
+    }
+    // If the dependency problems are not marked as fixed at this point they
+    // should be related to the resource plugin and the config entity should
+    // be deleted.
+    return $parent || $changed;
+  }
+
+  /**
+   * Normalize the method to upper case and check validity.
+   *
+   * @param string $method
+   *   The request method.
+   *
+   * @return string
+   *   The normalised request method.
+   *
+   * @throws \InvalidArgumentException
+   *   If the method is not supported.
+   */
+  protected function normaliseRestMethod($method) {
+    $valid_methods = ['GET', 'POST', 'PATCH', 'DELETE'];
+    $normalised_method = strtoupper($method);
+    if (!in_array($normalised_method, $valid_methods)) {
+      throw new \InvalidArgumentException('The method is not supported.');
+    }
+    return $normalised_method;
+  }
+}
diff --git a/core/modules/rest/src/Plugin/ResourceBase.php b/core/modules/rest/src/Plugin/ResourceBase.php
index 33cb3aa..6b440d5 100644
--- a/core/modules/rest/src/Plugin/ResourceBase.php
+++ b/core/modules/rest/src/Plugin/ResourceBase.php
@@ -7,7 +7,6 @@
 use Psr\Log\LoggerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
 
 /**
  * Common base class for resource plugins.
@@ -88,59 +87,6 @@ public function permissions() {
   }
 
   /**
-   * {@inheritdoc}
-   */
-  public function routes() {
-    $collection = new RouteCollection();
-
-    $definition = $this->getPluginDefinition();
-    $canonical_path = isset($definition['uri_paths']['canonical']) ? $definition['uri_paths']['canonical'] : '/' . strtr($this->pluginId, ':', '/') . '/{id}';
-    $create_path = isset($definition['uri_paths']['https://www.drupal.org/link-relations/create']) ? $definition['uri_paths']['https://www.drupal.org/link-relations/create'] : '/' . strtr($this->pluginId, ':', '/');
-
-    $route_name = strtr($this->pluginId, ':', '.');
-
-    $methods = $this->availableMethods();
-    foreach ($methods as $method) {
-      $route = $this->getBaseRoute($canonical_path, $method);
-
-      switch ($method) {
-        case 'POST':
-          $route->setPath($create_path);
-          // Restrict the incoming HTTP Content-type header to the known
-          // serialization formats.
-          $route->addRequirements(array('_content_type_format' => implode('|', $this->serializerFormats)));
-          $collection->add("$route_name.$method", $route);
-          break;
-
-        case 'PATCH':
-          // Restrict the incoming HTTP Content-type header to the known
-          // serialization formats.
-          $route->addRequirements(array('_content_type_format' => implode('|', $this->serializerFormats)));
-          $collection->add("$route_name.$method", $route);
-          break;
-
-        case 'GET':
-        case 'HEAD':
-          // Restrict GET and HEAD requests to the media type specified in the
-          // HTTP Accept headers.
-          foreach ($this->serializerFormats as $format_name) {
-            // Expose one route per available format.
-            $format_route = clone $route;
-            $format_route->addRequirements(array('_format' => $format_name));
-            $collection->add("$route_name.$method.$format_name", $format_route);
-          }
-          break;
-
-        default:
-          $collection->add("$route_name.$method", $route);
-          break;
-      }
-    }
-
-    return $collection;
-  }
-
-  /**
    * Provides predefined HTTP request methods.
    *
    * Plugins can override this method to provide additional custom request
@@ -179,23 +125,13 @@ public function availableMethods() {
   }
 
   /**
-   * Setups the base route for all HTTP methods.
-   *
-   * @param string $canonical_path
-   *   The canonical path for the resource.
-   * @param string $method
-   *   The HTTP method to be used for the route.
-   *
-   * @return \Symfony\Component\Routing\Route
-   *   The created base route.
+   * {@inheritdoc}
    */
-  protected function getBaseRoute($canonical_path, $method) {
+  public function getBaseRoute($canonical_path, $method) {
     $lower_method = strtolower($method);
 
     $route = new Route($canonical_path, array(
       '_controller' => 'Drupal\rest\RequestHandler::handle',
-      // Pass the resource plugin ID along as default property.
-      '_plugin' => $this->pluginId,
     ), array(
       '_permission' => "restful $lower_method $this->pluginId",
     ),
diff --git a/core/modules/rest/src/Plugin/ResourceInterface.php b/core/modules/rest/src/Plugin/ResourceInterface.php
index 0bc2bfb..d154aa2 100644
--- a/core/modules/rest/src/Plugin/ResourceInterface.php
+++ b/core/modules/rest/src/Plugin/ResourceInterface.php
@@ -17,17 +17,6 @@
 interface ResourceInterface extends PluginInspectionInterface {
 
   /**
-   * Returns a collection of routes with URL path information for the resource.
-   *
-   * This method determines where a resource is reachable, what path
-   * replacements are used, the required HTTP method for the operation etc.
-   *
-   * @return \Symfony\Component\Routing\RouteCollection
-   *   A collection of routes that should be registered for this resource.
-   */
-  public function routes();
-
-  /**
    * Provides an array of permissions suitable for .permissions.yml files.
    *
    * A resource plugin can define a set of user permissions that are used on the
@@ -46,4 +35,16 @@ public function permissions();
    */
   public function availableMethods();
 
+  /**
+   * Setups the base route for all HTTP methods.
+   *
+   * @param string $canonical_path
+   *   The canonical path for the resource.
+   * @param string $method
+   *   The HTTP method to be used for the route.
+   *
+   * @return \Symfony\Component\Routing\Route
+   *   The created base route.
+   */
+  public function getBaseRoute($canonical_path, $method);
 }
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 6ec5f26..355143f 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -2,10 +2,14 @@
 
 namespace Drupal\rest\Plugin\rest\resource;
 
+use Drupal\Component\Plugin\DependentPluginInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageException;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\rest\Plugin\ResourceBase;
 use Drupal\rest\ResourceResponse;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -26,7 +30,49 @@
  *   }
  * )
  */
-class EntityResource extends ResourceBase {
+class EntityResource extends ResourceBase implements DependentPluginInterface {
+  /**
+   * Definition of the resources entity type.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface
+   */
+  protected $entityType;
+
+  /**
+   * Constructs a Drupal\rest\Plugin\rest\resource\EntityResource object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager
+   * @param array $serializer_formats
+   *   The available serialization formats.
+   * @param \Psr\Log\LoggerInterface $logger
+   *   A logger instance.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager,  $serializer_formats,
+                              LoggerInterface $logger) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
+    $this->entityType = $entity_type_manager->getDefinition($plugin_definition['entity_type']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->getParameter('serializer.formats'),
+      $container->get('logger.factory')->get('rest')
+    );
+  }
 
   /**
    * Responds to entity GET requests.
@@ -234,7 +280,7 @@ protected function validate(EntityInterface $entity) {
   /**
    * {@inheritdoc}
    */
-  protected function getBaseRoute($canonical_path, $method) {
+  public function getBaseRoute($canonical_path, $method) {
     $route = parent::getBaseRoute($canonical_path, $method);
     $definition = $this->getPluginDefinition();
 
@@ -245,4 +291,13 @@ protected function getBaseRoute($canonical_path, $method) {
     return $route;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    if (isset($this->entityType)) {
+      return ['module' => [$this->entityType->getProvider()]];
+    }
+  }
+
 }
diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index 8e0cd74..e2acbd0 100644
--- a/core/modules/rest/src/RequestHandler.php
+++ b/core/modules/rest/src/RequestHandler.php
@@ -2,10 +2,13 @@
 
 namespace Drupal\rest;
 
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -16,11 +19,35 @@
 /**
  * Acts as intermediate request forwarder for resource plugins.
  */
-class RequestHandler implements ContainerAwareInterface {
+class RequestHandler implements ContainerAwareInterface, ContainerInjectionInterface {
 
   use ContainerAwareTrait;
 
   /**
+   * The resource configuration storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $resourceStorage;
+
+  /**
+   * Creates a new RequestHandler instance.
+   *
+   * @param \Drupal\Core\Entity\EntityStorageInterface $entity_storage
+   *   The resource configuration storage.
+   */
+  public function __construct(EntityStorageInterface $entity_storage) {
+    $this->resourceStorage = $entity_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('entity_type.manager')->getStorage('rest_resource_config'));
+  }
+
+  /**
    * Handles a web API request.
    *
    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
@@ -32,13 +59,12 @@ class RequestHandler implements ContainerAwareInterface {
    *   The response object.
    */
   public function handle(RouteMatchInterface $route_match, Request $request) {
-
-    $plugin = $route_match->getRouteObject()->getDefault('_plugin');
     $method = strtolower($request->getMethod());
 
-    $resource = $this->container
-      ->get('plugin.manager.rest')
-      ->createInstance($plugin);
+    $resource_config_id = $route_match->getRouteObject()->getDefault('_rest_resource_config');
+    /** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
+    $resource_config = $this->resourceStorage->load($resource_config_id);
+    $resource = $resource_config->getResourcePlugin();
 
     // Deserialize incoming data if available.
     $serializer = $this->container->get('serializer');
@@ -51,9 +77,8 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
       // formats are configured allow all and hope that the serializer knows the
       // format. If the serializer cannot handle it an exception will be thrown
       // that bubbles up to the client.
-      $config = $this->container->get('config.factory')->get('rest.settings')->get('resources');
-      $method_settings = $config[$plugin][$request->getMethod()];
-      if (empty($method_settings['supported_formats']) || in_array($format, $method_settings['supported_formats'])) {
+      $request_method = $request->getMethod();
+      if (!$resource_config->hasSupportedFormats($request_method) || $resource_config->supportsFormat($request_method, $format)) {
         $definition = $resource->getPluginDefinition();
         $class = $definition['serialization_class'];
         try {
@@ -99,7 +124,7 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
     }
 
     return $response instanceof ResourceResponse ?
-      $this->renderResponse($request, $response, $serializer, $format) :
+      $this->renderResponse($request, $response, $serializer, $format, $resource_config) :
       $response;
   }
 
@@ -130,14 +155,15 @@ public function csrfToken() {
    *   The serializer to use.
    * @param string $format
    *   The response format.
+   * @param \Drupal\rest\RestResourceConfigInterface $resource_config
+   *   The resource config.
    *
    * @return \Drupal\rest\ResourceResponse
    *   The altered response.
-   *
    * @todo Add test coverage for language negotiation contexts in
    *   https://www.drupal.org/node/2135829.
    */
-  protected function renderResponse(Request $request, ResourceResponse $response, SerializerInterface $serializer, $format) {
+  protected function renderResponse(Request $request, ResourceResponse $response, SerializerInterface $serializer, $format, RestResourceConfigInterface $resource_config) {
     $data = $response->getResponseData();
     $context = new RenderContext();
     $output = $this->container->get('renderer')
@@ -150,10 +176,8 @@ protected function renderResponse(Request $request, ResourceResponse $response,
     }
 
     $response->headers->set('Content-Type', $request->getMimeType($format));
-    // Add rest settings config's cache tags.
-    $response->addCacheableDependency($this->container->get('config.factory')
-      ->get('rest.settings'));
-
+    // Add rest config's cache tags.
+    $response->addCacheableDependency($resource_config);
     return $response;
   }
 
diff --git a/core/modules/rest/src/RestPermissions.php b/core/modules/rest/src/RestPermissions.php
index 57e95e4..473c192 100644
--- a/core/modules/rest/src/RestPermissions.php
+++ b/core/modules/rest/src/RestPermissions.php
@@ -2,8 +2,8 @@
 
 namespace Drupal\rest;
 
-use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\rest\Plugin\Type\ResourcePluginManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -20,30 +20,30 @@ class RestPermissions implements ContainerInjectionInterface {
   protected $restPluginManager;
 
   /**
-   * The config factory.
+   * The REST resource config storage.
    *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
-  protected $configFactory;
+  protected $resourceConfigStorage;
 
   /**
    * Constructs a new RestPermissions instance.
    *
    * @param \Drupal\rest\Plugin\Type\ResourcePluginManager $rest_plugin_manager
    *   The rest resource plugin manager.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The config factory.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
    */
-  public function __construct(ResourcePluginManager $rest_plugin_manager, ConfigFactoryInterface $config_factory) {
+  public function __construct(ResourcePluginManager $rest_plugin_manager, EntityTypeManagerInterface $entity_type_manager) {
     $this->restPluginManager = $rest_plugin_manager;
-    $this->configFactory = $config_factory;
+    $this->resourceConfigStorage = $entity_type_manager->getStorage('rest_resource_config');
   }
 
   /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    return new static($container->get('plugin.manager.rest'), $container->get('config.factory'));
+    return new static($container->get('plugin.manager.rest'), $container->get('entity_type.manager'));
   }
 
   /**
@@ -53,12 +53,11 @@ public static function create(ContainerInterface $container) {
    */
   public function permissions() {
     $permissions = [];
-    $resources = $this->configFactory->get('rest.settings')->get('resources');
-    if ($resources && $enabled = array_intersect_key($this->restPluginManager->getDefinitions(), $resources)) {
-      foreach ($enabled as $id => $resource) {
-        $plugin = $this->restPluginManager->createInstance($id);
-        $permissions = array_merge($permissions, $plugin->permissions());
-      }
+    /** @var \Drupal\rest\RestResourceConfigInterface[] $resource_configs */
+    $resource_configs = $this->resourceConfigStorage->loadMultiple();
+    foreach ($resource_configs as $resource_config) {
+      $plugin = $resource_config->getResourcePlugin();
+      $permissions = array_merge($permissions, $plugin->permissions());
     }
     return $permissions;
   }
diff --git a/core/modules/rest/src/RestResourceConfigInterface.php b/core/modules/rest/src/RestResourceConfigInterface.php
new file mode 100644
index 0000000..1a1e4ef
--- /dev/null
+++ b/core/modules/rest/src/RestResourceConfigInterface.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace Drupal\rest;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
+
+interface RestResourceConfigInterface extends ConfigEntityInterface, EntityWithPluginCollectionInterface {
+
+  /**
+   * Retrieves the REST resource plugin id.
+   *
+   * @return string The plugin id
+   */
+  public function getResourcePluginID();
+
+  /**
+   * Retrieves the REST resource plugin.
+   *
+   * @return \Drupal\rest\Plugin\ResourceInterface The resource plugin
+   */
+  public function getResourcePlugin();
+
+  /**
+   * Denotes where a given request method is enabled for this resource.
+   *
+   * @param string $method The request method
+   * @return bool
+   */
+  public function isRequestMethodEnabled($method);
+
+  /**
+   * Retrieves a list of supported authentication mechanisms
+   *   for a specific request method.
+   *
+   * @param string $method The request method e.g GET or POST
+   * @return string[] An array of supported authentication provider plugin id's
+   */
+  public function getSupportedAuthenticationProviders($method);
+
+  /**
+   * Denotes whether the given request method supports
+   *   the specified authentication provider on this resource
+   *
+   * @param string $method The request method e.g GET or POST
+   * @param string $auth An authentication provider plugin id
+   * @return bool
+   */
+  public function hasSupportForAuthenticationProvider($method, $auth);
+
+  /**
+   * Denotes whether the given request method
+   *   supports at least one authentication provider.
+   *
+   * @param string $method The request method e.g GET or POST
+   * @return bool
+   */
+  public function supportsAuthenticationProviders($method);
+
+  /**
+   * Add the specified authentication provider to
+   *   the list of supported authentication providers
+   *   for the given request method.
+   *
+   * @param string $method The request method e.g GET or POST
+   * @param string $auth An authentication provider plugin id
+   * @return $this
+   */
+  public function addSupportedAuthenticationProvider($method, $auth);
+
+  /**
+   *  Remove the specified authentication provider from
+   *   the list of supported authentication providers
+   *   for the given request method.
+   *
+   * @param string $method The request method e.g GET or POST
+   * @param string $auth An authentication provider plugin id
+   * @return $this
+   */
+  public function removeSupportedAuthenticationProvider($method, $auth);
+
+  /**
+   * Retrieves a list of supported formats for a specific request method.
+   *
+   * @param string $method The request method e.g GET or POST
+   * @return string[] An array of supported format plugin id's
+   */
+  public function getSupportedFormats($method);
+
+  /**
+   * Denotes whether the given request method supports
+   *   the specified format on this resource
+   *
+   * @param string $method The request method e.g GET or POST
+   * @param string $format A format plugin id
+   * @return bool
+   */
+  public function supportsFormat($method, $format);
+
+  /**
+   * Denotes whether the given request method supports at least one format.
+   *
+   * @param string $method The request method e.g GET or POST
+   * @return bool
+   */
+  public function hasSupportedFormats($method);
+
+  /**
+   * Add the specified format to
+   *   the list of supported authentication providers
+   *   for the given request method.
+   *
+   * @param string $method The request method e.g GET or POST
+   * @param string $format A format plugin id
+   * @return $this
+   */
+  public function addSupportedFormat($method, $format);
+
+  /**
+   *  Remove the specified format from
+   *   the list of supported authentication providers
+   *   for the given request method.
+   *
+   * @param string $method The request method e.g GET or POST
+   * @param string $format A format plugin id
+   * @return $this
+   */
+  public function removeSupportedFormat($method, $format);
+
+}
diff --git a/core/modules/rest/src/RestServiceProvider.php b/core/modules/rest/src/RestServiceProvider.php
new file mode 100644
index 0000000..0783dc8
--- /dev/null
+++ b/core/modules/rest/src/RestServiceProvider.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rest\RestServiceProvider.
+ */
+
+namespace Drupal\rest;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
+
+/**
+ * Registers rest dependencies in the container.
+ */
+class RestServiceProvider implements ServiceProviderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function register(ContainerBuilder $container) {
+    $auth = [];
+    // Build a dependency list for available authentication providers.
+    foreach ($container->findTaggedServiceIds('authentication_provider') as $service_id => $service_tags) {
+      $service_def = $container->findDefinition($service_id);
+      $service_class = $service_def->getClass();
+      if (!empty($service_class)) {
+        // Extract module name based on class namespace.
+        list($namespace_base, $module_name) = explode('\\', $service_class);
+        if (!empty($namespace_base) && ucfirst(strtolower($namespace_base)) == 'Drupal') {
+          // Remove the 'authentication.' prefix from the provider ID.
+          $provider_id = substr($service_id, 15);
+          $auth[$provider_id] = $module_name;
+        }
+      }
+    }
+    if (!empty($auth)) {
+      // Register the dependency list in the container.
+      $container->setParameter('rest.dependencies.auth', $auth);
+    }
+
+    $formats = [];
+    // Build a dependency list for available formats.
+    foreach ($container->findTaggedServiceIds('encoder') as $service_id => $service_tags) {
+      $service_def = $container->findDefinition($service_id);
+      $service_class = $service_def->getClass();
+      if (!empty($service_class)) {
+        // Extract module name based on class namespace.
+        list($namespace_base, $module_name) = explode('\\', $service_class);
+        if (!empty($namespace_base) && ucfirst(strtolower($namespace_base)) == 'Drupal') {
+          foreach ($service_tags as $service_tag) {
+            if (isset($service_tag['format'])) {
+              $format = $service_tag['format'];
+              $formats[$format] = $module_name;
+            }
+          }
+        }
+      }
+    }
+    if (!empty($formats)) {
+      // Register the dependency list in the container.
+      $container->setParameter('rest.dependencies.format', $formats);
+    }
+  }
+}
diff --git a/core/modules/rest/src/Routing/ResourceRoutes.php b/core/modules/rest/src/Routing/ResourceRoutes.php
index e88f18b..f234094 100644
--- a/core/modules/rest/src/Routing/ResourceRoutes.php
+++ b/core/modules/rest/src/Routing/ResourceRoutes.php
@@ -2,9 +2,10 @@
 
 namespace Drupal\rest\Routing;
 
-use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Routing\RouteSubscriberBase;
 use Drupal\rest\Plugin\Type\ResourcePluginManager;
+use Drupal\rest\RestResourceConfigInterface;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\Routing\RouteCollection;
 
@@ -21,11 +22,11 @@ class ResourceRoutes extends RouteSubscriberBase {
   protected $manager;
 
   /**
-   * The Drupal configuration factory.
+   * The REST resource config storage.
    *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
-  protected $config;
+  protected $resourceConfigStorage;
 
   /**
    * A logger instance.
@@ -39,14 +40,14 @@ class ResourceRoutes extends RouteSubscriberBase {
    *
    * @param \Drupal\rest\Plugin\Type\ResourcePluginManager $manager
    *   The resource plugin manager.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config
-   *   The configuration factory holding resource settings.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager
    * @param \Psr\Log\LoggerInterface $logger
    *   A logger instance.
    */
-  public function __construct(ResourcePluginManager $manager, ConfigFactoryInterface $config, LoggerInterface $logger) {
+  public function __construct(ResourcePluginManager $manager, EntityTypeManagerInterface $entity_type_manager, LoggerInterface $logger) {
     $this->manager = $manager;
-    $this->config = $config;
+    $this->resourceConfigStorage = $entity_type_manager->getStorage('rest_resource_config');
     $this->logger = $logger;
   }
 
@@ -58,56 +59,95 @@ public function __construct(ResourcePluginManager $manager, ConfigFactoryInterfa
    * @return array
    */
   protected function alterRoutes(RouteCollection $collection) {
-    $routes = array();
-
-    // Silently ignore resources that are in the settings but are not defined on
-    // the plugin manager currently. That avoids exceptions when REST module is
-    // enabled before another module that provides the resource plugin specified
-    // in the settings.
-    // @todo Remove in https://www.drupal.org/node/2308745
-    $resources = $this->config->get('rest.settings')->get('resources') ?: array();
-    $enabled_resources = array_intersect_key($resources, $this->manager->getDefinitions());
-    if (count($resources) != count($enabled_resources)) {
-      trigger_error('rest.settings lists resources relying on the following missing plugins: ' . implode(', ', array_keys(array_diff_key($resources, $enabled_resources))));
+    // Iterate over all enabled REST resource configs.
+    /** @var \Drupal\rest\RestResourceConfigInterface[] $resource_configs */
+    $resource_configs = $this->resourceConfigStorage->loadMultiple();
+    foreach ($resource_configs as $resource_config) {
+      $resource_routes = $this->getRoutesForResourceConfig($resource_config);
+      $collection->addCollection($resource_routes);
     }
+  }
+
+  /**
+   * Provides all routes for a given REST resource config.
+   *
+   * This method determines where a resource is reachable, what path
+   * replacements are used, the required HTTP method for the operation etc.
+   *
+   * @param \Drupal\rest\RestResourceConfigInterface $rest_resource_config
+   *   The rest resource config.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   *   The route collection.
+   */
+  protected function getRoutesForResourceConfig(RestResourceConfigInterface $rest_resource_config) {
+    $collection = new RouteCollection();
+
+    $plugin = $rest_resource_config->getResourcePlugin();
+    $plugin_id = $plugin->getPluginId();
+    $plugin_def = $plugin->getPluginDefinition();
+
+    $canonical_path = isset($plugin_def['uri_paths']['canonical']) ? $plugin_def['uri_paths']['canonical'] : '/' . strtr($plugin_id, ':', '/') . '/{id}';
+    $create_path = isset($definition['uri_paths']['https://www.drupal.org/link-relations/create']) ? $definition['uri_paths']['https://www.drupal.org/link-relations/create'] : '/' . strtr($plugin_id, ':', '/');
 
-    // Iterate over all enabled resource plugins.
-    foreach ($enabled_resources as $id => $enabled_methods) {
-      $plugin = $this->manager->createInstance($id);
-      foreach ($plugin->routes() as $name => $route) {
-        // @todo: Are multiple methods possible here?
-        $methods = $route->getMethods();
-        // Only expose routes where the method is enabled in the configuration.
-        if ($methods && ($method = $methods[0]) && $method && isset($enabled_methods[$method])) {
-          $route->setRequirement('_access_rest_csrf', 'TRUE');
-
-          // Check that authentication providers are defined.
-          if (empty($enabled_methods[$method]['supported_auth']) || !is_array($enabled_methods[$method]['supported_auth'])) {
-            $this->logger->error('At least one authentication provider must be defined for resource @id', array(':id' => $id));
-            continue;
-          }
-
-          // Check that formats are defined.
-          if (empty($enabled_methods[$method]['supported_formats']) || !is_array($enabled_methods[$method]['supported_formats'])) {
-            $this->logger->error('At least one format must be defined for resource @id', array(':id' => $id));
-            continue;
-          }
-
-          // If the route has a format requirement, then verify that the
-          // resource has it.
-          $format_requirement = $route->getRequirement('_format');
-          if ($format_requirement && !in_array($format_requirement, $enabled_methods[$method]['supported_formats'])) {
-            continue;
-          }
-
-          // The configuration seems legit at this point, so we set the
-          // authentication provider and add the route.
-          $route->setOption('_auth', $enabled_methods[$method]['supported_auth']);
-          $routes["rest.$name"] = $route;
-          $collection->add("rest.$name", $route);
+    $route_name = strtr($plugin_id, ':', '.');
+
+    $methods = $plugin->availableMethods();
+    foreach ($methods as $method) {
+      if ($rest_resource_config->isRequestMethodEnabled($method)) {
+        // Check that authentication providers are defined.
+        if (!$rest_resource_config->supportsAuthenticationProviders($method)) {
+          $this->logger->error('At least one authentication provider must be defined for resource @id', array(':id' => $plugin_id));
+          continue;
+        }
+
+        // Check that formats are defined.
+        if (!$rest_resource_config->hasSupportedFormats($method)) {
+          $this->logger->error('At least one format must be defined for resource @id', array(':id' => $plugin_id));
+          continue;
+        }
+
+        $route = $plugin->getBaseRoute($canonical_path, $method);
+        $route->setRequirement('_access_rest_csrf',  'TRUE');
+        $route->setOption('_auth', $rest_resource_config->getSupportedAuthenticationProviders($method));
+        $route->setDefault('_rest_resource_config', $rest_resource_config->id());
+
+        $supported_formats = $rest_resource_config->getSupportedFormats($method);
+        switch ($method) {
+          case 'POST':
+            $route->setPath($create_path);
+            // Restrict the incoming HTTP Content-type header to the known
+            // serialization formats.
+            $route->addRequirements(array('_content_type_format' => implode('|', $supported_formats)));
+            $collection->add("rest.$route_name.$method", $route);
+            break;
+          case 'PATCH':
+            // Restrict the incoming HTTP Content-type header to the known
+            // serialization formats.
+            $route->addRequirements(array('_content_type_format' => implode('|', $supported_formats)));
+            $collection->add("rest.$route_name.$method", $route);
+            break;
+
+          case 'GET':
+          case 'HEAD':
+            // Restrict GET and HEAD requests to the media type specified in the
+            // HTTP Accept headers.
+            foreach ($supported_formats as $format_name) {
+              // Expose one route per available format.
+              $format_route = clone $route;
+              $format_route->addRequirements(array('_format' => $format_name));
+              $collection->add("rest.$route_name.$method.$format_name", $format_route);
+            }
+            break;
+
+          default:
+            $collection->add("rest.$route_name.$method", $route);
+            break;
         }
       }
     }
+
+    return $collection;
   }
 
 }
diff --git a/core/modules/rest/src/Tests/AuthTest.php b/core/modules/rest/src/Tests/AuthTest.php
index 08db9ab..e9fb7d7 100644
--- a/core/modules/rest/src/Tests/AuthTest.php
+++ b/core/modules/rest/src/Tests/AuthTest.php
@@ -16,7 +16,7 @@ class AuthTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('basic_auth', 'hal', 'rest', 'entity_test', 'comment');
+  public static $modules = array('basic_auth', 'hal', 'rest', 'entity_test');
 
   /**
    * Tests reading from an authenticated resource.
diff --git a/core/modules/rest/src/Tests/CreateTest.php b/core/modules/rest/src/Tests/CreateTest.php
index 035480b..28c8af6 100644
--- a/core/modules/rest/src/Tests/CreateTest.php
+++ b/core/modules/rest/src/Tests/CreateTest.php
@@ -23,7 +23,7 @@ class CreateTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'entity_test', 'comment');
+  public static $modules = array('hal', 'rest', 'entity_test', 'comment', 'node');
 
   /**
    * The 'serializer' service.
diff --git a/core/modules/rest/src/Tests/DeleteTest.php b/core/modules/rest/src/Tests/DeleteTest.php
index ccba38e..4cc8016 100644
--- a/core/modules/rest/src/Tests/DeleteTest.php
+++ b/core/modules/rest/src/Tests/DeleteTest.php
@@ -16,7 +16,7 @@ class DeleteTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'entity_test');
+  public static $modules = array('hal', 'rest', 'entity_test', 'node');
 
   /**
    * Tests several valid and invalid delete requests on all entity types.
diff --git a/core/modules/rest/src/Tests/NodeTest.php b/core/modules/rest/src/Tests/NodeTest.php
index 7f0ed81..9eedd2c 100644
--- a/core/modules/rest/src/Tests/NodeTest.php
+++ b/core/modules/rest/src/Tests/NodeTest.php
@@ -19,7 +19,7 @@ class NodeTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'comment');
+  public static $modules = array('hal', 'rest', 'comment', 'node');
 
   /**
    * Enables node specific REST API configuration and authentication.
diff --git a/core/modules/rest/src/Tests/PageCacheTest.php b/core/modules/rest/src/Tests/PageCacheTest.php
index 1958fdc..b29ab60 100644
--- a/core/modules/rest/src/Tests/PageCacheTest.php
+++ b/core/modules/rest/src/Tests/PageCacheTest.php
@@ -34,7 +34,7 @@ public function testConfigChangePageCache() {
     $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET', NULL, $this->defaultMimeType);
     $this->assertResponse(200, 'HTTP response code is correct.');
     $this->assertHeader('x-drupal-cache', 'MISS');
-    $this->assertCacheTag('config:rest.settings');
+    $this->assertCacheTag('config:rest.resource.entity__entity_test');
     $this->assertCacheTag('entity_test:1');
     $this->assertCacheTag('entity_test_access:field_test_text');
 
@@ -42,17 +42,17 @@ public function testConfigChangePageCache() {
     $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET', NULL, $this->defaultMimeType);
     $this->assertResponse(200, 'HTTP response code is correct.');
     $this->assertHeader('x-drupal-cache', 'HIT');
-    $this->assertCacheTag('config:rest.settings');
+    $this->assertCacheTag('config:rest.resource.entity__entity_test');
     $this->assertCacheTag('entity_test:1');
     $this->assertCacheTag('entity_test_access:field_test_text');
 
-    // Trigger a config save which should clear the page cache, so we should get
-    // a cache miss now for the same request.
-    $this->config('rest.settings')->save();
+    // Trigger an resource config save which should clear the page cache, so we
+    // should get a cache miss now for the same request.
+    $this->resourceConfigStorage->load('entity__entity_test')->save();
     $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET', NULL, $this->defaultMimeType);
     $this->assertResponse(200, 'HTTP response code is correct.');
     $this->assertHeader('x-drupal-cache', 'MISS');
-    $this->assertCacheTag('config:rest.settings');
+    $this->assertCacheTag('config:rest.resource.entity__entity_test');
     $this->assertCacheTag('entity_test:1');
     $this->assertCacheTag('entity_test_access:field_test_text');
   }
diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php
index 71b1a08..a07ecfa 100644
--- a/core/modules/rest/src/Tests/RESTTestBase.php
+++ b/core/modules/rest/src/Tests/RESTTestBase.php
@@ -11,6 +11,13 @@
 abstract class RESTTestBase extends WebTestBase {
 
   /**
+   * The REST resource config storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $resourceConfigStorage;
+
+  /**
    * The default serialization format to use for testing REST operations.
    *
    * @var string
@@ -51,15 +58,18 @@
    *
    * @var array
    */
-  public static $modules = array('rest', 'entity_test', 'node');
+  public static $modules = array('rest', 'entity_test');
 
   protected function setUp() {
     parent::setUp();
     $this->defaultFormat = 'hal_json';
     $this->defaultMimeType = 'application/hal+json';
     $this->defaultAuth = array('cookie');
+    $this->resourceConfigStorage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
     // Create a test content type for node testing.
-    $this->drupalCreateContentType(array('name' => 'resttest', 'type' => 'resttest'));
+    if (in_array('node', static::$modules)) {
+      $this->drupalCreateContentType(array('name' => 'resttest', 'type' => 'resttest'));
+    }
   }
 
   /**
@@ -255,24 +265,32 @@ protected function entityValues($entity_type) {
    * @param array $auth
    *   (Optional) The list of valid authentication methods.
    */
-  protected function enableService($resource_type, $method = 'GET', $format = NULL, $auth = NULL) {
-    // Enable REST API for this entity type.
-    $config = $this->config('rest.settings');
-    $settings = array();
-
+  protected function enableService($resource_type, $method = 'GET', $format = NULL, array $auth = []) {
     if ($resource_type) {
+      // Enable REST API for this entity type.
+      $resource_config_id = str_replace(':', '__', $resource_type);
+      // get entity by id
+      /** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
+      $resource_config = $this->resourceConfigStorage->load($resource_config_id);
+      $resource_config = ($resource_config !== NULL) ? $resource_config : $this->resourceConfigStorage->create(['id' => $resource_config_id]);
+
       if ($format == NULL) {
         $format = $this->defaultFormat;
       }
-      $settings[$resource_type][$method]['supported_formats'][] = $format;
+      $resource_config->addSupportedFormat($method, $format);
 
-      if ($auth == NULL) {
+      if (!is_array($auth) || empty($auth)) {
         $auth = $this->defaultAuth;
       }
-      $settings[$resource_type][$method]['supported_auth'] = $auth;
+      foreach ($auth as $auth_provider) {
+        $resource_config->addSupportedAuthenticationProvider($method, $auth_provider);
+      }
+      $resource_config->save();
+    } else {
+      foreach ($this->resourceConfigStorage->loadMultiple() as $resource_config) {
+        $resource_config->delete();
+      }
     }
-    $config->set('resources', $settings);
-    $config->save();
     $this->rebuildCache();
   }
 
diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php
index 31b5db8..4ee05cd 100644
--- a/core/modules/rest/src/Tests/ReadTest.php
+++ b/core/modules/rest/src/Tests/ReadTest.php
@@ -17,7 +17,7 @@ class ReadTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'entity_test');
+  public static $modules = array('hal', 'rest', 'entity_test', 'node');
 
   /**
    * Tests several valid and invalid read requests on all entity types.
diff --git a/core/modules/rest/src/Tests/ResourceTest.php b/core/modules/rest/src/Tests/ResourceTest.php
index df99cc6..a171d7f 100644
--- a/core/modules/rest/src/Tests/ResourceTest.php
+++ b/core/modules/rest/src/Tests/ResourceTest.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\rest\Tests;
 
-use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\user\Entity\Role;
 
@@ -32,9 +31,7 @@ class ResourceTest extends RESTTestBase {
    */
   protected function setUp() {
     parent::setUp();
-    $this->config = $this->config('rest.settings');
-
-    // Create an entity programmatically.
+    // Create an entity programmatic.
     $this->entity = $this->entityCreate('entity_test');
     $this->entity->save();
 
@@ -47,19 +44,12 @@ protected function setUp() {
    * Tests that a resource without formats cannot be enabled.
    */
   public function testFormats() {
-    $settings = array(
-      'entity:entity_test' => array(
-        'GET' => array(
-          'supported_auth' => array(
-            'basic_auth',
-          ),
-        ),
-      ),
-    );
-
+    /** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
+    $resource_config = $this->resourceConfigStorage->create(['id' => 'entity__entity_test']);
     // Attempt to enable the resource.
-    $this->config->set('resources', $settings);
-    $this->config->save();
+    $resource_config
+      ->addSupportedAuthenticationProvider('GET', 'basic_auth')
+      ->save();
     $this->rebuildCache();
 
     // Verify that accessing the resource returns 406.
@@ -77,19 +67,12 @@ public function testFormats() {
    * Tests that a resource without authentication cannot be enabled.
    */
   public function testAuthentication() {
-    $settings = array(
-      'entity:entity_test' => array(
-        'GET' => array(
-          'supported_formats' => array(
-            'hal_json',
-          ),
-        ),
-      ),
-    );
-
+    /** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
+    $resource_config = $this->resourceConfigStorage->create(['id' => 'entity__entity_test']);
     // Attempt to enable the resource.
-    $this->config->set('resources', $settings);
-    $this->config->save();
+    $resource_config
+      ->addSupportedFormat('GET', 'hal_json')
+      ->save();
     $this->rebuildCache();
 
     // Verify that accessing the resource returns 401.
@@ -118,30 +101,4 @@ public function testUriPaths() {
     }
   }
 
-  /**
-   * Tests that a resource with a missing plugin does not cause an exception.
-   */
-  public function testMissingPlugin() {
-    $settings = array(
-      'entity:nonexisting' => array(
-        'GET' => array(
-          'supported_formats' => array(
-            'hal_json',
-          ),
-        ),
-      ),
-    );
-
-    try {
-      // Attempt to enable the resource.
-      $this->config->set('resources', $settings);
-      $this->config->save();
-      $this->rebuildCache();
-      $this->pass('rest.settings referencing a missing REST resource plugin does not cause an exception.');
-    }
-    catch (PluginNotFoundException $e) {
-      $this->fail('rest.settings referencing a missing REST resource plugin caused an exception.');
-    }
-  }
-
 }
diff --git a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
index 36a482c..2747bd3 100644
--- a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
+++ b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
@@ -1,18 +1,15 @@
 <?php
 
-/**
- * @file
- * Contains \Drupal\Tests\rest\Kernel\RequestHandlerTest.
- */
-
 namespace Drupal\Tests\rest\Kernel;
 
+use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Routing\RouteMatch;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\rest\Plugin\ResourceBase;
 use Drupal\rest\Plugin\Type\ResourcePluginManager;
 use Drupal\rest\RequestHandler;
 use Drupal\rest\ResourceResponse;
+use Drupal\rest\RestResourceConfigInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Route;
 
@@ -32,11 +29,19 @@ class RequestHandlerTest extends KernelTestBase {
   public static $modules = ['serialization', 'rest'];
 
   /**
+   * The entity storage.
+   *
+   * @var \Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $entityStorage;
+
+  /**
    * {@inheritdoc}
    */
   public function setUp() {
     parent::setUp();
-    $this->requestHandler = new RequestHandler();
+    $this->entityStorage = $this->prophesize(EntityStorageInterface::class);
+    $this->requestHandler = new RequestHandler($this->entityStorage->reveal());
     $this->requestHandler->setContainer($this->container);
   }
 
@@ -47,17 +52,19 @@ public function setUp() {
    */
   public function testBaseHandler() {
     $request = new Request();
-    $route_match = new RouteMatch('test', new Route('/rest/test', ['_plugin' => 'restplugin', '_format' => 'json']));
+    $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => 'restplugin', '_format' => 'json']));
 
     $resource = $this->prophesize(StubRequestHandlerResourcePlugin::class);
     $resource->get(NULL, $request)
       ->shouldBeCalled();
 
-    // Setup stub plugin manager that will return our plugin.
-    $stub = $this->prophesize(ResourcePluginManager::class);
-    $stub->createInstance('restplugin')
-      ->willReturn($resource->reveal());
-    $this->container->set('plugin.manager.rest', $stub->reveal());
+    // Setup the configuration.
+    $config = $this->prophesize(RestResourceConfigInterface::class);
+    $config->getResourcePlugin()->willReturn($resource->reveal());
+    $config->getCacheContexts()->willReturn([]);
+    $config->getCacheTags()->willReturn([]);
+    $config->getCacheMaxAge()->willReturn(12);
+    $this->entityStorage->load('restplugin')->willReturn($config->reveal());
 
     // Response returns NULL this time because response from plugin is not
     // a ResourceResponse so it is passed through directly.
@@ -89,15 +96,17 @@ public function testBaseHandler() {
    */
   public function testSerialization($data) {
     $request = new Request();
-    $route_match = new RouteMatch('test', new Route('/rest/test', ['_plugin' => 'restplugin', '_format' => 'json']));
+    $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => 'restplugin', '_format' => 'json']));
 
     $resource = $this->prophesize(StubRequestHandlerResourcePlugin::class);
 
-    // Setup stub plugin manager that will return our plugin.
-    $stub = $this->prophesize(ResourcePluginManager::class);
-    $stub->createInstance('restplugin')
-      ->willReturn($resource->reveal());
-    $this->container->set('plugin.manager.rest', $stub->reveal());
+    // Setup the configuration.
+    $config = $this->prophesize(RestResourceConfigInterface::class);
+    $config->getResourcePlugin()->willReturn($resource->reveal());
+    $config->getCacheContexts()->willReturn([]);
+    $config->getCacheTags()->willReturn([]);
+    $config->getCacheMaxAge()->willReturn(12);
+    $this->entityStorage->load('restplugin')->willReturn($config->reveal());
 
     $response = new ResourceResponse($data);
     $resource->get(NULL, $request)
diff --git a/core/modules/system/src/Tests/System/ResponseGeneratorTest.php b/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
index 9d6cf4f..d0d22a2 100644
--- a/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
+++ b/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
@@ -16,7 +16,7 @@ class ResponseGeneratorTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'node');
+  public static $modules = array('hal', 'rest', 'node', 'basic_auth');
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/system/src/Tests/Update/UpdatePathTestBase.php b/core/modules/system/src/Tests/Update/UpdatePathTestBase.php
index 19e1fcf..82169ad 100644
--- a/core/modules/system/src/Tests/Update/UpdatePathTestBase.php
+++ b/core/modules/system/src/Tests/Update/UpdatePathTestBase.php
@@ -265,7 +265,15 @@ protected function runUpdates() {
     }
 
     // Ensure that the update hooks updated all entity schema.
-    $this->assertFalse(\Drupal::service('entity.definition_update_manager')->needsUpdates(), 'After all updates ran, entity schema is up to date.');
+    $needs_updates = \Drupal::entityDefinitionUpdateManager()->needsUpdates();
+    $this->assertFalse($needs_updates, 'After all updates ran, entity schema is up to date.');
+    if ($needs_updates) {
+      foreach (\Drupal::entityDefinitionUpdateManager()->getChangeSummary() as $entity_type_id => $summary) {
+        foreach ($summary as $message) {
+          $this->fail($message);
+        }
+      }
+    }
   }
 
   /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index cdaeba6..ba1172e 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1131,7 +1131,11 @@ function system_update_8004() {
   $manager = \Drupal::entityDefinitionUpdateManager();
   foreach (array_keys(\Drupal::entityManager()
     ->getDefinitions()) as $entity_type_id) {
-    $manager->updateEntityType($manager->getEntityType($entity_type_id));
+    // Only update the entity type if it already exists. This condition is
+    // needed in case new entity types are introduced after this update.
+    if ($entity_type = $manager->getEntityType($entity_type_id)) {
+      $manager->updateEntityType($entity_type);
+    }
   }
 }
 
