• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » How to implement custom Block Patterns API endpoints with token authentication in Gutenberg blocks

How to implement custom Block Patterns API endpoints with token authentication in Gutenberg blocks

Registering Custom REST API Endpoints for Block Patterns

WordPress’s REST API provides a powerful mechanism for interacting with your site’s data programmatically. When developing custom Gutenberg blocks, you might need to fetch or manipulate data that isn’t directly exposed by default REST API endpoints. This is particularly true for custom block patterns, where you might want to serve dynamic pattern data or manage pattern collections via an API. This guide details how to register custom REST API endpoints specifically for handling block pattern-related data, secured with token authentication.

We’ll focus on creating a custom endpoint that can retrieve a list of block patterns based on certain criteria, and we’ll implement a robust token-based authentication system to ensure only authorized requests can access this data.

Implementing Token-Based Authentication

For secure API access, especially when dealing with potentially sensitive pattern data or when integrating with external services, token authentication is a standard and effective approach. We’ll use a simple token-based system where a pre-shared secret token is passed in the request headers. This requires a custom authentication callback within WordPress.

Generating and Storing the Secret Token

The secret token should be a strong, randomly generated string. It’s best practice to store this token securely, not directly in your plugin’s code. A common approach is to use environment variables or a dedicated configuration file that is not committed to version control. For simplicity in this example, we’ll define it as a constant, but in a production environment, you’d fetch this from a more secure location.

Let’s assume you have a mechanism to generate a token. For instance, using PHP:

// In your plugin's main file or a dedicated constants file
if ( ! defined( 'MY_BLOCK_PATTERNS_API_SECRET' ) ) {
    // In production, this should be loaded from environment variables or a secure config.
    // Example generation: bin2hex(random_bytes(32))
    define( 'MY_BLOCK_PATTERNS_API_SECRET', 'your_super_secret_and_long_token_here' );
}

Creating the Authentication Callback

WordPress’s REST API allows you to hook into the authentication process. We’ll create a function that checks for our secret token in the `Authorization` header of incoming requests.

The `rest_authentication_errors` filter is the key here. It allows us to return a `WP_Error` object if authentication fails, or `true` if it succeeds, or `null` to let other authentication methods proceed.

/**
 * Custom authentication for Block Patterns API.
 * Checks for a Bearer token in the Authorization header.
 *
 * @param WP_Error|bool|null $result The result of the authentication attempt.
 * @return WP_Error|null
 */
function my_block_patterns_api_authenticate( $result ) {
    // If a previous authentication method has already authenticated, return the result.
    if ( true === $result || is_wp_error( $result ) ) {
        return $result;
    }

    // Check if the request is for our specific API namespace.
    // Replace 'my-block-patterns/v1' with your actual namespace.
    $namespace = 'my-block-patterns/v1';
    $request_uri = $_SERVER['REQUEST_URI'];

    if ( strpos( $request_uri, '/wp-json/' . $namespace ) === false ) {
        return $result; // Not our API, let other auth methods handle it.
    }

    // Check for the Authorization header.
    $auth_header = isset( $_SERVER['HTTP_AUTHORIZATION'] ) ? $_SERVER['HTTP_AUTHORIZATION'] : '';

    if ( empty( $auth_header ) ) {
        return new WP_Error(
            'rest_not_logged_in',
            __( 'Authentication required: No Authorization header found.', 'my-text-domain' ),
            array( 'status' => 401 )
        );
    }

    // Expecting a "Bearer YOUR_TOKEN" format.
    if ( ! preg_match( '/Bearer\s(\S+)/', $auth_header, $matches ) ) {
        return new WP_Error(
            'rest_invalid_token_format',
            __( 'Authentication required: Invalid Authorization header format. Expected "Bearer YOUR_TOKEN".', 'my-text-domain' ),
            array( 'status' => 401 )
        );
    }

    $token = $matches[1];

    // Compare the provided token with our secret token.
    if ( ! defined( 'MY_BLOCK_PATTERNS_API_SECRET' ) || $token !== MY_BLOCK_PATTERNS_API_SECRET ) {
        return new WP_Error(
            'rest_invalid_token',
            __( 'Authentication failed: Invalid token.', 'my-text-domain' ),
            array( 'status' => 401 )
        );
    }

    // Authentication successful. Return null to indicate success and allow the request to proceed.
    // We don't need to set a logged-in user for this API, just validate the token.
    return null;
}
add_filter( 'rest_authentication_errors', 'my_block_patterns_api_authenticate' );

