diff --git a/core/MAINTAINERS.txt b/core/MAINTAINERS.txt
index 169c785..13e5b52 100644
--- a/core/MAINTAINERS.txt
+++ b/core/MAINTAINERS.txt
@@ -237,6 +237,9 @@ Filter module
 Forum module
 - Lee Rowlands 'larowlan' http://drupal.org/user/395439
 
+Hypertext Application Language (HAL) module
+- Lin Clark 'linclark' http://drupal.org/user/396253
+
 Help module
 - ?
 
diff --git a/core/modules/hal/hal.info.yml b/core/modules/hal/hal.info.yml
new file mode 100644
index 0000000..ac48857
--- /dev/null
+++ b/core/modules/hal/hal.info.yml
@@ -0,0 +1,7 @@
+name: 'HAL (Hypertext Application Language)'
+description: 'Serializes entities using HAL'
+package: Core
+core: 8.x
+dependencies:
+  - rest
+  - serialization
\ No newline at end of file
diff --git a/core/modules/hal/hal.module b/core/modules/hal/hal.module
new file mode 100644
index 0000000..72e9b86
--- /dev/null
+++ b/core/modules/hal/hal.module
@@ -0,0 +1,6 @@
+<?php
+
+/**
+ * @file
+ * Drupal-required module file for HAL module.
+ */
diff --git a/core/modules/hal/lib/Drupal/hal/Encoder/JsonEncoder.php b/core/modules/hal/lib/Drupal/hal/Encoder/JsonEncoder.php
new file mode 100644
index 0000000..6d1da5d
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/Encoder/JsonEncoder.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\JsonEncoder.
+ */
+
+namespace Drupal\hal\Encoder;
+
+use Symfony\Component\Serializer\Encoder\JsonEncoder as SymfonyJsonEncoder;
+
+/**
+ * Encodes HAL data in JSON.
+ *
+ * Simply respond to application/hal+json requests using the JSON encoder.
+ */
+class JsonEncoder extends SymfonyJsonEncoder {
+
+  /**
+   * The formats that this Encoder supports.
+   *
+   * @var string
+   */
+  protected $format = 'hal_json';
+
+  /**
+   * Overrides \Symfony\Component\Serializer\Encoder\JsonEncoder::supportsEncoding()
+   */
+  public function supportsEncoding($format) {
+    return $format == $this->format;
+  }
+
+}
diff --git a/core/modules/hal/lib/Drupal/hal/HalBundle.php b/core/modules/hal/lib/Drupal/hal/HalBundle.php
new file mode 100644
index 0000000..bb1e2dc
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/HalBundle.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\HalBundle.
+ */
+
+namespace Drupal\hal;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * HAL dependency injection container.
+ */
+class HalBundle extends Bundle {
+
+  /**
+   * Overrides \Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    $priority = 10;
+
+    $container->register('serializer.normalizer.entity_reference_item.hal', 'Drupal\hal\Normalizer\EntityReferenceItemNormalizer')
+      ->addMethodCall('setLinkManager', array(new Reference('rest.link_manager')))
+      ->addTag('normalizer', array('priority' => $priority));
+    $container->register('serializer.normalizer.field_item.hal', 'Drupal\hal\Normalizer\FieldItemNormalizer')
+      ->addMethodCall('setLinkManager', array(new Reference('rest.link_manager')))
+      ->addTag('normalizer', array('priority' => $priority));
+    $container->register('serializer.normalizer.field.hal', 'Drupal\hal\Normalizer\FieldNormalizer')
+      ->addMethodCall('setLinkManager', array(new Reference('rest.link_manager')))
+      ->addTag('normalizer', array('priority' => $priority));
+    $container->register('serializer.normalizer.entity.hal', 'Drupal\hal\Normalizer\EntityNormalizer')
+      ->addMethodCall('setLinkManager', array(new Reference('rest.link_manager')))
+      ->addTag('normalizer', array('priority' => $priority));
+
+    $container->register('serializer.encoder.hal', 'Drupal\hal\Encoder\JsonEncoder')
+      ->addTag('encoder', array(
+        'priority' => $priority,
+        'format' => array(
+          'hal_json' => 'HAL (JSON)',
+        ),
+      ));
+
+    $container->register('hal.subscriber', 'Drupal\hal\HalSubscriber')
+      ->addTag('event_subscriber');
+  }
+}
diff --git a/core/modules/hal/lib/Drupal/hal/HalSubscriber.php b/core/modules/hal/lib/Drupal/hal/HalSubscriber.php
new file mode 100644
index 0000000..93a70bb
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/HalSubscriber.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\HalSubscriber.
+ */
+
+namespace Drupal\hal;
+
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Subscribes to the kernel request event to add HAL media types.
+ */
+class HalSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Registers HAL formats with the Request class.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The event to process.
+   */
+  public function onKernelRequest(GetResponseEvent $event) {
+    $request = $event->getRequest();
+    $request->setFormat('hal_json', 'application/hal+json');
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onKernelRequest', 40);
+    return $events;
+  }
+
+}
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php
new file mode 100644
index 0000000..0613d2a
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\Normalizer\EntityNormalizer.
+ */
+
+namespace Drupal\hal\Normalizer;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\EntityNG;
+
+/**
+ * Converts the Drupal entity object structure to a HAL array structure.
+ */
+class EntityNormalizer extends NormalizerBase {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\EntityInterface';
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
+   */
+  public function normalize($entity, $format = NULL, array $context = array()) {
+    // Create the array of normalized properties, starting with the URI.
+    $normalized = array(
+      '_links' => array(
+        'self' => array(
+          'href' => $this->getEntityUri($entity),
+        ),
+        'type' => array(
+          'href' => $this->linkManager->getTypeUri($entity->entityType(), $entity->bundle()),
+        ),
+      ),
+    );
+
+    // If the properties to use were specified, only output those properties.
+    // Otherwise, output all properties except internal ID.
+    if (isset($context['included_fields'])) {
+      foreach ($context['included_fields'] as $property_name) {
+        $properties[] = $entity->get($property_name);
+      }
+    }
+    else {
+      $properties = $entity->getProperties();
+    }
+    foreach ($properties as $property) {
+      // In some cases, Entity API will return NULL array items. Ensure this is
+      // a real property and that it is not the internal id.
+      if (!is_object($property) || $property->getName() == 'id') {
+        continue;
+      }
+      $normalized_property = $this->serializer->normalize($property, $format, $context);
+      $normalized = NestedArray::mergeDeep($normalized, $normalized_property);
+    }
+
+    return $normalized;
+  }
+
+  /**
+   * Constructs the entity URI.
+   *
+   * @param $entity
+   *   The entity.
+   *
+   * @return string
+   *   The entity URI.
+   */
+  protected function getEntityUri($entity) {
+    $uri_info = $entity->uri();
+    return url($uri_info['path'], array('absolute' => TRUE));
+  }
+
+}
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php
new file mode 100644
index 0000000..b56c2bb
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\Normalizer\EntityReferenceItemNormalizer.
+ */
+
+namespace Drupal\hal\Normalizer;
+
+/**
+ * Converts the Drupal entity reference item object to HAL array structure.
+ */
+class EntityReferenceItemNormalizer extends FieldItemNormalizer {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\Field\Type\EntityReferenceItem';
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
+   */
+  public function normalize($field_item, $format = NULL, array $context = array()) {
+    $target_entity = $field_item->get('entity')->getValue();
+
+    // If the parent entity passed in a langcode, unset it before normalizing
+    // the target entity. Otherwise, untranslatable fields of the target entity
+    // will include the langcode.
+    $langcode = isset($context['langcode']) ? $context['langcode'] : NULL;
+    unset($context['langcode']);
+    $context['included_fields'] = array('uuid');
+
+    // Normalize the target entity.
+    $embedded = $this->serializer->normalize($target_entity, $format, $context);
+    $link = $embedded['_links']['self'];
+    // If the field is translatable, add the langcode to the link relation
+    // object. This does not indicate the language of the target entity.
+    if ($langcode) {
+      $embedded['lang'] = $link['lang'] = $langcode;
+    }
+
+    // The returned structure will be recursively merged into the normalized
+    // entity so that the items are properly added to the _links and _embedded
+    // objects.
+    $field_name = $field_item->getParent()->getName();
+    $entity = $field_item->getRoot();
+    $field_uri = $this->linkManager->getRelationUri($entity->entityType(), $entity->bundle(), $field_name);
+    return array(
+      '_links' => array(
+        $field_uri => array($link),
+      ),
+      '_embedded' => array(
+        $field_uri => array($embedded),
+      ),
+    );
+  }
+
+}
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php
new file mode 100644
index 0000000..b06a90f
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\Normalizer\FieldItemNormalizer.
+ */
+
+namespace Drupal\hal\Normalizer;
+
+/**
+ * Converts the Drupal field item object structure to HAL array structure.
+ */
+class FieldItemNormalizer extends NormalizerBase {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\Field\FieldItemInterface';
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
+   */
+  public function normalize($field_item, $format = NULL, array $context = array()) {
+    $values = $field_item->getPropertyValues();
+    if (isset($context['langcode'])) {
+      $values['lang'] = $context['langcode'];
+    }
+
+    // The values are wrapped in an array, and then wrapped in another array
+    // keyed by field name so that field items can be merged by the
+    // FieldNormalizer. This is necessary for the EntityReferenceItemNormalizer
+    // to be able to place values in the '_links' array.
+    $field = $field_item->getParent();
+    return array(
+      $field->getName() => array($values),
+    );
+  }
+
+}
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php
new file mode 100644
index 0000000..d088bca
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\Normalizer\FieldNormalizer.
+ */
+
+namespace Drupal\hal\Normalizer;
+
+use Drupal\Component\Utility\NestedArray;
+
+/**
+ * Converts the Drupal field structure to HAL array structure.
+ */
+class FieldNormalizer extends NormalizerBase {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\Field\FieldInterface';
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
+   */
+  public function normalize($field, $format = NULL, array $context = array()) {
+    $normalized_field_items = array();
+    $entity = $field->getParent();
+    $field_name = $field->getName();
+    $field_definition = $entity->getPropertyDefinition($field_name);
+
+    // If this field is not translatable, it can simply be normalized without
+    // separating it into different translations.
+    if (empty($field_definition['translatable'])) {
+      $normalized_field_items = $this->normalizeFieldItems($field, $format, $context);
+    }
+    // Otherwise, the languages have to be extracted from the entity and passed
+    // in to the field item normalizer in the context. The langcode is appended
+    // to the field item values.
+    else {
+      foreach ($entity->getTranslationLanguages() as $lang) {
+        $context['langcode'] = $lang->langcode == 'und' ? LANGUAGE_DEFAULT : $lang->langcode;
+        $translation = $entity->getTranslation($lang->langcode);
+        $translated_field = $translation->get($field_name);
+        $normalized_field_items = array_merge($normalized_field_items, $this->normalizeFieldItems($translated_field, $format, $context));
+      }
+    }
+
+    // Merge deep so that links set in entity reference normalizers are merged
+    // into the links property.
+    $normalized = NestedArray::mergeDeepArray($normalized_field_items);
+    return $normalized;
+  }
+
+  /**
+   * Helper function to normalize field items.
+   *
+   * @param \Drupal\Core\Entity\Field\FieldInterface $field
+   *   The field object.
+   * @param string $format
+   *   The format.
+   * @param array $context
+   *   The context array.
+   *
+   * @return array
+   *   The array of normalized field items.
+   */
+  protected function normalizeFieldItems($field, $format, $context) {
+    $normalized_field_items = array();
+    if (!$field->isEmpty()) {
+      foreach ($field as $field_item) {
+        $normalized_field_items[] = $this->serializer->normalize($field_item, $format, $context);
+      }
+    }
+    return $normalized_field_items;
+  }
+
+}
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/NormalizerBase.php b/core/modules/hal/lib/Drupal/hal/Normalizer/NormalizerBase.php
new file mode 100644
index 0000000..d159f1f
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/NormalizerBase.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\Normalizer\NormalizerBase.
+ */
+
+namespace Drupal\hal\Normalizer;
+
+use Drupal\serialization\Normalizer\NormalizerBase as SerializationNormalizerBase;
+
+/**
+ * Base class for Normalizers.
+ */
+abstract class NormalizerBase extends SerializationNormalizerBase {
+
+  /**
+   * The formats that the Normalizer can handle.
+   *
+   * @var array
+   */
+  protected $formats = array('hal_json');
+
+  /**
+   * The hypermedia link manager.
+   *
+   * @var \Drupal\rest\LinkManager\LinkManager
+   */
+  protected $linkManager;
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::supportsNormalization().
+   */
+  public function supportsNormalization($data, $format = NULL) {
+    return in_array($format, $this->formats) && parent::supportsNormalization($data, $format);
+  }
+
+  /**
+   * Sets the link manager.
+   *
+   * The link manager determines the hypermedia type and relation links which
+   * correspond to different bundles and fields.
+   *
+   * @param \Drupal\rest\LinkManager\LinkManager $link_manager
+   */
+  public function setLinkManager($link_manager) {
+    $this->linkManager = $link_manager;
+  }
+
+}
diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizeTest.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizeTest.php
new file mode 100644
index 0000000..848b19a
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizeTest.php
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\Tests\NormalizeTest.
+ */
+
+namespace Drupal\hal\Tests;
+
+/**
+ * Test the HAL normalizer.
+ */
+class NormalizeTest extends NormalizerTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Normalize Test',
+      'description' => 'Test that entities can be normalized in HAL.',
+      'group' => 'HAL',
+    );
+  }
+
+  /**
+   * Tests the normalize function.
+   */
+  public function testNormalize() {
+    $target_entity_de = entity_create('entity_test', (array('langcode' => 'de', 'field_test_entity_reference' => NULL)));
+    $target_entity_de->save();
+    $target_entity_en = entity_create('entity_test', (array('langcode' => 'en', 'field_test_entity_reference' => NULL)));
+    $target_entity_en->save();
+
+    // Create a German entity.
+    $values = array(
+      'langcode' => 'de',
+      'name' => $this->randomName(),
+      'user_id' => 1,
+      'field_test_text' => array(
+        'value' => $this->randomName(),
+        'format' => 'full_html',
+      ),
+      'field_test_entity_reference' => array(
+        'target_id' => $target_entity_de->id(),
+      ),
+    );
+    // Array of translated values.
+    $translation_values = array(
+      'name' => $this->randomName(),
+      'field_test_entity_reference' => array(
+        'target_id' => $target_entity_en->id(),
+      )
+    );
+
+    $entity = entity_create('entity_test', $values);
+    $entity->save();
+    // Add an English value for name and entity reference properties.
+    $entity->getTranslation('en')->set('name', array(0 => array('value' => $translation_values['name'])));
+    $entity->getTranslation('en')->set('field_test_entity_reference', array(0 => $translation_values['field_test_entity_reference']));
+    $entity->save();
+
+    $type_uri = url('rest/type/entity_test/entity_test', array('absolute' => TRUE));
+    $relation_uri = url('rest/relation/entity_test/entity_test/field_test_entity_reference', array('absolute' => TRUE));
+
+    $expected_array = array(
+      '_links' => array(
+        'curies' => array(
+          array(
+            'href' => '/relations',
+            'name' => 'site',
+            'templated' => true,
+          ),
+        ),
+        'self' => array(
+          'href' => $this->getEntityUri($entity),
+        ),
+        'type' => array(
+          'href' => $type_uri,
+        ),
+        $relation_uri => array(
+          array(
+            'href' => $this->getEntityUri($target_entity_de),
+            'lang' => 'de',
+          ),
+          array(
+            'href' => $this->getEntityUri($target_entity_en),
+            'lang' => 'en',
+          ),
+        ),
+      ),
+      '_embedded' => array(
+        $relation_uri => array(
+          array(
+            '_links' => array(
+              'self' => array(
+                'href' => $this->getEntityUri($target_entity_de),
+              ),
+              'type' => array(
+                'href' => $type_uri,
+              ),
+            ),
+            'uuid' => array(
+              array(
+                'value' => $target_entity_de->uuid(),
+              ),
+            ),
+            'lang' => 'de',
+          ),
+          array(
+            '_links' => array(
+              'self' => array(
+                'href' => $this->getEntityUri($target_entity_en),
+              ),
+              'type' => array(
+                'href' => $type_uri,
+              ),
+            ),
+            'uuid' => array(
+              array(
+                'value' => $target_entity_en->uuid(),
+              ),
+            ),
+            'lang' => 'en',
+          ),
+        ),
+      ),
+      'uuid' => array(
+        array(
+          'value' => $entity->uuid(),
+        ),
+      ),
+      'langcode' => array(
+        array(
+          'value' => 'de',
+        ),
+      ),
+      'name' => array(
+        array(
+          'value' => $values['name'],
+          'lang' => 'de',
+        ),
+        array(
+          'value' => $translation_values['name'],
+          'lang' => 'en',
+        ),
+      ),
+      'field_test_text' => array(
+        array(
+          'value' => $values['field_test_text']['value'],
+          'format' => $values['field_test_text']['format'],
+        ),
+      ),
+    );
+
+    $normalized = $this->container->get('serializer')->normalize($entity, $this->format);
+    $this->assertEqual($normalized['_links']['self'], $expected_array['_links']['self'], 'self link placed correctly.');
+    // @todo Test curies.
+    // @todo Test type.
+    $this->assertFalse(isset($normalized['id']), 'Internal id is not exposed.');
+    $this->assertEqual($normalized['uuid'], $expected_array['uuid'], 'Non-translatable fields is normalized.');
+    $this->assertEqual($normalized['name'], $expected_array['name'], 'Translatable field with multiple language values is normalized.');
+    $this->assertEqual($normalized['field_test_text'], $expected_array['field_test_text'], 'Field with properties is normalized.');
+    $this->assertEqual($normalized['_embedded'][$relation_uri], $expected_array['_embedded'][$relation_uri], 'Entity reference field is normalized.');
+    $this->assertEqual($normalized['_links'][$relation_uri], $expected_array['_links'][$relation_uri], 'Links are added for entity reference field.');
+  }
+
+  /**
+   * Constructs the entity URI.
+   *
+   * @param $entity
+   *   The entity.
+   *
+   * @return string
+   *   The entity URI.
+   */
+  protected function getEntityUri($entity) {
+    $entity_uri_info = $entity->uri();
+    return url($entity_uri_info['path'], array('absolute' => TRUE));
+  }
+
+}
diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
new file mode 100644
index 0000000..224586d
--- /dev/null
+++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\hal\Tests\NormalizerTestBase.
+ */
+
+namespace Drupal\hal\Tests;
+
+use Drupal\Core\Language\Language;
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Test the HAL normalizer.
+ */
+abstract class NormalizerTestBase extends DrupalUnitTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('entity_test', 'entity_reference', 'field', 'field_sql_storage', 'hal', 'language', 'rest', 'serialization', 'system', 'text', 'user');
+
+  /**
+   * The format being tested.
+   *
+   * @var string
+   */
+  protected $format = 'hal_json';
+
+  /**
+   * Overrides \Drupal\simpletest\DrupalUnitTestBase::setup().
+   */
+  function setUp() {
+    parent::setUp();
+    $this->installSchema('system', array('variable', 'url_alias'));
+    $this->installSchema('field', array('field_config', 'field_config_instance'));
+    $this->installSchema('user', array('users'));
+    $this->installSchema('language', array('language'));
+    $this->installSchema('entity_test', array('entity_test'));
+
+    // Add English as a language.
+    $english = new Language(array(
+      'langcode' => 'en',
+      'name' => 'English',
+    ));
+    language_save($english);
+    // Add German as a language.
+    $german = new Language(array(
+      'langcode' => 'de',
+      'name' => 'Deutsch',
+    ));
+    language_save($german);
+
+    // Create the test text field.
+    $field = array(
+      'field_name' => 'field_test_text',
+      'type' => 'text',
+      'cardinality' => 1,
+      'translatable' => FALSE,
+    );
+    field_create_field($field);
+    $instance = array(
+      'entity_type' => 'entity_test',
+      'field_name' => 'field_test_text',
+      'bundle' => 'entity_test',
+    );
+    field_create_instance($instance);
+
+    // Create the test entity reference field.
+    $field = array(
+      'translatable' => TRUE,
+      'settings' => array(
+        'target_type' => 'entity_test',
+      ),
+      'field_name' => 'field_test_entity_reference',
+      'type' => 'entity_reference',
+    );
+    field_create_field($field);
+    $instance = array(
+      'entity_type' => 'entity_test',
+      'field_name' => 'field_test_entity_reference',
+      'bundle' => 'entity_test',
+    );
+    field_create_instance($instance);
+  }
+
+}
diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManager.php b/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManager.php
new file mode 100644
index 0000000..3f5b947
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManager.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\rest\LinkManager\LinkManager.
+ */
+
+namespace Drupal\rest\LinkManager;
+
+class LinkManager implements LinkManagerInterface {
+
+  /**
+   * The type link manager.
+   *
+   * @var \Drupal\rest\LinkManager\TypeLinkManagerInterface
+   */
+  protected $typeLinkManager;
+
+  /**
+   * The relation link manager.
+   *
+   * @var \Drupal\rest\LinkManager\RelationLinkManagerInterface
+   */
+  protected $relationLinkManager;
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_link_manager
+   *   Manager for handling bundle URIs.
+   * @param \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_link_manager
+   *   Manager for handling bundle URIs.
+   */
+  public function __construct(TypeLinkManagerInterface $type_link_manager, RelationLinkManagerInterface $relation_link_manager) {
+    $this->typeLinkManager = $type_link_manager;
+    $this->relationLinkManager = $relation_link_manager;
+  }
+
+  /**
+   * Implements \Drupal\rest\LinkManager\TypeLinkManagerInterface::getTypeUri().
+   */
+  public function getTypeUri($entity_type, $bundle) {
+    return $this->typeLinkManager->getTypeUri($entity_type, $bundle);
+  }
+
+  /**
+   * Implements \Drupal\rest\LinkManager\RelationLinkManagerInterface::getRelationUri().
+   */
+  public function getRelationUri($entity_type, $bundle, $field_name) {
+    return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name);
+  }
+}
diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManagerInterface.php b/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManagerInterface.php
new file mode 100644
index 0000000..60e5629
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/LinkManager/LinkManagerInterface.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rest\LinkManager\LinkManagerInterface
+ */
+
+namespace Drupal\rest\LinkManager;
+
+/**
+ * Interface implemented by link managers.
+ *
+ * There are no explicit methods on the manager interface. Instead link managers
+ * broker the interactions of the different components, and therefore must
+ * implement each component interface, which is enforced by this interface
+ * extending all of the component ones.
+ *
+ * While a link manager may directly implement these interface methods with
+ * custom logic, it is expected to be more common for plugin managers to proxy
+ * the method invocations to the respective components.
+ */
+interface LinkManagerInterface extends TypeLinkManagerInterface, RelationLinkManagerInterface {
+}
diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php
new file mode 100644
index 0000000..78f5d7e
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rest\LinkManager\RelationLinkManager.
+ */
+
+namespace Drupal\rest\LinkManager;
+
+class RelationLinkManager implements RelationLinkManagerInterface{
+
+  /**
+   * Get a relation link for the field.
+   *
+   * @param string $entity_type
+   *   The bundle's entity type.
+   * @param string $bundle
+   *   The name of the bundle.
+   * @param string $field_name
+   *   The name of the field.
+   *
+   * @return array
+   *   The URI that identifies this field.
+   */
+  public function getRelationUri($entity_type, $bundle, $field_name) {
+    // @todo Make the base path configurable.
+    return url("rest/relation/$entity_type/$bundle/$field_name", array('absolute' => TRUE));
+  }
+
+}
diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManagerInterface.php b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManagerInterface.php
new file mode 100644
index 0000000..bd28432
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManagerInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rest\LinkManager\RelationLinkManagerInterface.
+ */
+
+namespace Drupal\rest\LinkManager;
+
+interface RelationLinkManagerInterface {
+
+  /**
+   * Gets the URI that corresponds to a field.
+   *
+   * @param string $entity_type
+   *   The bundle's entity type.
+   * @param string $bundle
+   *   The bundle name.
+   * @param string $field_name
+   *   The field name.
+   *
+   * @return string
+   *   The corresponding URI for the field.
+   */
+  public function getRelationUri($entity_type, $bundle, $field_name);
+}
diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManager.php b/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManager.php
new file mode 100644
index 0000000..7988bfe
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManager.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rest\LinkManager\TypeLinkManager.
+ */
+
+namespace Drupal\rest\LinkManager;
+
+class TypeLinkManager implements TypeLinkManagerInterface {
+
+  /**
+   * Get a type link for a bundle.
+   *
+   * @param string $entity_type
+   *   The bundle's entity type.
+   * @param string $bundle
+   *   The name of the bundle.
+   *
+   * @return array
+   *   The URI that identifies this bundle.
+   */
+  public function getTypeUri($entity_type, $bundle) {
+    // @todo Make the base path configurable.
+    return url("rest/type/$entity_type/$bundle", array('absolute' => TRUE));
+  }
+
+}
diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManagerInterface.php b/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManagerInterface.php
new file mode 100644
index 0000000..ba4dc3c
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/LinkManager/TypeLinkManagerInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rest\LinkManager\TypeLinkManagerInterface.
+ */
+
+namespace Drupal\rest\LinkManager;
+
+interface TypeLinkManagerInterface {
+
+  /**
+   * Gets the URI that corresponds to a bundle.
+   *
+   * When using hypermedia formats, this URI can be used to indicate which
+   * bundle the data represents. Documentation about required and optional
+   * fields can also be provided at this URI.
+   *
+   * @param $entity_type
+   *   The bundle's entity type.
+   * @param $bundle
+   *   The bundle name.
+   *
+   * @return string
+   *   The corresponding URI for the bundle.
+   */
+  public function getTypeUri($entity_type, $bundle);
+}
diff --git a/core/modules/rest/lib/Drupal/rest/RestBundle.php b/core/modules/rest/lib/Drupal/rest/RestBundle.php
index 49a1169..37db78e 100644
--- a/core/modules/rest/lib/Drupal/rest/RestBundle.php
+++ b/core/modules/rest/lib/Drupal/rest/RestBundle.php
@@ -32,5 +32,11 @@ public function build(ContainerBuilder $container) {
 
     $container->register('access_check.rest.csrf', 'Drupal\rest\Access\CSRFAccessCheck')
       ->addTag('access_check');
+
+    $container->register('rest.link_manager', 'Drupal\rest\LinkManager\LinkManager')
+      ->addArgument(new Reference('rest.link_manager.type'))
+      ->addArgument(new Reference('rest.link_manager.relation'));
+    $container->register('rest.link_manager.type', 'Drupal\rest\LinkManager\TypeLinkManager');
+    $container->register('rest.link_manager.relation', 'Drupal\rest\LinkManager\RelationLinkManager');
   }
 }
