or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mddata-sourcing.mdfile-handling.mdincremental-builds.mdindex.mdmultilingual.md
tile.json

incremental-builds.mddocs/

Incremental Builds

FastBuilds support and webhook handling for efficient development workflows, incremental content updates, and preview functionality.

Capabilities

FastBuilds Support

Enables incremental builds by fetching only changed content since the last build, dramatically reducing build times for large sites.

/**
 * FastBuilds configuration option
 * Requires the gatsby_fastbuilds Drupal module and user permissions
 */
fastBuilds: boolean;

FastBuilds process:

  1. Check lastFetched timestamp from plugin status
  2. Query Drupal's FastBuilds endpoint with timestamp
  3. Process only changed entities since last build
  4. Update lastFetched timestamp for next build

Usage Examples:

{
  resolve: `gatsby-source-drupal`,
  options: {
    baseUrl: `https://your-site.com/`,
    basicAuth: {
      username: process.env.BASIC_AUTH_USERNAME,
      password: process.env.BASIC_AUTH_PASSWORD,
    },
    fastBuilds: true, // Requires authentication and Drupal module
  },
}

Webhook Update Handling

Processes webhook updates for real-time content synchronization during development and preview.

/**
 * Handles webhook updates for incremental content changes
 * @param gatsbyApi - Gatsby API functions and webhook data
 * @param pluginOptions - Plugin configuration options
 * @returns Promise that resolves when update is processed
 */
function handleWebhookUpdate(
  gatsbyApi: {
    nodeToUpdate: DrupalEntity;
    actions: {
      createNode: Function;
      unstable_createNodeManifest: Function;
    };
    cache: Cache;
    createNodeId: Function;
    createContentDigest: Function;
    getCache: Function;
    getNode: Function;
    reporter: Reporter;
  },
  pluginOptions: PluginOptions
): Promise<void>;

interface WebhookBody {
  secret?: string;
  action: 'insert' | 'update' | 'delete';
  data: DrupalEntity | DrupalEntity[];
}

Usage Examples:

// Webhook handling is automatic when webhookBody is present
// Configure webhook secret for security:
{
  resolve: `gatsby-source-drupal`,
  options: {
    baseUrl: `https://your-site.com/`,
    secret: process.env.PREVIEW_SECRET, // Must match Drupal configuration
  },
}

// Webhook request format from Drupal:
{
  "secret": "your-shared-secret",
  "action": "update",
  "data": {
    "id": "article-123",
    "type": "node--article",
    "attributes": { /* updated content */ },
    "relationships": { /* entity relationships */ }
  }
}

Node Deletion Handling

Manages node deletion and relationship cleanup when content is removed from Drupal.

/**
 * Handles deletion of nodes and cleanup of relationships
 * @param options - Configuration for deletion handling
 * @returns Promise resolving to the deleted node
 */
function handleDeletedNode(options: {
  actions: Actions;
  node: DrupalEntity;
  getNode: Function;
  createNodeId: Function;
  createContentDigest: Function;
  cache: Cache;
  entityReferenceRevisions: string[];
  pluginOptions: PluginOptions;
}): Promise<GatsbyNode>;

The deletion process:

  1. Find existing Gatsby node by ID
  2. Clean up cached relationship data
  3. Remove references from related nodes
  4. Delete the node from Gatsby
  5. Update related nodes with cleaned relationships

Preview and Development Support

Enables Gatsby Preview integration for content editors and development workflows.

/**
 * Creates node manifests for Gatsby Cloud Preview support
 * @param options - Manifest creation configuration
 */
function drupalCreateNodeManifest(options: {
  attributes: DrupalEntityAttributes;
  gatsbyNode: GatsbyNode;
  unstable_createNodeManifest: Function;
}): void;

Usage Examples:

// Preview is automatically enabled in development with refresh endpoint
process.env.NODE_ENV === 'development' && 
process.env.ENABLE_GATSBY_REFRESH_ENDPOINT

// Or explicitly enabled with:
process.env.GATSBY_IS_PREVIEW === 'true'

Node Creation for Missing Entities

Ensures referenced nodes exist before processing relationships, creating missing nodes as needed.

/**
 * Creates node if it doesn't exist (for webhook updates and references)
 * @param options - Node creation configuration
 * @returns Promise that resolves when node is created or already exists
 */
