Skip to content

REST API

Miguel Muscat edited this page Nov 30, 2022 · 1 revision

The SDK provides an abstraction layer for the WordPress REST API that aims to make endpoints leaner and easier to implement. A lot of error handling and typical data manipulation is done automatically so that your implementations can focus on simply serving the request.

Creating an endpoint

To create an endpoint, simply instantiate a new RebelCode\WpSdk\Wp\RestEndpoint. These can later be registered to WordPress using their register() method.

use RebelCode\WpSdk\Wp\RestEndpoint;

$endpoint = new RestEndpoint(
    'my_plugin/v1',
    'my/endpoint',
    ['GET', 'POST'],
    $handler,
    $authGuard
);

$endpoint->register();

The constructor accepts the following arguments:

  • The namespace
  • The endpoint route
  • The supported HTTP methods
  • The handler that takes the request and returns a response (1)
  • An optional authentication guard that allows or blocks requests (2)

(1) Handlers contain the logic for that endpoint. You will need to implement these yourself.
(2) Auth guards are optional. If you do not provide one, the endpoint will be publicly accessible.

Implementing an endpoint handler

To implement a new endpoint, you need to create a new class that implements theRebelCode\WpSdk\Wp\RestEndpointHandler interface. This interface only requires one method to be implemented:

use RebelCode\WpSdk\Wp\RestEndpointHandler;

class MyEndpoint implements RestEndpointHandler
{
    public function handle(\WP_REST_Request $request)
    {
        return new \WP_REST_Response(['message' => 'Hello world!']);
    }
}

This class-based design makes it easy to inject dependencies into your endpoint handlers:

use RebelCode\WpSdk\Wp\RestEndpointHandler;
use RebelCode\WpSdk\Wp\PostCollection;

class MyEndpoint implements RestEndpointHandler
{
    /** @var PostCollection */
    protected $books;
    
    public function __construct(PostCollection $books)
    {
        $this->books = $books;
    }

    public function handle(\WP_REST_Request $request)
    {
        $page = (int) ($request->get_param('page') ?? 1);
        $result = $this->books->limit(20)->page($page);

        return new \WP_REST_Response($result);
    }
}

Note: You have may have noticed that the above example returns a response with a PostCollection instance; the example never calls get() on the collection to get the resulting array. This is because collections are iterators, and the SDK will automatically detect when Traversable instances are provided as the response data and will convert them to arrays.

Auth Guards

Authentication guards are optional. If you do not provide one to an endpoint, it will be publicly accessible. If provided, they will decide whether a request is allowed. Requests that are rejected will not be handled by your endpoint handler, and will instead return an erroneous response, typically with a 4xx status code.

Bundled auth guards

The SDK provides two auth guard implementations. Both are located in the RebelCode\WpSdk\Wp\RestAuthGuard namespace.

  • RestAuthNonce - Requires a valid nonce in the request.
  • RestAuthUserCapability - Requires a request from a logged-in user with a specific capability.

Example 1: Nonce

use RebelCode\WpSdk\Wp\RestAuthGuard\RestAuthNonce;

$auth = new RestAuthNonce('delete_book', 'my_nonce');

The constructor takes 2 arguments: the nonce action string and the name of the parameter where the nonce value is expected to be found in the request.

This will block requests that do not have a valid delete_book nonce value as the my_nonce request parameter.

Example 2: User capability

use RebelCode\WpSdk\Wp\RestAuthGuard\RestAuthUserCapability;

$auth = new RestAuthUserCapability('delete_posts');

The constructor takes a single argument: the capability that the user must have. This will block requests that do not have the WordPress cookie for logged-in users or requests that come from users that do not have this capability.

Custom auth guards

Auth guards are classes that implement the RebelCode\WpSdk\Wp\RestAuthGuard interface. This interface only requires one method to be implemented:

use RebelCode\WpSdk\Wp\RestAuthGuard;
use RebelCode\WpSdk\Wp\RestAuthError;
use WP_REST_Request

class MyAuthGuard implements RestAuthGuard
{
    public function getAuthError(WP_REST_Request $request): ?RestAuthError
    {
        $apiKey = $request->get_header('X-My-Auth');

        if ($apiKey !== 'my-secret-api-key') {
            return new RestAuthError(403, ['Invalid API key']);
        } else {
            return null;
        }
    }
}

The getAuthError() method should return null if the request is allowed, or an instance of RestAuthError if the request is rejected.

Services

Factories

You can create service factories for endpoints and auth guards by using their respective static factory() methods:

use Dhii\Services\Factories\Constructor;
use RebelCode\WpSdk\Wp\RestEndpoint;
use RebelCode\WpSdk\Module;
use RebelCode\WpSdk\Wp\RestAuthGuard\RestAuthUserCapability;

class MyModule extends Module
{
    public function getFactories(): array
    {
        return [
            'my_endpoint' => RestEndpoint::factory(
                'my_plugin/v1',        // namespace
                'my/endpoint',         // route
                ['POST'],              // methods
                'my_endpoint/handler', // handler
                'my_endpoint/auth',    // auth guard
            ]),
            
            'my_endpoint/handler' => new Constructor(MyEndpoint::class),
            
            'my_endpoint/auth' => RestAuthUserCapability::factory('delete_posts'),
        ];
    }
}

Note: The RestEndpoint::factory() takes similar arguments to the constructor, but instead of the actual handler and auth guard instances, it takes service IDs. The bundled auth guard implementations also have static factory() methods that take the same arguments as their respective constructors.

Registering endpoints

Endpoints can be automatically registered by the WordPressModule. This is done by extending the wp/rest_endpoints service with your own endpoint service IDs:

use Dhii\Services\Extensions\ArrayExtension;
use RebelCode\WpSdk\Module;
use RebelCode\WpSdk\Wp\RestEndpoint;

class MyModule extends Module
{
    public function getFactories(): array
    {
        return [
            'my_endpoint' => RestEndpoint::factory(/* ... */),
            'other_endpoint' => RestEndpoint::factory(/* ... */),
        ];
    }

    public function getExtensions() : array
    {
        return [
            'wp/rest_endpoints' => new ArrayExtension([
                'my_endpoint',
                'other_endpoint'
            ]),
        ];
    }
}

Clone this wiki locally