diff --git a/includes/callback.inc b/includes/callback.inc
index 1efdf00..aa466af 100644
--- a/includes/callback.inc
+++ b/includes/callback.inc
@@ -181,6 +181,12 @@ function js_callback_bootstrap(array &$info) {
         module_invoke($module, 'init');
       }
     }
+
+    // At this point in the execution flow it is safe to allow our cache handler
+    // to perform a full bootstrap in case of cache misses.
+    if ($info['cache']) {
+      JsProxyCache::setFullBootstrapAllowed(TRUE);
+    }
   }
 }
 
diff --git a/js.api.php b/js.api.php
index a4115bf..d150b57 100644
--- a/js.api.php
+++ b/js.api.php
@@ -53,11 +53,23 @@
  *     instance, loading an entity when entity cache is cold may result in some
  *     data not being loaded and entity cache being corrupt; saving that entity
  *     in subsequent requests may even lead to data loss, if the cache entry was
- *     not refreshed meanwhile. A possible solution is raising the bootstrap
- *     level to full, although this defeats the purpose of using this module.
- *     An alternative solution is monitoring the code paths triggered by the
- *     callback via the "xhprof" integration and make sure all required
- *     dependencies are actually loaded.
+ *     not refreshed meanwhile.
+ *     Note: by default JS Callback intercepts requests via core's Cache API.
+ *     When a cache miss is detected, it will automatically perform a full
+ *     bootstrap in an attempt to ensure all modules affecting the data to be
+ *     cached are loaded. See "cache" property for more info.
+ *     This does not, however, solve the general issue where a complex callback
+ *     performs a storage write via an API that allows data to be altered via
+ *     hook implementations. In cases like this all dependencies need to be
+ *     explicitly loaded.
+ *     A temporary solution is to raise the bootstrap level to full. However,
+ *     this defeats the entire purpose of using this module.
+ *     A more permanent solution is to monitor the execution path of a callback
+ *     via the "xhprof" integration and ensure all required dependencies are
+ *     added to the callback info.
+ *   - cache: (optional) Flag indicating whether a full bootstrap should be
+ *     performed when detecting a cache miss. Defaults to TRUE. See "bootstrap"
+ *     property for more info.
  *   - includes: (optional) Load additional files from the /includes directory,
  *     without the extension.
  *   - dependencies: (optional) Load additional modules for this callback.
diff --git a/js.module b/js.module
index 739489b..d6bd9f5 100644
--- a/js.module
+++ b/js.module
@@ -488,10 +488,8 @@ function js_execute_request() {
     register_shutdown_function('_js_fatal_error_handler');
   }
 
-  // Memcache requires an additional bootstrap phase to access variables.
-  if (!empty($conf['cache_default_class']) && $conf['cache_default_class'] === 'MemCacheDrupal') {
-    js_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);
-  }
+  // Initialize the cache system and our custom handler.
+  _js_cache_initialize();
 
   // Immediately clone the request method so it cannot be altered any further.
   static $method;
