A short history of Headless Drupal
The term Headless Drupal may be relatively new to the Drupalverse, but there have been Headless Drupal applications all the way back to Drupal 5 and although the iterations of Headless Drupal are different they are all revolving around a basic concept that should be kept in mind when you are planning a Headless Drupal project. That concept is providing an excellent front end and back end experience, while at the same time leveraging the power that Drupal provides with its large library of contributed modules, core features, and user permissions system. In order to accomplish this objective, you need to take a summary of the many technologies that can be paired with Drupal 8 in a headless or modified headless Drupal configuration.
These include but are not limited to:
- Headless Drupal Applications built with Angular.js
- Headless Drupal Applications built with React
- Headless Drupal Applications built with Java
- Headless Drupal Applications built with Node.js
- Headless Drupal Applications built with Enchant UI
- Headless Drupal Applications built with Flash or Flex
Each of those will go deeper in that line of talk regarding the specific technology and what you need to set up on the consumer for the Headless Drupal 8 site. In this series, I will be just talking about the various things you can set up on the Drupal 8 side in order to get prepared for planning that awesome Drupal 8 headless site your client is expecting, including using headless Drupal to perform rapid prototyping. In this article, while talking about Headless Drupal 8 to try and minimize confusion across various technologies when I refer to the consumer I am talking about the application that will be calling back to your Headless Drupal 8 site in order to receive data. When I talk about the provider during this article I am talking about the actual Headless Drupal 8 site that will be serving your application needs. So without further delay lets start.
Handling Headless Drupal 8 consumers and Configuring CORS
For many technologies, you will need to make sure that you are properly configuring CORS to allow your Headless Drupal 8 system to serve your consuming applications. For Apache, and Nginx this is a relatively simple task of adding an HTTP header to your response from Drupal 8.
Headless Drupal 8 CORS configuration for Apache 2:
Header set Access-Control-Allow-Origin "*"
Headless Drupal 8 CORS header via Nginx:
# # Wide-open CORS config for nginx # location / { if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; # # Om nom nom cookies # add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # # Custom headers and headers various browsers *should* be OK with but aren't # add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; # # Tell client that this pre-flight info is valid for 20 days # add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain charset=UTF-8'; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; } }
Adding a CORS header via PHP (Handy if you want to handle this all from within your main Drupal 8 Headless provider module).
Out of the three options I listed above for setting up Headless Drupal 8 CORS I tend to prefer the latter option, setting the CORS via PHP. I feel like embedding this about your Core module that will be providing the API is the way to go. Even better is if your planning to open up your Drupal 8 Headless provider to the public you could create a simple module for managing who does and does not have access to the API, allowing for more advanced controls like Rate Limiting or Throttling similar to how Facebook, Twitter, and other public API systems handle things. The thing to remember with Headless Drupal 8 is you are creating an API and then doing what is called dogfooding your own API, and there are many great reasons you should dog food your own API.
Developing your Headless Drupal 8 Provider
With this setup out of the way, the next concern is setting up a structure for your End-points that will make sense and will be future-proofed when you need to make changes for business needs. I recommend using a versioned API structure from the beginning allowing your to set release points when you need to change how your API delivers information. In addition to this, I also recommend having a method of signaling what format you want your data to be returned in. It should be a simple matter in your API to output XML, JSON, or even CSV data so that the consumer of your Headless Drupal 8 data will be free to choose the format of the data.
You should try to follow some basic best practices:
- Use Web Standards where they make sense
- Make it Easily browsable and explorable via a browser address bar
- Make it Simple, Intuitive, and Consistent so that Adoption is easy to process
- It should be flexible and allow powering things like Enchant
- It should be efficient and maintain balance with other requirements
Remember that an API is your developer's UI, so like any UI, it is important to factor in the user's experience. Make sure that while you are building the RESTful provider that you properly utilize RESTful URLs and actions. There are a lot of great resources out on this topic, one that is constantly referred to is the Architectural Styles and the Design of Network-based Software Architectures dissertation given by Roy Thomas Fielding way back in 2000, but a majority of the principles outlined still hold true today.
In addition to the above, I suggest building out an API that has a central resource that then is built atop and extended based on the resources you wish to expose to the public. I will go more in-depth into creating a custom module that can be the base for your API in a later blog post, but for now, this post is more of an overview of all of these topics and kind of an index for the others.
Creating a Rate Limited API
So there is an importance of monitoring the usage of your API, this is good from a business and marketing perspective if your creating an Application that has a publicly accessible API, then at some point, you could build enough value to offer it as a SaaS product. In order to do that you need to have a method of adding domains/consumers to your list of allowed Coors. One of the easiest ways to accomplish this is to have a simple configuration entity that tracks API keys and Secrets. You can then associate a usage count with that information. You could build mechanisms that will handle an hourly period, 24 hour period, etc. This allows you to make sure that your API is able to service your mission critical sites while also allowing some access to the public. I will go more in-depth on creating a public rate-limited API in a later blog post.
User Sessions and Permissions
Another one of what I think is the biggest benefits of having a hybrid headless Drupal application is being able to utilize a centralized user permissions system based in Drupal. I think Drupal was revolutionary when it comes to its user permissions. When I first started developing websites you had to manually code permissions, roles, and track your users against it. With Drupal's permissions API it is pretty much done for you, so really all your application needs to be able to do is create and track sessions created in Drupal and then use that session ID/user ID to do occasional permission checks in your application where you need it. Again this is a way of having your critical data centralized in Drupal and then using an application like Angular, Node, or React to render that information properly, which I think should be the end goal for most of the headless Drupal 8 applications out there. I will get more into Headless Drupal 8 Sessions and Headless Drupal 8 Permissions in a later blog post.
Comparisons with Drupal 8 and a Headless Drupal 8 project
So in comparison to having solely a Drupal 8 site, or Headless Drupal 8 project, or even just going with a Mean application for your project. You can easily double your development time with a Headless Drupal 8 site if you do not make sure to leverage Drupal 8's API in every instance that you can for your application. If you are rewriting things like User permissions, sessions, or other logic then you may be better off building the application as just a Mean application, but again your looking at losing a large community that is supporting those core API functionalities in Drupal 8 and back to maintaining a project 100% on your own. Whereas if you're using Drupal 8's API to serve your headless Drupal 8 application you gain a large number of dev hours at little to no cost.
I think comparing a plain Drupal 8 site to a headless Drupal 8 site can be trickier because with Twig and the new Symfony API everyone is really on the same page as far as experience with building Drupal sites, but you can take advantage of a team that has heavy knowledge of various front-end technologies that Drupal 8 does not naturally service if you choose to go headless. You can also build your front end application in a vacuum while your backend team is building the various entities or other back-end modules required to service your application. So with this, you could do rapid prototyping with something like angular.js and node.js while your team on the back end focuses on building error-proof code.