This post will walk you through how to get a Drupal 8 app server going with a AngularJS client side theme. The instructions were written as I went through the process on OS X, but are hopefully clear enough to work on other OSes and web servers (I used apache). And if you are worried about google crawling your site, see

What I am hoping to get out of this post is to spark more interest in the Drupal community (see about separating out the theme layer from Drupal so we can bootstrap the core to a lower layer (like DATABASE and/or SESSION) rather than loading all layers. This way we can get the best performance out of Drupal and achieve a clean Separation of Concerns (SoC). Let's make it easy for designer types to do their thing and maybe we can get pretty sites too.

When done we will have one site running Drupal as the app/api server and another site running the client angular app (which could be hosted on a CDN). The angular app is on github at and can be used as a starting point. Hopefully it can build over time to include all of the core TPLs. As modules develop we can create other github(?) projects for those module's TPLs and easily load them into the angular app. Fork it or download it and build your theme.


Let me try to explain to you, what to my taste is characteristic for all intelligent thinking. It is, that one is willing to study in depth an aspect of one's subject matter in isolation for the sake of its own consistency, all the time knowing that one is occupying oneself only with one of the aspects. We know that a program must be correct and we can study it from that viewpoint only; we also know that it should be efficient and we can study its efficiency on another day, so to speak. In another mood we may ask ourselves whether, and if so: why, the program is desirable. But nothing is gained —on the contrary!— by tackling these various aspects simultaneously. It is what I sometimes have called "the separation of concerns", which, even if not perfectly possible, is yet the only available technique for effective ordering of one's thoughts, that I know of. This is what I mean by "focusing one's attention upon some aspect": it does not mean ignoring the other aspects, it is just doing justice to the fact that from this aspect's point of view, the other is irrelevant. It is being one- and multiple-track minded simultaneously. - Edsger W. Dijkstra

It would not be a bad thing update brew and upgrade at least the node formula:
$ brew update
$ brew doctor

If the doctor says you have problems, now is a good time to fix them. Next, at least upgrade node to the latest version:
$ brew upgrade node
or to upgrade everything:
$ brew upgrade

If you already have a drupal 8 site running with REST Web Services, you skip down to the angular client section.

Git and install Drupal 8

Get the latest Drupal 8 and put it into the d8server directory:
$ git clone --branch 8.x d8server
$ cd d8server

Copy over the sites/default/default.settings.php to sites/default/settings.php and make it writable by apache.
$ cp sites/default/default.settings.php sites/default/settings.php
$ sudo chown _www sites/default/settings.php

Create the sites/default/files directory and make it writable by apache.
$ mkdir sites/default/files
$ sudo chown _www sites/default/files

Setup a virtual host (mine will be d8server.local) create a database (mine is named d8server).
$ sudo vi /etc/hosts
Add a line for your new local site: d8server.local
Add a virtual host to apache:
$ sudo vi /etc/apache2/extra/httpd-vhosts.conf

<VirtualHost *:80>
    ServerAdmin rkoberg
    DocumentRoot "/Users/rkoberg/Sites/d8server"
    ServerName d8server.local
    <Directory "/Users/rkoberg/Sites/d8server">
      Options Indexes FollowSymLinks Includes ExecCGI
      AllowOverride All
      Order deny,allow
      Allow from all
    ErrorLog "/private/var/log/apache2/d8server-error_log"
    CustomLog "/private/var/log/apache2/d8server-access_log" common

Restart apache:
$ sudo apachectl restart
Visit the site at http://d8server.local. What? I have a fatal error:
Parse error: syntax error, unexpected '[' in /Users/rkoberg/Sites/d8server/core/vendor/guzzlehttp/guzzle/src/functions.php on line 20
Oh, right... I was working on a Drupal 7 site which requires php53 and Drupal 8 requires php54 or better. So, I will install php55 using homebrew:
$ brew install php55
Now make sure the apache conf is pointing to the correct lib file for php55. Hmmm... homebrew did not set that up for me. I will comment out the php53 (will need it again for D7 dev work) and add php55:

#LoadModule php5_module /usr/local/Cellar/php53/5.3.28/libexec/apache2/
LoadModule php5_module /usr/local/Cellar/php55/5.5.12/libexec/apache2/

and restart apache. (if copying and pasting here, make sure of the version homebrew got for you)

That was a pain. There is a better way using vagrant and virtualbox to create a virtual machine for each site. It is very handy, but I won't get into that here. But there are lots of recipes out there to get sites up and running quickly and run them on OSes that are exactly like your production environments. You can even use vagrant to provision up an AWS EC2 instance or instances. Check out

Visiting the local site now gives me the Drupal install wizard. (if you did not create your DB, settings.php and files directory, do that now)

Create/edit your first article at http://d8server.local/node/add/article. Note: I am adding an image and a tag. You should too.
My first drupal 8 article
Next enable the core REST modules at http://d8server.local/admin/modules
Drupal 8 Core REST modules
We will need to add a bit of code to allow our client site/app to communicate with our server site/app so we do not get cross-origin resource scripting errors in browsers. There is a D7 module for CORS, but it hasn't been ported to D8 yet. I will take a shortcut and which will enable any site to request the d8server resources. In your drupal .htaccess file, add this line:
Header set Access-Control-Allow-Origin "*"
For more info, try

Now let's try to access our node RESTfully:
$ curl -i -H "Accept: application/json" http://d8server.local/node/1
Hmmm... that gives me an empty JSON object '{}'. Let's try to get HAL (Hypertext Application Language) JSON:
$ curl -i -H "Accept: application/hal+json" http://d8server.local/node/1
Hmmm... that give me an error:
A fatal error occurred: No authentication credentials provided.
Hmmm... time for the google. Ahhh... of course, it is a permissions issue, so go to http://d8server.local/admin/people/permissions and check "Anonymous user" for "Access GET on Content resource" under the section labeled "RESTful Web Services". Google also tells me that application+hal is enabled by default. If we want to change these things and make other entities available for RESTful requests we can either edit sites/default/files/config_XXXX/active/rest.settings.yml or install a contrib module called restui. (See and

Now that I have set the permissions, the last curl hal+json request returns node/1 as HAL JSON. That is enough Drupal for now. (I also installed the restui contrib module)

AngularJS Drupal client app/site

Basic setup

To make this simple and get started quickly with a bunch of tools and best practices let's install some things. First, if you don't have nodejs installed on your system, do that by:
$ brew install node
This also gives you the node package manager npm, which we will use to install some very useful tools.

Now that node is installed grab the tools:
$ npm install -g yo grunt-cli bower generator-angular
Create a directory for your project and go into it:
$ mkdir d8client && cd $_
Create the angular app:
$ yo angular d8client
and hit enter (Y) through the questions to get the defaults.

Now you have a well thought out starting point for the client side app. Among other things you can use Grunt to serve your site. It will build your SASS, JS and others for you on change. When it sees a change, it will reload the page for you. To start the server, run:
$ grunt serve

Use git, gitflow and github to version our code

Let's put this in git and use gitflow. If you are not familiar with gitflow, read (just use it, it is good for you and your team). If you do not have gitflow:
$ brew install git-flow
yo angular created a .gitignore file for you that excludes a bulk of the files so that only your app is versioned. Since I use IntelliJ IDEA, I want to add those files to the .gitignore now. I add these lines:


Next, let's init the repo (just hit enter to accept the defaults):
$ git flow init
This will drop you into the develop branch. Let's first check what we will be adding to the repo:
$ git status
If you see anything that should not go into the repo, add it to your .gitignore. Now let's add files, commit them, and hook it up to a repo at github (my project is at
$ git add -A .
$ git commit -m "initial commit"
$ git remote add origin

The git remote add origin ... is unique to my project. You can create your own project at github and copy the URL from under the "HTTPS clone URL".
github clone URL
Now we can push our develop branch off to github:
$ git push origin develop
Refresh your github page and make sure to select the develop branch to see your files (the page will default to the master branch).

Back to AngularJS and building the Single Page App (SPA)

First thing we want to do is handle all of our node pages that will be served from /node/[nid]. The following can easily be done manually, but let's use yeoman and generator-angular to do it for us (checkout the other generators at
yo angular:route node
Which generates the following:


and adds a route to our app/scripts/app.js that looks like:

      .when('/node', {
        templateUrl: 'views/node.html',
        controller: 'NodeCtrl'

We want to add the node ID as a parameter to this, which looks like:

      .when('/node/:nid', {
        templateUrl: 'views/node.html',
        controller: 'NodeCtrl'

In our app/scripts/controllers/node.js, change it to look like:

  .controller('NodeCtrl', ['$scope', '$routeParams', function ($scope, $routeParams) {

    var nid = $routeParams.nid;
    console.log('nid', nid);


We have changed a few things here. First we made the second argument to the controller an array and put the params to the function first as strings. You want to do this for when we build our app and the JS get minified (trust me). We added the $routeParams which gives us access to the route parameters, namely our node ID. Visit http://localhost:9000/#/node/1 and check out the console to verify we have the node ID. Next we have to change app/scripts/app.js first to handle minification and add a global Accept header to all our requests. This can be done each time we make a request or overriden on some, if we want. Modify the app.js to:

  .config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) {

    $httpProvider.defaults.headers.common.Accept = 'application/hal+json';

      .when('/', {

Now let's add a request to GET the node from Drupal in the app/scripts/controllers/node.js:

  .controller('NodeCtrl', ['$scope', '$routeParams', '$http', function ($scope, $routeParams, $http) {

    var nid = $routeParams.nid;
    console.log('nid', nid);

    $http.get('http://d8server.local/node/' + nid).then(function(response) {
      console.log('NodeCtrl GET response', response);


Now check out the console to see the response from Drupal. If you got an error Access-Control-Allow-Origin, make sure you added Header set Access-Control-Allow-Origin "*" to your .htaccess as mentioned above or follow instructions here. You should be seeing the returned node hal+json in the console, which we can inspect to set up $scope variables for our template and looks like:
Chrome console output for node/1
Drill in and inspect the returned object. Let's make it easy for our template and 'control' what can be used in our HTML template. We will duplicate the default node view eventually, but let's keep it simple for now and just add the type, title and body:

  .controller('NodeCtrl', ['$scope', '$routeParams', '$http', function ($scope, $routeParams, $http) {

    var nid = $routeParams.nid;

    $http.get('http://d8server.local/node/' + nid).then(function(response) {
      console.log('NodeCtrl GET response', response);
      var data =;
      $scope.type = data.type[0].target_id;
      $scope.title = data.title[0].value;
      $scope.body = data.body[0].value;
      console.log('NodeCtrl GET $scope', $scope);

And we can set up the app/views/node.html template like:

<article class="">
  <div ng-bind-html="body"></div>

We need to use the ng-bind-html attribute for the body because it contains markup. If we don't use that the markup will display escaped. But there is a problem: We don't have the image data available. We also only have a reference to the taxonomy term. There has to be a way to get that information in the node request, right? I am not seeing anything in permissions related to files/images and REST services. Let's check google.

...Ufff.... after about 6 hours of trying to figure out how to get the image and taxonomy term data, I give up for now.