• 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 build custom Timber Twig templating engines extensions utilizing modern REST API Controllers schemas

How to build custom Timber Twig templating engines extensions utilizing modern REST API Controllers schemas

Leveraging REST API Controller Schemas for Timber Twig Extensions

This post details the construction of custom Timber Twig extensions in WordPress, specifically focusing on how to dynamically generate and utilize data structures derived from the WordPress REST API’s controller schemas. This approach allows for highly decoupled, data-driven templating logic, enabling frontend developers to consume complex WordPress data models with predictable, self-documenting interfaces.

Understanding REST API Controller Schemas

WordPress’s REST API, particularly since version 4.7, exposes metadata about its endpoints and the data they represent. This metadata, often referred to as the schema, provides a machine-readable description of the available fields, their types, validation rules, and capabilities for a given resource. For custom post types and taxonomies registered via code, these schemas are automatically generated. We can tap into this by querying the /wp/v2//schema endpoint.

For instance, to inspect the schema for a custom post type named ‘project’, you would typically make a GET request to /wp-json/wp/v2/project/schema. The response is a JSON object detailing the structure of a ‘project’ post, including properties like ‘title’, ‘content’, ‘status’, and any custom fields registered with register_post_meta that are exposed to the REST API.

Registering a Custom Timber Function

Timber provides a robust mechanism for extending its Twig environment with custom PHP functions. These functions can then be called directly within your Twig templates. To create a function that fetches and processes a REST API schema, we’ll hook into Timber’s timber_context filter or register a function directly using Timber\Functions::add_function.

Method 1: Using timber_context Filter

This method injects data into the global Timber context, making it available to all templates. We’ll define a function that retrieves the schema for a given post type and returns it in a format suitable for Twig.

Example: Fetching Post Type Schema

Place the following PHP code in your theme’s functions.php file or a custom plugin:

<?php
/**
 * Add custom function to Timber context to fetch REST API schema.
 *
 * @param array $context The Timber context array.
 * @return array The modified Timber context array.
 */
add_filter( 'timber_context', function( $context ) {
    $context['get_rest_schema'] = function( $post_type ) {
        if ( ! post_type_exists( $post_type ) ) {
            return null;
        }

        $request = new WP_REST_Request( 'GET', "/wp/v2/{$post_type}/schema" );
        $response = rest_do_request( $request );

        if ( is_wp_error( $response ) ) {
            return null;
        }

        $schema = $response->get_data();

        // Optionally, filter or transform the schema here.
        // For example, to only expose 'properties' and 'required' fields.
        $filtered_schema = [
            'properties' => $schema['properties'] ?? [],
            'required'   => $schema['required'] ?? [],
        ];

        return $filtered_schema;
    };
    return $context;
} );
?>

Method 2: Using Timber\Functions::add_function

This method registers a function directly with Timber’s Twig environment, offering more explicit control over its availability.

Example: Registering a Schema Fetcher Function

<?php
use Timber;
use WP_REST_Request;

// Ensure Timber is active before attempting to register functions.
if ( class_exists( 'Timber' ) ) {
    Timber\Functions::add_function( 'get_rest_schema', function( $post_type ) {
        if ( ! post_type_exists( $post_type ) ) {
            return null;
        }

        $request = new WP_REST_Request( 'GET', "/wp/v2/{$post_type}/schema" );
        $response = rest_do_request( $request );

        if ( is_wp_error( $response ) ) {
            return null;
        }

        $schema = $response->get_data();

        // Example: Return only the 'properties' for simplicity in Twig.
        return $schema['properties'] ?? [];
    } );
}
?>

Utilizing Schemas in Twig Templates

Once the custom function is registered, you can call it directly within your Twig templates. This allows you to dynamically build forms, validate input, or simply display field information based on the API’s definition.

Example: Rendering a Dynamic Form Field

Suppose you have a custom post type ‘event’ and you want to render an input field for its ‘location’ property, which is defined in the REST API schema. You can do this in your Twig template:

