WordPress REST API Controllers Overview
Architecture
WordPress REST API controllers follow a consistent object-oriented architecture based on the abstract WP_REST_Controller class. Each controller manages a specific resource type (posts, users, comments, terms, etc.) and provides standardized CRUD operations through RESTful endpoints.
Controller Hierarchy
WP_REST_Controller (abstract base)
├── WP_REST_Posts_Controller
│ ├── WP_REST_Attachments_Controller
│ ├── WP_REST_Blocks_Controller
│ ├── WP_REST_Templates_Controller
│ ├── WP_REST_Menu_Items_Controller
│ └── WP_REST_Menus_Controller
├── WP_REST_Users_Controller
├── WP_REST_Comments_Controller
├── WP_REST_Terms_Controller
├── WP_REST_Revisions_Controller
├── WP_REST_Autosaves_Controller
├── WP_REST_Taxonomies_Controller
├── WP_REST_Post_Types_Controller
├── WP_REST_Post_Statuses_Controller
├── WP_REST_Settings_Controller
├── WP_REST_Themes_Controller
├── WP_REST_Plugins_Controller
├── WP_REST_Sidebars_Controller
├── WP_REST_Widgets_Controller
├── WP_REST_Widget_Types_Controller
├── WP_REST_Block_Types_Controller
├── WP_REST_Block_Patterns_Controller
├── WP_REST_Block_Pattern_Categories_Controller
├── WP_REST_Search_Controller
├── WP_REST_Site_Health_Controller
├── WP_REST_Application_Passwords_Controller
├── WP_REST_Font_Families_Controller
├── WP_REST_Font_Faces_Controller
├── WP_REST_Font_Collections_Controller
├── WP_REST_Global_Styles_Controller
├── WP_REST_Global_Styles_Revisions_Controller
└── WP_REST_URL_Details_Controller
Core Concepts
Namespace and Rest Base
Every controller defines two key properties:
protected $namespace = 'wp/v2'; // API version namespace
protected $rest_base = 'posts'; // Resource endpoint path
The resulting endpoint URL becomes: /wp-json/wp/v2/posts
Route Registration Pattern
Controllers register routes in register_routes():
public function register_routes() {
// Collection route (list/create)
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => $this->get_collection_params(),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'create_item' ),
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);
// Single item route (read/update/delete)
register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<id>[d]+)',
array(
'args' => array(
'id' => array(
'description' => __( 'Unique identifier for the resource.' ),
'type' => 'integer',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
'permission_callback' => array( $this, 'get_item_permissions_check' ),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'update_item' ),
'permission_callback' => array( $this, 'update_item_permissions_check' ),
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_item' ),
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}
CRUD Method Pairs
Each CRUD operation has a permission check method paired with an action method:
| Operation | Permission Check | Action Method |
|---|---|---|
| List | get_items_permissions_check() |
get_items() |
| Read | get_item_permissions_check() |
get_item() |
| Create | create_item_permissions_check() |
create_item() |
| Update | update_item_permissions_check() |
update_item() |
| Delete | delete_item_permissions_check() |
delete_item() |
Schema System
Controllers define JSON Schema for their resources:
public function get_item_schema() {
return array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'post',
'type' => 'object',
'properties' => array(
'id' => array(
'description' => __( 'Unique identifier.' ),
'type' => 'integer',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
),
// ... more properties
),
);
}
Context-Based Field Filtering
The context parameter controls which fields are returned:
- view: Default context, public fields
- edit: Additional fields for editing (requires authentication)
- embed: Minimal fields for embedding in other resources
Response Preparation
The prepare_item_for_response() method transforms database objects to API responses:
public function prepare_item_for_response( $item, $request ) {
$fields = $this->get_fields_for_response( $request );
$data = array();
if ( rest_is_field_included( 'id', $fields ) ) {
$data['id'] = $item->ID;
}
// ... build response data
$context = $request['context'] ?? 'view';
$data = $this->filter_response_by_context( $data, $context );
$response = rest_ensure_response( $data );
$response->add_links( $this->prepare_links( $item ) );
return $response;
}
Database Preparation
The prepare_item_for_database() method transforms request data for database operations:
protected function prepare_item_for_database( $request ) {
$prepared = new stdClass();
if ( isset( $request['title'] ) ) {
$prepared->post_title = $request['title'];
}
// ... map request fields to database fields
return apply_filters( 'rest_pre_insert_post', $prepared, $request );
}
Batching Support
Controllers can enable batching for bulk operations:
protected $allow_batch = array( 'v1' => true );
This enables sending multiple requests in a single HTTP call to /wp-json/batch/v1.
Meta Fields
Controllers that support meta fields use companion meta field classes:
WP_REST_Post_Meta_FieldsWP_REST_User_Meta_FieldsWP_REST_Comment_Meta_FieldsWP_REST_Term_Meta_Fields
Additional Fields
Plugins can register additional fields using register_rest_field():
register_rest_field( 'post', 'custom_field', array(
'get_callback' => 'get_custom_field',
'update_callback' => 'update_custom_field',
'schema' => array(
'type' => 'string',
'description' => 'A custom field.',
'context' => array( 'view', 'edit' ),
),
));
HTTP Method Constants
WP_REST_Server provides constants for HTTP methods:
WP_REST_Server::READABLE= ‘GET’WP_REST_Server::CREATABLE= ‘POST’WP_REST_Server::EDITABLE= ‘POST, PUT, PATCH’WP_REST_Server::DELETABLE= ‘DELETE’WP_REST_Server::ALLMETHODS= ‘GET, POST, PUT, PATCH, DELETE’
Error Handling
Controllers return WP_Error objects for failures:
return new WP_Error(
'rest_forbidden',
__( 'Sorry, you cannot do that.' ),
array( 'status' => 403 )
);
Common error codes:
rest_forbidden– Permission deniedrest_invalid_param– Invalid parameterrest_post_invalid_id– Invalid post IDrest_user_invalid_id– Invalid user IDrest_term_invalid– Invalid term
HEAD Request Optimization
Controllers optimize HEAD requests by:
- Setting
'fields' => 'ids'to only fetch IDs - Disabling meta/term cache priming
- Returning empty response body
$is_head_request = $request->is_method( 'HEAD' );
if ( $is_head_request ) {
$args['fields'] = 'ids';
$args['update_post_meta_cache'] = false;
}
Pagination
Collection endpoints support pagination via:
page– Current page number (1-indexed)per_page– Items per page (1-100, default 10)offset– Offset for pagination
Response headers indicate totals:
X-WP-Total– Total items availableX-WP-TotalPages– Total pages available
Link headers provide navigation:
Link: <url>; rel="prev"– Previous pageLink: <url>; rel="next"– Next page
Source Location
/wp-includes/rest-api/endpoints/
Since Version
REST API Controllers were introduced in WordPress 4.7.0.