Typing the WP REST API in a React admin
Plugin settings screens get messy fast. A small pattern for keeping REST responses the same shape every time — and typed end to end.
A plugin settings screen is a React app talking to a handful of REST endpoints. The pain starts when the PHP side returns a slightly different shape than the TypeScript side expects. Here's how I keep both honest.
Register the route with a schema
register_rest_route accepts an args schema. Fill it in. It validates input, documents the shape, and becomes the contract your TypeScript types mirror.
register_rest_route( 'portfolio/v1', '/settings', array(
'methods' => 'POST',
'callback' => 'portfolio_save_settings',
'permission_callback' => fn() => current_user_can( 'manage_options' ),
'args' => array(
'accent' => array( 'type' => 'string', 'required' => true ),
),
) );Fetch with apiFetch, not raw fetch
@wordpress/api-fetch handles the nonce and base path for you. Wrap it once, type the return, and every call site gets autocomplete.
import apiFetch from "@wordpress/api-fetch";
interface Settings { accent: string }
export const getSettings = () =>
apiFetch<Settings>({ path: "/portfolio/v1/settings" });One typed wrapper per resource, one PHP schema per route. The two ends can't drift without a compiler error — which is exactly what you want at 2am.