A talk by @thorstenfrommen
The history of the WordPress REST API:
A possible future of the WordPress REST API:
wp/v2/users
endpoint to support multisite;
wp/v2/sites
endpoint;
wp/v2/networks
endpoint.
[An] Application Programming Interface (API) is a set of subroutine definitions, protocols, and tools for building application software. In general terms, it is a set of clearly defined methods of communication between various software components. Wikipedia
Stack Overflow
For us,
an API consists of all means of communication with a specific piece of software.
Representational state transfer (REST) or RESTful Web services are one way of providing interoperability between computer systems on the Internet. REST-compliant Web services allow requesting systems to access and manipulate textual representations of Web resources using a uniform and predefined set of stateless operations. Wikipedia
Stack Overflow
For us,
REST is a client–server architecture for providing interoperability regarding the
access and manipulation of resources by means of predefined stateless operations.
Consequently, the WordPress REST API provides read/write access to (partial) data of a WordPress website, in a REST-compliant way.
Send a GET
request to the posts endpoint.
GET /wp-json/wp/v2/posts HTTP/1.1
Host: https://2017.berlin.wordcamp.org
Send a GET
request to the posts endpoint, and supply the post ID.
GET /wp-json/wp/v2/posts/3 HTTP/1.1
Host: https://2017.berlin.wordcamp.org
Send a DELETE
request to the posts endpoint, and supply the post ID.
DELETE /wp-json/wp/v2/posts/3 HTTP/1.1
Host: https://2017.berlin.wordcamp.org
Send an UPDATE
request to the posts endpoint, and supply both an ID and new data.
UPDATE /wp-json/wp/v2/posts/3 HTTP/1.1
Host: https://2017.berlin.wordcamp.org
Content-Type: application/json; charset=utf-8
Content-Length: 47
{ "title": "Welcome to WordCamp Berlin 2017!" }
add_action( 'rest_api_init', function () {
register_rest_route( 'tomjn/v1', 'test', [
'callback' => function () {
return 'moomins';
},
] );
} );
(Or: “I don’t need no WP REST Starter.”)
WP_REST_Posts_Controller
Class
The suggested way to develop using the WordPress REST API:
register_rest_route()
.
Install with Composer:
$ composer require inpsyde/wp-rest-starter
The package contains:
WP_REST_Response
factory).
register_rest_field( 'post', 'awesomeness', $args );
$fields = new Field\Collection();
$field = new Field\Field( 'awesomeness' );
// TODO: Set up $field (e.g., set reader callback).
$fields->add( 'post', $field );
( new Field\Registry() )->register_fields( $fields );
interface Collection extends \IteratorAggregate {
public function add(
string $resource,
Field $field
): Collection;
public function delete(
string $resource,
string $field_name
): Collection;
}
interface Field {
public function definition(): array;
public function name(): string;
}
interface ReadableField extends Field {
public function set_get_callback(
Reader $reader = null
): ReadableField;
}
interface Reader {
public function get_value(
array $object,
string $field_name,
\WP_REST_Request $request,
string $object_type = ''
);
}
interface Registry {
const ACTION_REGISTER = 'wp_rest_starter.register_fields';
public function register_fields( Collection $fields );
}
final class Registry implements Common\Field\Registry {
public function register_fields( Common\Field\Collection $fields ) {
do_action( Common\Field\Registry::ACTION_REGISTER, /* args */ );
foreach ( $fields as $resource => $resource_fields ) {
foreach ( $resource_fields as $field_name => $field ) {
register_rest_field(
$resource,
$field_name,
$field->definition()
);
}
}
}
}
register_rest_route( 'awesomeness/v1', 'foos', $args );
register_rest_route( 'awesomeness/v1', 'foos/(?P<type>\d+)', $args );
// ...
register_rest_route( 'awesomeness/v1', 'bars', $args );
Issue: redundant (hard-coded) data.
$routes = new Route\Collection();
// ...
$routes->add( new Route\Route( 'foos', $options ) );
$routes->add( new Route\Route( 'foos/(?P<type>\d+)', $options ) );
// ...
$routes->add( new Route\Route( 'bars', $options ) );
// ...
( new Route\Registry( 'awesomeness/v1' ) )->register_routes( $routes );
interface Collection extends \IteratorAggregate {
public function add( Route $route ): Collection;
public function delete( int $index ): Collection;
}
interface Route {
public function options(): array;
public function url(): string;
}
interface Registry {
const ACTION_REGISTER = 'wp_rest_starter.register_routes';
public function register_routes( Collection $routes );
}
final class Registry implements Common\Route\Registry {
// Constructor, taking the namespace.
public function register_routes( Common\Route\Collection $routes ) {
do_action( Common\Route\Registry::ACTION_REGISTER, /* args */ );
foreach ( $routes as $route ) {
register_rest_route(
$this->namespace,
$route->url(),
$route->options()
);
}
}
}
$route = new Route\Route( $url, $options );
~\Core\Route\Options
.
$options = Route\Options::from_arguments( $handler, $args, 'POST' );
$options->set_schema( $schema );
$route = new Route\Route( $url, $options );
interface MessageInterface { /* ... */ }
interface RequestInterface extends MessageInterface { /* ... */ }
interface ServerRequestInterface extends RequestInterface { /* ... */ }
interface ResponseInterface extends MessageInterface { /* ... */ }
class WP_HTTP_Response { /* ... */ }
class WP_REST_Request implements ArrayAccess { /* ... */ }
class WP_REST_Response extends WP_HTTP_Response { /* ... */ }
get_headers()
Method
class WP_REST_Request implements ArrayAccess {
/** @return string[][] */
public function get_headers() { return [ 'k' => [ 'v1', 'v2' ] ]; }
}
class WP_REST_Response extends WP_HTTP_Response {
/** @return string[] */
public function get_headers() { return [ 'k' => 'v1, v2' ]; }
}
class Request extends \WP_REST_Request
implements Psr\Http\Message\ServerRequestInterface {
// ...
}
class Response extends \WP_REST_Response
implements Psr\Http\Message\ResponseInterface {
// ...
}
use Inpsyde\WPRESTStarter\Core\Request\Request;
// ...
$request = Request::from_wp_rest_request( $request );
use Inpsyde\WPRESTStarter\Core\Response\Response;
// ...
$response = Response::from_wp_rest_response( $response );
Well, this is no big deal: