Decoupled Drupal is no longer a new idea. We are past the hype stage. Numerous large projects have been built following this architectural pattern over the past years, Dries has blogged about it several times, and Drupal 8’s ongoing API-first initiative has helped facilitate and improve the approach. One consistent problem encountered by those interested in exploring decoupled Drupal, however, is that guides for full end-to-end implementation best practices have been sorely lacking. Different companies have been solving and re-solving the same problems in isolation. But then, along came Contenta.
What is Contenta?
Contenta is the community-led API-first Drupal distribution, born out of discussions at DrupalCon Baltimore. Its goal is to lower the barrier to entry for developing decoupled Drupal sites, as well as showcase best practices for doing so. With a few commands you can now spin up a Drupal 8 backend with the JSON API module and even default data models and content, so you can begin writing a consuming application immediately! Numerous example applications have already been built to showcase different technologies, like Elm, Ember, Ionic, React, Vue, and even A-Frame WebVR. For the past several weeks I have been helping build the Contenta Angular application, and am excited to share some of the progress made so far.
What is Contenta Angular?
Contenta Angular, as the name implies, is an Angular application consuming Contenta’s APIs. The latest stable version of it can be viewed live in the wild at any time here, thanks to our continuous integration setup. At a glance, it may just look like a recipe site, but there is a lot more going on than may be obvious. Let’s take a quick look under the hood at what makes it unique.
We built this application using Angular's CLI, which gives developers a lot of tools to scaffold out new components, run tests, spin up a development environment, and much more. Playing around with the CLI is a great way to introduce yourself to Angular’s power without getting overwhelmed (as with Drupal, there’s a lot to learn!), so integrating it was essential to our goal of lowering barriers.
Service workers can do some really neat things, and we are using the Angular service worker to handle much of our configuration automatically. One special detail - our service worker is caching our external API calls and images loaded from a remote server, so the entire site works offline for repeat visits, even the parts we request from elsewhere on the web!
Progressive Web App (PWA)
Aside from the offline capabilities, we have added other PWA behaviors as well. Using a manifest file, we set up basic information about our app like theme colors and icons. This enables a repeat visitor from a mobile device to be prompted to add the site as an installable web app, which will behave like any other app on their phone. Once added, when a visitor opens the app a custom loading dashboard will be presented.
Firebase, our demo host, runs on HTTP/2 by default, so we can take advantage of new capabilities like server push. Our app, with the help of a small config library, is ready to begin pushing all essential js and css assets to you as soon as your initial request is made!
Our code repository is integrated with Travis CI. Each time a pull request is created, we run lint, unit tests, end to end tests, and code coverage reports. Once the PR is merged, it will trigger an automatic deployment to our development Firebase instance. Once changes are merged into the master branch, it triggers a final deployment to production.
Our state management solution follows best practices with the latest ngrx 4 library, based largely on the recent writing on the topic by Victor Savkin. This approach elevates the router as the source of truth for some state, and follows the Redux pattern of using functional reducers to determine a new state from an action. Things like remote API calls are treated as side effects, which are triggered by an optimistic state change. If, however, that API call fails, ngrx can be alerted and the state change reversed, keeping everything in perfect sync.
That may sound complex, but the end result is a careful and scalable approach to the various types of state information our app needs to accommodate, while also allowing us to do neat things like time travel debugging. It can be seen in action most readily by playing with the filters on our site - change a filter and it changes the URL in the address bar, which then triggers an action, a reducer, and any effects. For the site as it exists today, it might be argued that this introduces more complexity than value, but the intent is to showcase something that gives us a solid foundation, and we’ve got big plans for our use of ngrx4 down the road.
Giving Back to Drupal
The Contenta project is deeply integrated with the Drupal 8 API-First Initiative, and as such part of our role as the builders of a consuming application is to help give feedback on where gaps in desirable API functionality may be, and generally help validate changes as Drupal 8 (and beyond) evolve to be ever more suited to decoupled uses. The codebase of the project itself is, of course, open sourced from day one and freely available for anyone to fork or submit issues / PRs against. Further, we already have some ideas for turning useful pieces of the app into generalized libraries that can assist anyone building a decoupled Drupal + Angular project.
Giving Back to Angular
There is another exciting role Contenta Angular will be playing with regards to helping make Angular better for everyone. We have a special branch of our repo and a site instance that runs the latest master branch of Angular. We are one of a growing number of partners working with the core Angular team to integrate with webhooks on their repo, so that any time a new commit is made to the Angular project, we will kick off a full build and deployment then report back any issues.
I’m also involved in an ongoing conversation and informal working group discussing how to improve Angular’s approach to building API-driven applications. As part of that effort I’m looking into ways the ideas we are incubating in Contenta Angular can help many kinds of Angular applications, whether they’re backed with Drupal, WordPress, LoopBack, or any other type of API.
I’ve touched on the highlights, but there are even more great features that I could mention. From the lovely developer experience provided by the use of TypeScript and dependency injection, to the cleanly abstracted visual design afforded us by Material, we are taking advantage of community best practices wherever we can. I’m tempted to mention that we are already running Angular 5.x on our production site, but of course, it’s just Angular now so version numbers mean less and less in terms of breaking changes.
We've got a lot of great things coming, and our public roadmap is the place to track them. We are currently working on adding Angular Universal, Angular’s server side rendering, to our stack, so our initial page loads will be even faster. We’ll also be adding some UX features like infinite scroll, more filters, and category pages soon, as well as improving our existing pages. In the medium-to-longer term, we are exploring adding authentication and editorial forms to our app, so that you will be able to login, edit and add new content to the Drupal site from within Angular.
If it’s not obvious, both the Contenta project as a whole and Contenta Angular in particular have a lot of community momentum behind them, and it is a great time to get involved. As I mentioned at the outset, our goal is to showcase best practices and lower the barriers to entry for getting going with decoupled Drupal. To that end, I encourage you to reach out to us in the Drupal Slack’s #contenta channel, take our code for a spin and report any issues or ideas so we can continue to improve the project together.