function createNodeIfItDoesNotExist(options: {
  nodeToUpdate: DrupalEntity;
  actions: Actions;
  createNodeId: Function;
  createContentDigest: Function;
  getNode: Function;
  reporter: Reporter;
  pluginOptions: PluginOptions;
}): Promise<void>;

This ensures referential integrity when:

  • Processing webhook updates with new references
  • Handling entity relationships during incremental builds
  • Managing complex content hierarchies

FastBuilds Setup

Drupal Module Requirements

  1. Install and enable the gatsby_fastbuilds module
  2. Create a Drupal user with appropriate permissions
  3. Grant the user permission to "sync gatsby fastbuild log entities"
  4. Configure authentication in your Gatsby site

FastBuilds Endpoint

The plugin queries the FastBuilds endpoint:

GET /gatsby-fastbuilds/sync/{timestamp}

Response format:

{
  "status": 1,
  "timestamp": 1623456789,
  "entities": [
    {
      "action": "update",
      "data": { /* entity data */ }
    },
    {
      "action": "delete", 
      "data": { "id": "node-123", "type": "node--article" }
    }
  ]
}

Error Handling

// Expired or missing timestamp
if (res.body.status === -1) {
  reporter.info('Unable to pull incremental data changes from Drupal');
  setPluginStatus({ lastFetched: res.body.timestamp });
  requireFullRebuild = true;
}

// Network or API errors trigger full rebuild
catch (error) {
  reporter.warn('FastBuilds request failed, falling back to full build');
  requireFullRebuild = true;
}

Webhook Integration

Development Server Integration

For development, the plugin provides webhook endpoints for live updates:

// Legacy endpoint (deprecated)
app.use('/___updatePreview/', bodyParser.text(), async (req, res) => {
  // Handle preview updates
});

// Modern approach uses Gatsby's refresh endpoint
// Configure in Drupal to POST to: http://localhost:8000/__refresh

Webhook Security

Implement webhook security with shared secrets:

// Validate webhook secret
if (pluginOptions.secret && pluginOptions.secret !== secret) {
  reporter.warn('The secret in this request did not match your plugin options secret.');
  return;
}

// Validate webhook structure
if (!action || !data) {
  reporter.warn('The webhook body was malformed');
  return;
}

Batch Updates

Handle multiple entity updates in a single webhook:

// Webhook can contain single entity or array
let nodesToUpdate = data;
if (!Array.isArray(data)) {
  nodesToUpdate = [data];
}

// Process each entity
for (const nodeToUpdate of nodesToUpdate) {
  await handleWebhookUpdate(/* ... */);
}

Performance Optimizations

Node Touching for Garbage Collection

During FastBuilds, existing nodes are "touched" to prevent garbage collection:

// Touch existing nodes during incremental builds
getNodes().forEach(node => {
  if (node.internal.owner === 'gatsby-source-drupal') {
    touchNode(node);
  }
});

Relationship Caching

Relationships are cached to optimize processing:

// Cache referenced nodes
await cache.set(`refnodes-${node.id}`, referencedNodes);

// Cache back-reference field names
await cache.set(`backrefs-${node.id}`, backRefsNames);

Concurrent Processing

Updates are processed with configurable concurrency:

// Process multiple webhook updates concurrently
await Promise.all(nodesToUpdate.map(async (nodeToUpdate) => {
  await handleWebhookUpdate(/* ... */);
}));

Content Sync and Manifests

Gatsby Cloud Integration

When using Gatsby Cloud, the plugin creates node manifests for Content Sync:

// Manifest creation for preview routing
const manifestId = `${id}-${updatedAt}-${langcode}`;
unstable_createNodeManifest({
  manifestId,
  node: gatsbyNode,
  updatedAtUTC: updatedAt,
});

Preview URL Generation

Content Sync uses manifests to route editors from Drupal to the correct preview pages, enabling seamless editorial workflows.

Types

interface WebhookBody {
  secret?: string;
  action: 'insert' | 'update' | 'delete';
  data: DrupalEntity | DrupalEntity[];
}

interface FastBuildsResponse {
  status: number; // 1 for success, -1 for expired/error
  timestamp: number;
  entities: FastBuildsEntity[];
}

interface FastBuildsEntity {
  action: 'insert' | 'update' | 'delete';
  data: DrupalEntity | { id: string; type: string };
}

interface NodeManifestOptions {
  manifestId: string;
  node: GatsbyNode;
  updatedAtUTC: string;
}