Skip to content

The WordPress REST API’s Undocumented Validation Options

The WordPress REST API is still very new. As with many early early technologies, documentation seems to be the last thing to get updated. This is why it can be fun/educational to read through the source code in open source projects. You’ll find things the documentation didn’t teach you! (Or at least not in any documentation I could easily find.)

The custom endpoint I was creating required quite a few arguments when called. I wanted to include sanitization and/or validation of these arguments. Setting up a schema for the endpoint made sense. I knew I could perform validation by specifying a ‘validate_callback’ function in the argument definition.

However browsing the source code in WordPress’ core revealed a few bonuses built right into the core, in a function called rest_validate_value_from_schema.

The ‘type’ attribute

By including in a ‘type‘ attribute when defining an argument, the REST API’s validation will automatically check if the value passed into this argument is of the appropriate data type.

register_rest_route( 'route/v1', '/endpoint/', array(
	'methods' => 'POST',
	'callback' => array( $this, 'endpoint_post_handler' ),
	'permissions_callback' => 'is_user_logged_in',
	'args' => array(
		'first_name' => array(
			'required' => true,
			'type' => 'string',
			'description' => 'The client\'s first name',
		),
	)
) );

In this example above, the “first_name” argument is required, and must be a string. ‘type‘ values that are supported in WordPress 4.7.2 are:

  • array
  • boolean
  • integer
  • number
  • string

NOTE: I could not get the ‘array’ type working properly in my tests. Once I figure out how to get this validation working properly, I’ll update this post.

If the value being passed to the REST endpoint for that argument does not match the data type specified by the type attribute, an HTTP 400 (Bad Request) error will be returned. Data about the invalid argument is returned in the body as part of the JSON response. The screenshot below shows an example of what happens if you pass a string into an argument with type set to boolean:

Sending a string to a boolean argument. (Screenshot from Postman)

The ‘enum’ attribute

The ‘enum‘ attribute allows you to specify a list of valid values for this argument as an array. This is called an enumeration or an enumerated type.

register_rest_route( 'route/v1', '/endpoint/', array(
	'methods' => 'POST',
	'callback' => array( $this, 'endpoint_post_handler' ),
	'permissions_callback' => 'is_user_logged_in',
	'args' => array(
		 'language_preference' => array(
			'required' => true,
			'type' => 'string',
			'description' => 'The locale in which this user would like to receive communications',
			'enum' => array( 
                             'en_CA',
                             'en_US',
                             'fr_CA',
                        ),
		),
	)
) );

In the example above, my language_preference endpoint is a string that only allows a predefined set of locale codes. If the value passed into the argument is not one of these items, an HTTP 400 error is returned. Data about the invalid argument is returned in the body as part of the JSON response.

The values are case-sensitive.

The ‘enum‘ attribute works in conjunction with the ‘type‘ attribute.

The ‘format’ attribute

The ‘format‘ attribute allows you to enforce the format of a string. Accepted values are:

  • date-time
  • email
  • ip
  • uri

For example, the following endpoint must contain an email address:

register_rest_route( 'route/v1', '/endpoint/', array(
	'methods' => 'POST',
	'callback' => array( $this, 'endpoint_post_handler' ),
	'permissions_callback' => 'is_user_logged_in',
	'args' => array(
		 'email' => array(
			'required' => true,
			'type' => 'string',
			'description' => 'The user\'s email address',
			'format' => 'email'
		),
	)
) );

If this were not an email address, an HTTP 400 error is returned. Data about the invalid argument is returned in the body as part of the JSON response.

The date value ensures that the argument contains an RFC3339 timestamp.

The ip value accepts both IPv4 and IPv6 addresses.

The uri value is a sanitization function. It will run the value through the esc_url_raw function to format it as a URI.

Numeric Ranges

There are a few attributes you can assign your endpoints arguments that will be used to check the value of both integer and numeric values. ( Setting the 'type' attribute to integer or number is required). These attributes are:

  • minimum
  • maximum
  • exclusiveMinimum
  • exclusiveMaximum

The minimum and maximum attributes contain the minimum and maximum values that will be accepted for this argument. The exclusiveMinimum and exclusiveMaximum attributes, when present, specify that the value must be greater less or less the specified number, but can’t be that number.

For example, the following endpoint accepts a ‘price’ argument with an integer value between 0 and 1000 (inclusive). Any value between 0 and 1000, including 0 or 1000, will be accepted.

register_rest_route( 'route/v1', '/endpoint/', array(
	'methods' => 'POST',
	'callback' => array( $this, 'endpoint_post_handler' ),
	'permissions_callback' => 'is_user_logged_in',
	'args' => array(
		 'price' => array(
			'required' => true,
			'type' => 'integer',
			'description' => 'Product value',
			'minimum' => 0,
			'maximum => 1000,
		),
	)
) );

The following endpoint accepts a ‘zero_one_two’ argument with an integer value between 0 and 3 (exclusive). This means that values 0, 1 and 2 will be accepted, but 3 will not.

register_rest_route( 'route/v1', '/endpoint/', array(
	'methods' => 'POST',
	'callback' => array( $this, 'endpoint_post_handler' ),
	'permissions_callback' => 'is_user_logged_in',
	'args' => array(
		 'zero_one_two' => array(
			'required' => true,
			'type' => 'integer',
			'description' => 'Product value',
			'minimum' => 0,
                        'maximum' => 3
			'maximumExclusive => true,
		),
	)
) );

This would be the same as using a minimum of 0 and a maximum of 2. The exclusiveMinimum and exclusiveMaximum attributes offer flexibility in how you define these ranges, and the wording of the returned error when a value falls outside these ranges.

Have fun!

These additional options should help you write better REST endpoints with less code than custom validation and sanitization functions.

Have fun!

One Comment

  1. Thanks for this post! It seems that the type value is case-sensitiv and ‘Array’ does the trick.

Leave a Reply

Your email address will not be published. Required fields are marked *