Hoping you all are familiar with the term Headless/Decoupled Drupal, I won't be diving into the details. However, if you'd want to know more about what Headless Drupal is, you are just one click away from finding the secrets of the Headless world! Today we will demonstrate how Drupal's JSON API can be leveraged to create a Decoupled application with Next.js as our rendering layer and Drupal Commerce as the content hub.
As a starter here is what we will be covering in this blog post:
- Setting up your Drupal commerce site
- Setting up the Next.js site and fetching data from the Drupal site
- Working with the data (Create pages dynamically)
Note: We will follow this up and cover checkout functionality in another blog post.Here is a link to the starter package. This is still a work in progress but good to get you started
Set up your Drupal Commerce site
Drupal Commerce provides you with a starter project. This will make your content work easier as it provides you with some preconfigured products. Alternatively, you can set up your site from scratch by following the Commerce documentation here.
Once you are done installing the site, you need to do a few more things:
- Enable JSON API, JSON:API Resources, JSON API Hypermedia, and Commerce API module. You can also add the Simple OAuth module for authentication
- Add JSON resources cross bundle module and enable it or you can also create a custom resource for the listing of all products. (Note: The cross bundle module is not compatible with JSON API Extras.)
- Allow read and write permissions for JSON API
- Edit your services.yml file to allow cross-origin headers
Fetching data from the Drupal site
Here is a link to the starter package. This is still a work in progress but good to get you started. Go through the readme file to check how to set up your Next.js site.
This is how your homepage should look like:
Let me walk you through the folder structure and the code:
- Pages folder: As the name suggests this is where you are going to be adding your pages. Each page is associated with a route based on its file name. Next.js also supports dynamic page routing.
- Components folder: This is where we will abstract our functionality into different components.
- api.js file: This is where all the magic happens. This page contains all the code responsible for manipulating the API data.
- next.config.js: This file is not included in the browser build and is used by the server. You can read more about it here.
- .env file: This is where all your environment variables will be stored.
Here are some of the Next.js functions we will be using to fetch data at the build and run time.
- getStaticProps (Static Generation): Fetches data at build time
- getStaticPaths (Static Generation): Specifies dynamic routes to pre-render pages based on data
- getServerSideProps (Server-side Rendering): Fetches data on each request
- getInitialProps: Enables server-side rendering in a page and allows you to do initial data population, which means sending the page with the data already populated from the server. getInitialProps will disable Automatic Static Optimization For the initial page load, getInitialProps will run on the server only. getInitialProps will then run on the client when navigating to a different route via the next/link component or by using next/router. However, if getInitialProps is used in a custom _app.js, and the page being navigated to implements getServerSideProps, then getInitialProps will run on the server.
Creating Static Routes
As mentioned above each page in Next.js is associated with a route based on its file name. So the index.js file inside the pages folder will work as our homepage.
In getStaticProps, we are using our JSON:API Client (you can find the relevant code in the api.js file) to fetch the relevant data and filter it as per our needs. getStaticProps function returns the data as props to our default component which is further manipulated to get the desired result.
Creating dynamic routes
Next.js also supports dynamic page routing. In Next.js you can add brackets to a page ([param]) to create a dynamic route. Inside the product folder in the pages folder, we have created a file [id].js which will be responsible for creating our product detail pages.
Here, getStaticPaths fetches the products id's for us and passes them as params to getStaticProps and get static props to pass the product data as props to the default product component.
Note that we are using the product id here. Because with JSON:API in Drupal you can't filter by path alias due to technical limitations. JSON:API filters translate to entity queries on field values; because Drupal path aliases aren't fields you can't use the filter query param to look up articles by their alias.
So to overcome this limitation we can use modules like:
- Decoupled Router: Which provides a new endpoint where a client can query a path for any possible JSON:API resources.
- Fieldable Path: Which provides a new entity field (and thus queryable via the filter query param) that is designed to mimic the entity's path (with Pathauto support). We will cover this in another blog post.
Another thing to note here is that for better a developer experience in development mode, getStaticProps and getStaticPaths run on every request otherwise they only run at build time.
This is it! You have set up a Decoupled site with a simple listing page and detail pages. As this is still a work in progress. If you face any difficulties please add your queries in the comments section below and I will be happy to answer!