0

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).

2
  • Since it's a custom endpoint, you could just cancel the post creation via the main callback, i.e. 'callback' => function () { if ( some condition ) { create post } else { return existing post or something else } }. Or am I not understanding the question?
    – Sally CJ
    Commented Jun 4, 2021 at 1:52
  • 1
    You're right. I could use that. It took a little bit to figure out, that I then had to do the footwork that the API does myself, by inserting the post as well, but that was easier that I expected. If you post your comment as an answer, then I'll mark it appropriately. Thanks!
    – Zeth
    Commented Jun 4, 2021 at 8:37

1 Answer 1

3

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.

2
  • Awesome! Can I get you to throw in a line or two about, why it's important that I register my own namespace and not use wp/v2? What can go wrong, if wp/v2 is used?
    – Zeth
    Commented Jun 5, 2021 at 11:08
  • 1
    It's because the REST API handbook says, "Do not place anything into the wp namespace unless you are making endpoints with the intention of merging them into core.".. :) And personally, I thought using your own namespace would better remind you that it's your custom route.. specifically custom routes like one that handle custom form submissions.
    – Sally CJ
    Commented Jun 5, 2021 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.