Validate rest-api call on create

The question:

WordPress Site A has an API, which is being used by another system/application (B). B is struggling to handle/controle/sanitize the posts being sent to the API, and often cause creation of duplicates. B is really struggling to solve this in their end, so I was wondering if I can add a validation-method to the endpoint in my end.

The problem is that it needs to be a validation on a create-endpoint.


Example

This is how I imagines it would look on an update endpoint (not tested):

I’m calling the endpoint: https://example.org/wp-json/wp/v2/foobar/100007?title=Test Title&meta[custom_var]=10

add_action( 'rest_api_init', function(){
  register_rest_route( 'wp/v2', '/foobar/(?P<id>d+)', [
    'methods'  => 'POST',
    'args'     => [
      'custom_var' => [
        'validate_callback' => function( $param, $request, $key ){
          if( $param > 5 ){
            return true
          }
          return false;
        },
      ],
    ],
  ] );
} );

But I can’t find anything in the rest documentation about validating the create endpoint.

I imagined it being something like this:

add_action( 'rest_api_init', function(){
  register_rest_route( 'wp/v2', '/foobar/', [
    'methods'  => 'POST',
    'validate_method' => function( ... ),
    ...
    ...
    ...
  ] );
} );

… But that doesn’t work.


I also considered doing the callback:

add_action( 'rest_api_init', function(){
  register_rest_route( 'wp/v2', '/foobar/', [
    'methods'  => 'POST',
    'callback' => function( ... ),
    ...
    ...
    ...
  ] );
} );

But that callback fires after the post is created (I assumed).

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

As I said in the comments, since it’s a custom endpoint, you could just cancel the post creation via the main callback, i.e. something like so:

'callback' => function () {
    if ( some condition ) {
        // create the post
    } else {
        // return existing post or something else
    }
}

And here’s an example where the POST endpoint creates a published post (of the post type), but only if there’s no existing published post having the same title as specified in a parameter named title:

Note: When registering custom REST routes, use your own namespace (e.g. my-namespace/v1) and not wp/v2.

add_action( 'rest_api_init', function() {
    register_rest_route( 'my-namespace/v1', '/foobar', array(
        'methods'             => 'POST',
        'callback'            => function ( $request ) {
            $posts = get_posts( array(
                'post_type' => 'post',
                'title'     => $request['title'],
                'fields'    => 'ids',
            ) );

            if ( ! empty( $posts ) ) {
                return new WP_Error(
                    'rest_post_exists',
                    'There is already a post with the same title.',
                    array( 'status' => 400 )
                );
            }

            return wp_insert_post( array(
                'post_type'   => 'post',
                'post_title'  => $request['title'],
                'post_status' => 'publish',
            ) );
        },
        'permission_callback' => function ( $request ) {
            return current_user_can( 'publish_posts' );
        },
        'args'                => array(
            'title' => array(
                'type'              => 'string',
                'required'          => true,
                'validate_callback' => function ( $param, $request ) {
                    if ( ! preg_match( '/[a-z0-9]+/', $param ) ) {
                        return new WP_Error(
                            'rest_invalid_title',
                            'Please enter a valid title for the post.',
                            array( 'status' => 400 )
                        );
                    }

                    return true;
                }
            ),
        ),
    ) );
} );

However, note that if foobar is actually a custom post type, then when registering the post type (see register_post_type()), you could actually set show_in_rest to true and then set rest_controller_class to the name of a custom REST controller class like the one here.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment