WP_REST_Server::READABLE, 'callback' => 'my_plugin_get_items', 'permission_callback' => 'my_plugin_items_permission', 'args' => array( 'per_page' => array( 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 100, 'sanitize_callback' => 'absint', ), 'page' => array( 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'sanitize_callback' => 'absint', ), ), ) ); } ); /** * Permission callback — require authentication. */ function my_plugin_items_permission( WP_REST_Request $request ): bool { return current_user_can( 'read' ); } /** * Endpoint callback — return paginated items. */ function my_plugin_get_items( WP_REST_Request $request ): WP_REST_Response { $per_page = $request->get_param( 'per_page' ); $page = $request->get_param( 'page' ); // Example: fetch from a custom table. global $wpdb; $table = $wpdb->prefix . 'my_items'; $offset = ( $page - 1 ) * $per_page; $total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" ); $items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} ORDER BY created_at DESC LIMIT %d OFFSET %d", $per_page, $offset ) ); $response = rest_ensure_response( $items ); $response->header( 'X-WP-Total', $total ); $response->header( 'X-WP-TotalPages', ceil( $total / $per_page ) ); return $response; }