Next-Drupal 1.3

April 19, 2022 - shadcn


Since releasing next-drupal 1.0 last December, we have seen incredible adoption. Our community has grown beyond expectations. We are now approaching 1000+ downloads per week.

Today we're excited to release next-drupal 1.3, one of our most important releases.

Next-Drupal 1.3 ships a powerful and flexible JSON:API client for fetching data from Drupal.

It is a replacement for functional helpers such as getResource and getResourceCollection and provides tons of customizations.

The DrupalClient is an optional feature. You can safely upgrade your site to next-drupal 1.3.0 without any breaking changes.

Features

  1. Customizable JSON:API client for data fetching.
  2. Helpers to fetching resources, menus, views and search indices.
  3. Support for custom auth - Bearer, Basic or bring your own.
  4. Support for custom serializer.
  5. Support for custom fetcher.
  6. Support for cached resources - memory cache, redis...etc.
  7. Improved error messages

DrupalClient

The DrupalClient is available in next-drupal ^1.3.0 as experimental.

import { DrupalClient } from "next-drupal"
 
const drupal = new DrupalClient("https://example.com")
 
// Fetch articles.
const articles = await drupal.getResourceCollection("node--article")

Data Fetching

Every data fetching helper has been upgraded and made more flexible with options for authentication, localization and caching.

import { drupal } from "lib/drupal"
 
// Fetch articles.
const articles = await drupal.getResourceCollection("node--article")
 
// Fetch articles with translation.
const articles = await drupal.getResourceCollectionFromContext(
  "node--article",
  context
)
 
// Get static paths for resources.
const paths = await drupal.getStaticPathsFromContext(
  ["node--article", "node--page"],
  context
)
 
// Make an auth request to fetch a block.
const menu = await drupal.getResource(
  "block_content--basic",
  "42487873-3aad-44ab-8dd6-c2fb0d82bb8f",
  {
    withAuth: true,
  }
)
 
// Fetch a menu and cache the response during build.
const menu = await drupal.getMenu("main", {
  withCache: process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD,
  cacheKey: `menu:main`,
})

Auth

In addition to Bearer tokens (via Simple OAuth), you can now provide own custom authentication implementation for fetching Drupal data.

Bearer

To configure authentication, provide a client_id and client_secret as options.

export const drupal = new DrupalClient(
  process.env.NEXT_PUBLIC_DRUPAL_BASE_URL,
  {
    auth: {
      clientId: process.env.DRUPAL_CLIENT_ID,
      clientSecret: process.env.DRUPAL_CLIENT_SECRET,
    },
  }
)

DrupalClient will fetch Bearer token and handle expiration for you.

Basic

You can also use the basic authorization header as follows:

⚠️

You need to enable the basic_auth module on Drupal.

export const drupal = new DrupalClient(
  process.env.NEXT_PUBLIC_DRUPAL_BASE_URL,
  {
    auth: () =>
      "Basic " +
      Buffer.from(
        `${process.env.BASIC_AUTH_USERNAME}:${process.env.BASIC_AUTH_PASSWORD}`
      ).toString("base64"),
  }
)

Callback

You can also provide a custom callback for authentication. The callback must return an Authorization compatible header.

export const drupal = new DrupalClient(
  process.env.NEXT_PUBLIC_DRUPAL_BASE_URL,
  {
    auth: () => {
      // Authenticate and return headers.
    },
  }
)

Serializer

The DrupalClient uses jsona as the default serializer for serializing and deserializing JSON:API data.

You can provide your own using the serializer option.

import { Deserializer } from "jsonapi-serializer"
 
const customSerializer = new Deserializer({
  keyForAttribute: "camelCase",
})
 
export const drupal = new DrupalClient(
  process.env.NEXT_PUBLIC_DRUPAL_BASE_URL,
  {
    serializer: customSerializer,
  }
)

Fetcher

You can now provide your own fetcher using the fetcher option. This replaces Next.js' polyfilled fetch for JSON:API calls.

import { DrupalClient } from "next-drupal"
import crossFetch from "cross-fetch"
 
const fetcher = (url, options) => {
  const { withAuth, ...opts } = options
 
  if (withAuth) {
    // Make additional requests to fetch a bearer token
    // Or any other Authorization headers.
  }
 
  return crossFetch(url, {
    ...opts,
    // Pass in additional options. Example: agent.
  })
}
 
export const drupal = new DrupalClient(
  process.env.NEXT_PUBLIC_DRUPAL_BASE_URL,
  {
    fetcher,
  }
)

Caching

The DrupalClient has support for caching resources. You can provide your own cache implementation using the cache option.

Here's an example on how you can use Redis to cache resources.

import { DrupalClient, DataCache } from "next-drupal"
import Redis from "ioredis"
 
const redis = new Redis(process.env.REDIS_URL)
 
// Create a custom cache.
export const redisCache: DataCache = {
  async set(key, value) {
    return await redis.set(key, value)
  },
 
  async get(key) {
    return await redis.get(key)
  },
}
 
export const drupal = new DrupalClient(
  process.env.NEXT_PUBLIC_DRUPAL_BASE_URL,
  {
    cache: redisCache,
  }
)

Now when you make a getResource or getMenu call, you can tell the client to cache and re-use responses.

import { PHASE_PRODUCTION_BUILD } from "next/constants"
 
const menu = await drupal.getMenu("main", {
  withCache: process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD,
  cacheKey: `menu:main`,
})

Error Messages

We have improved the error messages from the client with support for formatted JSON:API errors.

Error messages

Upgrading

You can try the new DrupalClient today by following our upgrade guide here.