diff --git a/modules/jsonapi_twig/jsonapi_twig.info.yml b/modules/jsonapi_twig/jsonapi_twig.info.yml
new file mode 100644
index 0000000..5f772b4
--- /dev/null
+++ b/modules/jsonapi_twig/jsonapi_twig.info.yml
@@ -0,0 +1,3 @@
+name: JSON Twig
+core: 8.x
+type: module
diff --git a/modules/jsonapi_twig/jsonapi_twig.services.yml b/modules/jsonapi_twig/jsonapi_twig.services.yml
new file mode 100644
index 0000000..d7379a2
--- /dev/null
+++ b/modules/jsonapi_twig/jsonapi_twig.services.yml
@@ -0,0 +1,5 @@
+services:
+  jsonapi_twig.twig_extension:
+    class: \Drupal\jsonapi_twig\TwigExtension
+    tags:
+      - { name: twig.extension }
diff --git a/modules/jsonapi_twig/src/TwigExtension.php b/modules/jsonapi_twig/src/TwigExtension.php
new file mode 100644
index 0000000..25878eb
--- /dev/null
+++ b/modules/jsonapi_twig/src/TwigExtension.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Drupal\jsonapi_twig;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
+class TwigExtension extends \Twig_Extension {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return 'jsonapi';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFunctions() {
+    return [
+      new \Twig_SimpleFunction('api', [$this, 'fetchFromJsonApi']),
+      new \Twig_SimpleFunction('jsonapi', [$this, 'fetchFromJsonApi']),
+    ];
+  }
+
+  public function fetchFromJsonApi($resource, array $options = []) {
+    if ($resource instanceof EntityInterface) {
+      $resource = "/{$resource->getEntityTypeId()}/{$resource->bundle()}/{$resource->uuid()}";
+    }
+    /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $kernel */
+    $kernel = \Drupal::service('http_kernel.basic');
+
+    $request = Request::create('/jsonapi/' . ltrim($resource, '/'), 'GET', $options + ['_format' => 'api_json']);
+
+//    if (is_array($options['fields'])) {
+//      $request->query->set('fields', $options['fields']);
+//    }
+
+    // @todo Drop this workaround when https://www.drupal.org/node/2613044 is
+    // done
+    $previous_request = \Drupal::requestStack()->getCurrentRequest();
+
+    // @ Find a nicer implementation with maybe less layers involved.
+    $response = $kernel->handle($request, HttpKernelInterface::SUB_REQUEST, TRUE);
+
+    \Drupal::requestStack()->push($previous_request);
+
+    // @todo How to take into account cacheability metadata.
+    return Json::decode($response->getContent());
+  }
+
+
+}
diff --git a/tests/themes/jsonapi_twig_test/jsonapi_twig_test.info.yml b/tests/themes/jsonapi_twig_test/jsonapi_twig_test.info.yml
new file mode 100644
index 0000000..b9f8af7
--- /dev/null
+++ b/tests/themes/jsonapi_twig_test/jsonapi_twig_test.info.yml
@@ -0,0 +1,4 @@
+name: JSONAPI twig test
+core: 8.x
+package: Testing
+type: theme
diff --git a/tests/themes/jsonapi_twig_test/templates/node--article.html.twig b/tests/themes/jsonapi_twig_test/templates/node--article.html.twig
new file mode 100644
index 0000000..17ddc1d
--- /dev/null
+++ b/tests/themes/jsonapi_twig_test/templates/node--article.html.twig
@@ -0,0 +1,8 @@
+{#
+// Theoretically this would work for sparse fieldsets.
+{% set data = api(node, {fields: {'node--article': 'title'}}) %}
+#}
+{% set data = api(node) %}
+{#{{ dump(data) }}#}
+<h2>Title: {{ data.data.attributes.title }}</h2>
+<strong>Created: {{ data.data.attributes.created | format_date }}</strong>
