REST API endpoints for AJAX requests
In Chisel theme, interactive UI is powered by WordPress REST endpoints – clean JSON routes you can call from blocks, components, or vanilla JS without page reloads. Endpoints are namespaced and versioned (e.g.,
wp-json/chisel/v2/...) and designed to return predictable payloads for things like form handlers, filters, lazy content, or block interactions.
Security and DX are built in. Write operations require a valid REST nonce and pass capability checks; read routes support caching and pagination when appropriate.
The result is a lightweight, theme‑native AJAX layer: organized routes, clear contracts, and fast responses that fit naturally into the theme’s block and component architecture.
Overview
- Namespacing & versioning: Endpoints live under a namespace such as
wp-json/chisel/v2/..., keeping routes organized and forward‑compatible. - Security first: All write operations require a valid REST nonce and pass a
permission_callback(cap checks) to prevent unauthorized requests. - Clean contracts: JSON input/output with well‑defined payloads. Success and error states use consistent shapes and HTTP status codes.
- Error handling: Standardized error responses (message, code, status) to simplify UI handling and retries.
- Performance: Optional caching (transients/object cache) and pagination for list endpoints. Avoids unnecessary DB load and supports fast UI updates.
- Frontend integration: Nonces and base URLs can be localized to scripts (e.g., via
wp_localize_script) to makefetch()calls straightforward from blocks, components, or vanilla JS. - Progressive enhancement: Endpoints are opt‑in and composable – use them for live search, filters, cart actions, form handlers, or async block rendering as needed.
Construction
- Namespace:
chisel/v2 - Base:
ajax - Route format:
/wp-json/chisel/v2/ajax/{action}/ - JS config:
chiselScripts.ajax.urlandchiselScripts.ajax.noncefrominc/WP/Assets.php
Default endpoint(s)
- load-more (POST)
- Handler:
inc/WP/AjaxEnpoints.php::load_more() - Body params:
post_type,per_page,page - Response:
{ error, message, data }wheredatais HTML (rendered via Timber templates likecomponents/{post_type}-item.twigorcomponents/post-item.twig)
- Handler:
Security
- Header:
X-WP-Nonce: {nonce}(nonce created withwp_create_nonce('wp_rest')) - Permission check:
Chisel\Controllers\AjaxController::permissions_check()(filterable viachisel_ajax_permissions_check)
Front-end usage
Helper: src/scripts/modules/utils.js::ajaxRequest(action, data, params?, headers?)
import Utils from './utils';
Utils.ajaxRequest('load-more', {
page: 1,
post_type: 'post,
per_page: 6,
}).then((response) => {
// Render response.data in DOM
});JavaScriptNotes:
- Final URL is
${chiselScripts.ajax.url}/{action}. - Defaults to POST with FormData. Override method/headers via the 3rd/4th params.
Adding a new endpoint
- Register route (by adding to
$this->routesor via filterchisel_ajax_routes:
<?php
public function set_properties() {
$this->routes = array(
'load-more' => array(),
);
}
// via filter:
add_filter('chisel_ajax_routes', function ( $routes ) {
$routes['my-action'] = array(
'methods' => array( 'POST' )
);
return $routes;
});PHP- Implement handler in
inc/WP/AjaxEnpoints.php:
<?php
public function my_action( \WP_REST_Request $request ) {
$data = $request->get_body_params(); // or $request->get_params() for GET
// ... validate/process ...
return new \WP_REST_Response([
'error' => 0,
'message' => 'ok',
'data' => [ /* your payload */ ],
], 200);
}PHP- Call from JS:
utils.ajaxRequest('my-action', { foo: 'bar' });JavaScriptFilters
chisel_ajax_routes— add/modify routes and HTTP methodschisel_ajax_permissions_check— override per-action permission logic
Key files
inc/Controllers/AjaxController.php— registers routes underchisel/v2/ajax, nonce permissioninc/WP/AjaxEnpoints.php— endpoint callbacks, e.g., load_more()inc/WP/Assets.php— exposeschiselScripts.ajax.{url, nonce}to JSsrc/scripts/modules/utils.js—ajaxRequest()helper