{# Assuming 'get_rest_schema' is available in the context #}
{# Or if using Timber\Functions::add_function, it's globally available #}

{% set event_schema = get_rest_schema('event') %}

{% if event_schema and event_schema.location %}
    <div class="form-group">
        <label for="event_location">{{ event_schema.location.description|default('Location') }}</label>
        <input
            type="{{ event_schema.location.type }}"
            id="event_location"
            name="event_location"
            value="{{ post.meta.location|default('') }}"
            class="form-control"
            aria-describedby="location-help"
            {% if event_schema.location.arg and 'required' in event_schema.location.arg %}required{% endif %}
        >
        {% if event_schema.location.description %}
            <small id="location-help" class="form-text text-muted">{{ event_schema.location.description }}</small>
        {% endif %}
    </div>
{% else %}
    {# Fallback for when schema is not available or location field is missing #}
    <div class="form-group">
        <label for="event_location">Location</label>
        <input type="text" id="event_location" name="event_location" value="{{ post.meta.location|default('') }}" class="form-control">
    </div>
{% endif %}

Example: Displaying Field Metadata

You can also use the schema to display information about fields, such as their types or whether they are required.

<h3>Event Details</h3>
<ul>
{% for field_name, field_definition in get_rest_schema('event').properties %}
    <li>
        <strong>{{ field_name|capitalize }}</strong>:
        Type: {{ field_definition.type }}
        {% if field_definition.description %}
            - {{ field_definition.description }}
        {% endif %}
        {% if field_definition.arg and 'required' in field_definition.arg %}
            (Required)
        {% endif %}
    </li>
{% endfor %}
</ul>

Advanced Considerations and Best Practices

Caching Schema Data

Fetching the REST API schema on every request can be inefficient. For performance-critical applications, consider implementing a caching layer. WordPress Transients API is an excellent choice for this.

<?php
use Timber;
use WP_REST_Request;

if ( class_exists( 'Timber' ) ) {
    Timber\Functions::add_function( 'get_rest_schema_cached', function( $post_type, $cache_duration = HOUR_IN_SECONDS ) {
        $cache_key = 'rest_schema_' . sanitize_key( $post_type );
        $cached_schema = get_transient( $cache_key );

        if ( false !== $cached_schema ) {
            return $cached_schema;
        }

        if ( ! post_type_exists( $post_type ) ) {
            return null;
        }

        $request = new WP_REST_Request( 'GET', "/wp/v2/{$post_type}/schema" );
        $response = rest_do_request( $request );

        if ( is_wp_error( $response ) ) {
            return null;
        }

        $schema = $response->get_data();
        $filtered_schema = $schema['properties'] ?? [];

        set_transient( $cache_key, $filtered_schema, $cache_duration );

        return $filtered_schema;
    } );
}
?>

Handling Custom Fields (register_post_meta)

Custom fields registered with register_post_meta are automatically included in the REST API schema if they are exposed. Ensure your register_post_meta calls include the show_in_rest argument set to true and provide a descriptive schema argument for proper API representation.

<?php
register_post_meta( 'event', 'event_location', [
    'show_in_rest' => true,
    'single'       => true,
    'type'         => 'string',
    'sanitize_callback' => 'sanitize_text_field',
    'auth_callback' => function() {
        // Define authorization logic if needed
        return current_user_can( 'edit_posts' );
    },
    'schema'       => [
        'description' => esc_html__( 'The location of the event.', 'your-text-domain' ),
        'type'        => 'string',
        'arg'         => [ // Defines arguments for the REST API
            'required' => false,
            'type'     => 'string',
        ],
    ],
] );
?>

When show_in_rest is true and a schema is provided, this metadata will be reflected in the /wp/v2/event/schema response, making it accessible via your Timber extension.

Security and Authorization

The REST API schema endpoint itself is generally publicly accessible. However, the auth_callback for register_post_meta and the general REST API authentication mechanisms determine who can *modify* data. Your Timber extension primarily *reads* schema information, which is less of a security concern than data modification. Always be mindful of what data you expose and how it’s used in your templates.

Conclusion

By integrating WordPress REST API controller schemas with Timber Twig extensions, you can build more dynamic, maintainable, and data-aware templating systems. This approach promotes a clear separation of concerns, allowing your PHP backend to define data structures and your Twig templates to consume them intelligently, leading to more robust and scalable WordPress applications.

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

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Carbon Fields custom wrappers
  • Building secure B2B pricing grids with custom REST API Controllers endpoints and role overrides

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 (182)
  • WordPress Plugin Development (197)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins

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