@@ -605,6 +603,41 @@ function js_execute_request() {
 }
 
 /**
+ * Initializes the cache system and our custom handler.
+ */
+function _js_cache_initialize() {
+  global $conf;
+
+  // We do not rely on autoloading as it may trigger a DB bootstrap.
+  module_load_include('php', 'js', 'src/JsProxyCache');
+
+  // Collect all the explicitly configured cache bins.
+  $default_key = JsProxyCache::DEFAULT_BIN_KEY;
+  $cache_bin_keys = array_values(array_filter(array_keys($conf), function ($key) {
+    return strpos($key, 'cache_class_') === 0;
+  }));
+  $cache_bin_keys[] = $default_key;
+
+  // Save the current configuration and override it to make sure an instance of
+  // our custom wrapper is instantiated for each configured bin.
+  $cache_conf = array();
+  $default_class = isset($conf[$default_key]) ? $conf[$default_key] : 'DrupalDatabaseCache';
+  foreach ($cache_bin_keys as $bin_key) {
+    $cache_conf[$bin_key] = isset($conf[$bin_key]) ? $conf[$bin_key] : $default_class;
+    $conf[$bin_key] = 'JsProxyCache';
+  }
+
+  // Finally ensure our custom wrappers know which actual cache backend they are
+  // supposed to use.
+  JsProxyCache::setConf($cache_conf);
+
+  // Memcache requires an additional bootstrap phase to access variables.
+  if (!empty($cache_conf[$default_key]) && $cache_conf[$default_key] === 'MemCacheDrupal') {
+    js_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);
+  }
+}
+
+/**
  * Sends content to the browser via the delivery callback.
  *
  * @param mixed $result
@@ -784,6 +817,7 @@ function js_get_callback($module = NULL, $callback = NULL, $reset = FALSE) {
             'access arguments' => array(),
             'access callback'  => FALSE,
             'bootstrap' => DRUPAL_BOOTSTRAP_DATABASE,
+            'cache' => TRUE,
             // Provide a standard function name to use if none is provided.
             'callback function' => $_module . '_js_callback_' . $_callback,
             'callback arguments' => array(),
diff --git a/src/JsCacheProxy.php b/src/JsCacheProxy.php
new file mode 100644
index 0000000..7ed14c2
--- /dev/null
+++ b/src/JsCacheProxy.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * JS custom cache handler.
+ *
+ * This is just a proxy for the actually configured cache backend, that ensures
+ * that Drupal is fully bootstrapped if an item cannot be retrieved from cache.
+ * By loading all hook implementations this avoids the risk of having incomplete
+ * or corrupt cache entries stored during a callback execution.
+ */
+class JsProxyCache implements DrupalCacheInterface {
+
+  const DEFAULT_BIN_KEY = 'cache_default_class';
+
+  /**
+   * The cache bin configuration.
+   *
+   * @var string[]
+   */
+  protected static $conf;
+
+  /**
+   * Flag indicating whether a full bootstrap may be performed.
+   *
+   * @var bool
+   */
+  protected static $fullBootstrapAllowed = FALSE;
+
+  /**
+   * The actual cache backend.
+   *
+   * @var DrupalCacheInterface
+   */
+  protected $backend;
+
+  /**
+   * Sets the cache bin configuration.
+   *
+   * @param string[] $conf
+   *   An associative array of cache backend class names keyed by their cache
+   *   bin name.
+   */
+  public static function setConf(array $conf) {
+    static::$conf = $conf;
+  }
+
+  /**
+   * Sets the flag indicating whether a full bootstrap can be performed.
+   *
+   * @param bool $allowed
+   *   The full bootstrap flag.
+   */
+  public static function setFullBootstrapAllowed($allowed) {
+    static::$fullBootstrapAllowed = $allowed;
+  }
+
+  /**
+   * Constructs a JS cache handler.
+   *
+   * @param string $bin
+   *   The cache bin name.
+   */
+  public function __construct($bin) {
+    if (isset(static::$conf[static::DEFAULT_BIN_KEY])) {
+      $class = isset(static::$conf[$bin]) ? static::$conf[$bin] : static::$conf[static::DEFAULT_BIN_KEY];
+      $this->backend = new $class($bin);
+    }
+    else {
+      throw new LogicException('The JS cache handler was not properly configured');
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($cid) {
+    $cache = $this->backend->get($cid);
+    if (!$cache) {
+      $this->doFullBootstrap();
+    }
+    return $cache;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMultiple(&$cids) {
+    $cache = $this->backend->getMultiple($cids);
+    if ($cids) {
+      $this->doFullBootstrap();
+    }
+    return $cache;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($cid, $data, $expire = CACHE_PERMANENT) {
+    $this->backend->set($cid, $data, $expire);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clear($cid = NULL, $wildcard = FALSE) {
+    $this->backend->clear($cid, $wildcard);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    return $this->backend->isEmpty();
+  }
+
+  /**
+   * Fully bootstraps Drupal.
+   */
+  protected function doFullBootstrap() {
+    if (static::$fullBootstrapAllowed) {
+      static::setFullBootstrapAllowed(FALSE);
+      if (drupal_get_bootstrap_phase() < DRUPAL_BOOTSTRAP_FULL) {
+        js_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+      }
+    }
+  }
+
+}