This function first checks if authentication has already succeeded or failed. Then, it verifies if the current request is targeting our custom API namespace. If it is, it looks for the `Authorization` header, expecting a `Bearer ` format. Finally, it compares the extracted token against our defined secret. If all checks pass, it returns `null`, allowing the REST API to process the request. Otherwise, it returns a `WP_Error` with an appropriate status code (401 Unauthorized).

Registering the Custom REST API Endpoint

Now that we have our authentication mechanism in place, we need to register the actual REST API endpoint that will serve our block pattern data. This is done using the `register_rest_route` function, typically within a plugin’s initialization function.

Defining the Endpoint Callback Function

This function will be responsible for fetching and returning the block pattern data. For this example, we’ll simulate fetching patterns. In a real-world scenario, you might query a custom database table, fetch from an external service, or dynamically generate pattern configurations.

/**
 * Callback function to retrieve block patterns.
 *
 * @param WP_REST_Request $request Full data about the request.
 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 */
function my_get_block_patterns_callback( WP_REST_Request $request ) {
    // In a real application, you would fetch pattern data here.
    // This could involve querying a database, an external API, or generating patterns dynamically.

    // For demonstration, let's return some sample pattern data.
    $patterns = array(
        array(
            'name'        => 'hero-section-with-image',
            'title'       => __( 'Hero Section with Image', 'my-text-domain' ),
            'description' => __( 'A compelling hero section with a background image and call to action.', 'my-text-domain' ),
            'categories'  => array( 'hero', 'featured' ),
            'content'     => '

Discover Our Amazing Products

Experience the difference with our innovative solutions.

Learn More
', 'block_types' => array( 'core/group', 'core/image', 'core/heading', 'core/paragraph', 'core/button' ), ), array( 'name' => 'testimonial-card', 'title' => __( 'Testimonial Card', 'my-text-domain' ), 'description' => __( 'A clean and modern testimonial card.', 'my-text-domain' ), 'categories' => array( 'testimonials' ), 'content' => '

This is an amazing product! It has completely transformed our workflow.

Jane Doe, CEO of Example Corp
', 'block_types' => array( 'core/quote', 'core/paragraph', 'core/attribution' ), ), ); // You can also accept query parameters from the request. // For example, to filter by category: $category = $request->get_param( 'category' ); if ( $category ) { $filtered_patterns = array_filter( $patterns, function( $pattern ) use ( $category ) { return in_array( $category, $pattern['categories'], true ); } ); $patterns = array_values( $filtered_patterns ); // Re-index array } // Prepare the response. $response = new WP_REST_Response( $patterns, 200 ); $response->set_headers( array( 'X-Custom-API-Source' => 'MyBlockPatterns' ) ); return $response; }

In this callback, we’re simulating a list of block patterns. Each pattern includes its `name`, `title`, `description`, `categories`, `content` (which is the HTML markup for the pattern), and `block_types` it uses. We also demonstrate how to accept a query parameter (e.g., `?category=hero`) to filter the results. The function returns a `WP_REST_Response` object, which is the standard way to return data from REST API endpoints in WordPress.

Registering the Route

Now, we register the route using `register_rest_route`. This function takes the namespace, the route itself, and an array of arguments including the callback, HTTP methods allowed, and permissions callback (though we’re handling permissions via the global `rest_authentication_errors` filter for simplicity here).

/**
 * Register custom REST API routes for block patterns.
 */
function my_register_block_patterns_routes() {
    $namespace = 'my-block-patterns/v1';
    $route     = '/patterns';

    register_rest_route( $namespace, $route, array(
        'methods'             => WP_REST_Server::READABLE, // Corresponds to GET requests
        'callback'            => 'my_get_block_patterns_callback',
        'permission_callback' => '__return_true', // We handle auth globally via rest_authentication_errors
        'args'                => array(
            'category' => array(
                'required'          => false,
                'type'              => 'string',
                'description'       => __( 'Filter patterns by category.', 'my-text-domain' ),
                'sanitize_callback' => 'sanitize_text_field',
                'validate_callback' => function( $param, $request, $key ) {
                    // Optional: Add more specific validation if needed
                    return true;
                },
            ),
        ),
    ) );
}
add_action( 'rest_api_init', 'my_register_block_patterns_routes' );

This code registers a GET endpoint at `/wp-json/my-block-patterns/v1/patterns`. The `permission_callback` is set to `__return_true` because our custom authentication filter `my_block_patterns_api_authenticate` will handle all the authorization logic for requests within our namespace. We’ve also defined an optional `category` argument with sanitization.

Integrating with Gutenberg Blocks

With the API endpoint set up, you can now consume this data from your custom Gutenberg blocks. This is typically done using JavaScript in your block’s `edit` function or within a custom plugin that manages block patterns.

Fetching Data in JavaScript

You’ll need to enqueue a JavaScript file that makes the API request. It’s good practice to pass the API URL and your secret token (if it’s safe to do so client-side, or preferably, have a server-side mechanism to proxy requests) to your JavaScript. For security, it’s highly recommended *not* to expose the secret token directly in client-side JavaScript. Instead, consider a server-side proxy or a nonce-based approach for client-side requests.

For demonstration purposes, let’s assume you have a way to securely pass the API endpoint URL. A common pattern is to use `wp_localize_script` to pass data from PHP to JavaScript.

/**
 * Enqueue scripts and pass data to JavaScript.
 */
function my_enqueue_block_patterns_scripts() {
    // Only enqueue if we are in the block editor or a relevant admin page.
    if ( ! is_admin() ) {
        return;
    }

    // Register and enqueue your JavaScript file.
    wp_enqueue_script(
        'my-block-patterns-editor-script',
        plugins_url( 'assets/js/editor.js', __FILE__ ), // Path to your JS file
        array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-api-fetch' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'assets/js/editor.js' )
    );

    // Localize script to pass data to JavaScript.
    wp_localize_script(
        'my-block-patterns-editor-script',
        'myBlockPatternsConfig',
        array(
            'apiFetch' => array(
                'endpoint' => rest_url( 'my-block-patterns/v1/patterns' ),
                // IMPORTANT: Do NOT pass the secret token here for client-side use.
                // If client-side authentication is required, use a nonce or a server-side proxy.
            ),
            // Example of passing a nonce if you were to implement nonce auth
            // 'nonce'    => wp_create_nonce( 'wp_rest' ),
        )
    );
}
add_action( 'enqueue_block_editor_assets', 'my_enqueue_block_patterns_scripts' );

Now, in your `assets/js/editor.js` file, you can use `wp.apiFetch` to interact with your custom endpoint.

// assets/js/editor.js
const { apiFetch } = wp;
const { useState, useEffect } = wp.element;
const { Button, SelectControl, Spinner } = wp.components;

// Assuming myBlockPatternsConfig is available from wp_localize_script
const apiEndpoint = myBlockPatternsConfig.apiFetch.endpoint;

function MyCustomPatternFetcher() {
    const [patterns, setPatterns] = useState([]);
    const [loading, setLoading] = useState(true);
    const [selectedCategory, setSelectedCategory] = useState('');

    useEffect(() => {
        fetchPatterns(selectedCategory);
    }, [selectedCategory]); // Re-fetch when category changes

    const fetchPatterns = async (category) => {
        setLoading(true);
        let url = apiEndpoint;
        if (category) {
            url += `?category=${encodeURIComponent(category)}`;
        }

        try {
            // For client-side requests to a protected endpoint, you'd typically need
            // to pass a nonce or use a server-side proxy.
            // If your endpoint is public (which is NOT the case with token auth),
            // this would work directly.
            //
            // Example with nonce (requires server-side setup for nonce validation):
            // const fetchedPatterns = await apiFetch({
            //     path: url.replace(rest_url(), ''), // apiFetch expects path relative to /wp-json/
            //     method: 'GET',
            //     // headers: { 'X-WP-Nonce': myBlockPatternsConfig.nonce } // If using nonce auth
            // });

            // For token auth, you'd typically need a server-side proxy or a different auth mechanism.
            // If you MUST do it client-side (less secure), you'd need to manually construct the fetch
            // and add the Authorization header, which is NOT recommended.
            //
            // Example of manual fetch (use with extreme caution):
            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Bearer ${YOUR_SECRET_TOKEN_HERE}` // NEVER do this in production client-side
                }
            });

            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            const fetchedPatterns = await response.json();
            setPatterns(fetchedPatterns);

        } catch (error) {
            console.error("Error fetching block patterns:", error);
            // Handle error display to the user
        } finally {
            setLoading(false);
        }
    };

    const handleCategoryChange = (value) => {
        setSelectedCategory(value);
    };

    const availableCategories = [
        { value: '', label: 'All Categories' },
        { value: 'hero', label: 'Hero' },
        { value: 'testimonials', label: 'Testimonials' },
        { value: 'featured', label: 'Featured' },
    ];

    return (
        

Custom Block Patterns

{loading && } {!loading && patterns.length === 0 &&

No patterns found.

} {!loading && patterns.length > 0 && (
    {patterns.map((pattern) => (
  • {pattern.title}

    {pattern.description}

    {/* You would typically have a button here to insert the pattern */}
  • ))}
)}
); } // Register the component or use it within your block's edit function // Example: Registering a new sidebar panel // wp.plugins.registerPlugin('my-custom-patterns-panel', { // render: MyCustomPatternFetcher, // icon: 'layout', // }); // Or directly within a block's edit function: // function Edit(props) { // return ( //
// // {/* Your block's other edit UI */} //
// ); // } // wp.blocks.registerBlockType('my-plugin/my-block', { // edit: Edit, // // ... other block settings // });

Security Note: Exposing the `Authorization` header directly in client-side JavaScript is a significant security risk. The `YOUR_SECRET_TOKEN_HERE` placeholder should NEVER be replaced with your actual secret token in production client-side code. For secure client-side consumption, consider these alternatives:

  • Server-Side Proxy: Create another WordPress REST API endpoint that acts as a proxy. Your JavaScript calls this internal endpoint, and your PHP code then makes the authenticated request to your custom block patterns endpoint, adding the secret token. This keeps the token server-side.
  • Nonce Authentication: If the data isn’t highly sensitive and you want to ensure the request originates from a logged-in WordPress user (even if not an admin), you could use WordPress nonces. This requires modifying the authentication callback to validate the nonce and potentially the endpoint registration to use a nonce-based permission callback.
  • OAuth/JWT: For more complex integrations, consider implementing a more robust authentication standard like OAuth 2.0 or JWT.

Conclusion

By following these steps, you can successfully implement custom REST API endpoints for your Gutenberg block patterns, complete with token-based authentication. This provides a secure and flexible way to manage and serve dynamic block pattern data, enhancing the capabilities of your WordPress site and custom blocks. Remember to prioritize security, especially when handling authentication tokens, by keeping sensitive information server-side whenever possible.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • How to construct high-throughput import engines for large vendor commission records sets using custom XML/JSON parsers
  • Optimizing p99 database query response latency in multi-site Service Provider custom tables
  • Troubleshooting guide: Resolving memory leak spikes caused by unclosed custom database loops in custom product catalogs
  • WordPress Development Recipe: Leveraging Nullsafe operator pipelines to build type-safe, auto-wired hooks
  • Troubleshooting database connection pool timeouts in production when using modern Genesis child themes wrappers

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (48)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (154)
  • WordPress Plugin Development (177)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • How to construct high-throughput import engines for large vendor commission records sets using custom XML/JSON parsers
  • Optimizing p99 database query response latency in multi-site Service Provider custom tables
  • Troubleshooting guide: Resolving memory leak spikes caused by unclosed custom database loops in custom product catalogs

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala