Enabling headless Drupal Commerce while improving its core

The Drupal community has been abuzz for the past two years with talk of "Becoming Headless" or "Decoupling All The Things." The trend raises reasonable questions from end users who feel this means Drupal is moving into a space that no longer represents them. We hear similar concerns from Drupal Commerce users when we talk about delivering headless commerce. However, we don't believe anyone should be worried that improving support for Drupal as a REST API server will detract from efforts to improve its utility as a traditional CMS.

From our perspective, you can (and we do) support at the same time both traditional applications, where Drupal itself provides the front end, and headless applications, where a JavaScript application renders data served from a Drupal powered REST API. In fact, in this post I'll demonstrate how supporting the latter has actually improved Drupal (and Drupal Commerce) for everyone. Headless initiatives help uncover bugs and fine-tune the underlying application architecture.

Drupal core, API-First, and headless commerce

When you remove the default presentation layer from a system, you are bound to find interesting problems. How much logic is embedded in a Drupal field formatter or field widget? What happens when those are not used to render or manipulate an entity’s values? As a quick example, work in the API-First Initiative related to taxonomy terms turned up a validation bug where code restricting parent terms to the same vocabulary only executed in the context of the default entity form. Fixing that bug to prevent invalid relationships via API updates contributed to improving Drupal core overall even though the issue wasn't affecting traditional Drupal usage.

We're looking for similar wins as we explore improving Drupal Commerce to support headless commerce applications. Not only will it make our code more disciplined, it will help us improve the user experience of the standard modules themselves through the use of JavaScript. I will be speaking about the process a little at DrupalCon Seattle in a session titled Delivering Headless Commerce.

In early 2018 I wrote about why Drupal Commerce needs the API-First initiative and JavaScript Modernisation Initiatives. The outcome a short time later was our progressively decoupled Cart Flyout module which provided a JavaScript enabled take on managing cart interactions. By the end of last summer, the module included an complete replacement for the Add to Cart form that just used JavaScript. This module does not require a fully decoupled architecture but still provides important performance and scalability enhancements to every Drupal Commerce user. However, it did come from our efforts to support a fully headless architecture.

Consider a couple examples of our work toward a fully headless Drupal Commerce improving the modules more generally:

Cart should grant "view" access to orders available from a user's cart session

While working on the Cart API module to find ways to use JSON:API, I realized we were missing some entity access control for carts to allow anonymous users to view their orders tracked in their session. With query-level entity access landing in Entity API, fetching orders over JSON:API or GraphQL automatically restricted the returned orders to carts belonging to authenticated users. We realized we needed to update Commerce Cart to support this use case for traditional and headless use cases.

Provide a constraint on the Coupons field to verify referenced coupons are available

I set a goal earlier this year to support coupon redemption through our Cart API. I ran into a problem early on while evaluating our code to remember where / how we validate coupons. I knew the code existed and expected it'd be pretty simple to reuse. Unfortunately, it turned out we only put validation logic in the default coupon checkout pane code, meaning a coupon redemption endpoint in the Cart API would have to reproduce the code to support client-side coupon validation. That sort of duplication is bound to lead to bugs or out of sync logic.

What was the solution? Add a validation constraint to our coupon reference field on orders. This constraint contains the code that validates a coupon for an order and ensures its related promotion applies. RESTful Web Services module and JSON:API automatically run validation against entities when they're modified, triggering this check and allowing invalid coupons to be detected right away. This in turn let us simplify our coupon redemption form as well. The final patch is still in progress, but once landed, it will make it easier for any Drupal Commerce site, headless or not, to add their own customizations on top of the default coupon redemption form or write their own.

What’s next for 2019?

We've been pretty busy preparing Centarro Toolbox for release at DrupalCon Seattle, so first I expect I'll take a deep breath and celebrate the Drupal 8.7 release. We're planning now how to ensure Drupal Commerce users can harness the power of Layout Builder and Media, and we'll be integrating them into our Belgrade based demo.

Second, we'll continue to improve the developer experience with Drupal Commerce over the various API based modules. Headless Drupal Commerce is already working in the wild. The team behind Spark POS uses JSON:API and Drupal Commerce to handle ZKungFu’s billion dollar business. 1xINTERNET has been pushing React based front-ends for a while and will even be presenting their work at DrupalCon. As project maintainers, we want to empower teams to build similar applications and move our support from product catalog navigation and shopping cart manipulation through to checkout completion and order management.

Matt Glaman's picture
Senior Drupal Consultant
Posted April 4, 2019