Imported from dallascrilley/dowser (
skills/wordpress-management/skills/wp-rest-api/SKILL.md). Install upstream withnpx skills add dallascrilley/dowser --skill wp-rest-api. Copyright stays with the author.
Wp-Rest-Api Skill
Comprehensive assistance with wp-rest-api development, generated from official documentation.
When to Use This Skill
This skill should be triggered when:
- Working with wp-rest-api
- Asking about wp-rest-api features or APIs
- Implementing wp-rest-api solutions
- Debugging wp-rest-api code
- Learning wp-rest-api best practices
Quick Reference
Common Patterns
Pattern 1: The API includes a number of global parameters (also called “meta-parameters”) which control how the API handles the request/response handling. These operate at a layer above the actual resources themselves, and are available on all resources. _fields A REST resource like a Post contains a large quantity of data: basic information such as content, title, and author ID, but also registered metadata and fields, media information, and links to other resources. Your application may not need all of this information on every request. To instruct WordPress to return only a subset of the fields in a response, you may use the _fields query parameter. If for example you are building an archive view and only need the ID, title, permalink, author and excerpt for a collection of posts, you can restrict the response to only those properties with this fields query: /wp/v2/posts?_fields=author,id,excerpt,title,link You may alternatively provide that same list using query parameter array syntax instead of a comma-separated list: /wp/v2/posts?_fields[]=author&_fields[]=id&_fields[]=excerpt&_fields[]=title&_fields[]=link When _fields is provided then WordPress will skip unneeded fields when generating your response object, avoiding potentially expensive internal computation or queries for data you don’t need. This also means the JSON object returned from the REST API will be smaller, requiring less time to download and less time to parse on your client device. Carefully design your queries to pull in only the needed properties from each resource to make your application faster to use and more efficient to run. As of WordPress 5.3 the _fields parameter supports nested properties. This can be useful if you have registered many meta keys, permitting you to request the value for only one of the registered meta properties: ?_fields=meta.one-of-many-keys Only the meta value with the key one-of-many-keys will be returned, and others will be excluded. You can also request specific deeply-nested properties within a complex meta object: ?_fields=meta.key_name.nested_prop.deeply_nested_prop,meta.key_name.other_nested_prop _embed Most resources include links to related resources. For example, a post can link to the parent post, or to comments on the post. To reduce the number of HTTP requests required, clients may wish to fetch a resource as well as the linked resources. The _embed parameter indicates to the server that the response should include these embedded resources. Embed mode is enabled if the _embed parameter is passed in the query string (GET parameter). This parameter does not require a value (i.e. ?_embed is valid), however can be passed “1” as a value if required by a client library. As of WordPress 5.4, the resources to embed can be limited by passing a list of link relation names to the _embed parameter. For example, /wp/v2/posts?_embed=author,wp:term will only embed the post’s author and the lists of terms associated with the post. Resources in embed mode will contain an additional _embedded key next to the _links key containing the linked resources. Only links with the embeddable parameter set to true will be embedded. In order to use _embed together with _fields, add _embedded as well as _links to the fields, for instance, /wp-json/wp/v2/posts/_embed=author,wp:term&_fields=title,author,_links,_embedded. For more about linking and embedding, see the Linking and Embedding page. _method (or X-HTTP-Method-Override header) Some servers and clients cannot correctly process some HTTP methods that the API makes use of. For example, all deletion requests on resources use the DELETE method, but some clients do not provide the ability to send this method. To ensure compatibility with these servers and clients, the API supports a method override. This can be passed either via a _method parameter or the X-HTTP-Method-Override header, with the value set to the HTTP method to use. Clients should only ever send a method override parameter or header with POST requests. Using the method override with GET requests may cause the request to be incorrectly cached. A POST to /wp-json/wp/v2/posts/42?_method=DELETE would be translated to a DELETE to the wp/v2/posts/42 route. Similarly, the following POST request would become a DELETE: POST /wp-json/wp/v2/posts/42 HTTP/1.1 Host: example.com X-HTTP-Method-Override: DELETE _envelope Similarly to _method, some servers, clients, and proxies do not support accessing the full response data. The API supports passing an _envelope parameter, which sends all response data in the body, including headers and status code. Envelope mode is enabled if the _envelope parameter is passed in the query string (GET parameter). This parameter does not require a value (i.e. ?_envelope is valid), but can be passed “1” as a value if required by a client library. For future compatibility, other values should not be passed. Enveloped responses include a “fake” HTTP 200 response code with no additional headers (apart from Content-Type) that should ensure the response correctly passes through intermediaries. For example, given the following response to a GET to wp/v2/users/me: HTTP/1.1 302 Found Location: http://example.com/wp-json/wp/v2/users/42 { "id": 42, ... } The equivalent enveloped response (with a GET to wp/v2/users/me?_envelope) would be: HTTP/1.1 200 OK { "status": 302, "headers": { "Location": "http://example.com/wp-json/wp/v2/users/42" }, "body": { "id": 42 } } _jsonp The API natively supports JSONP responses to allow cross-domain requests for legacy browsers and clients. This parameter takes a JavaScript callback function which will be prepended to the data. This URL can then be loaded via a tag. The callback function can contain any alphanumeric, _ (underscore), or . (period) character. Callbacks which contain invalid characters will receive a HTTP 400 error response, and the callback will not be called. Modern browsers can use Cross-Origin Resource Sharing (CORS) preflight requests for cross-domain requests, but JSONP can be used to ensure support with all browsers. Browser Support MDN Article on CORS For example: function receiveData( data ) { // Do something with the data here. // For demonstration purposes, we'll simply log it. console.log( data ); }
_fields
Pattern 2: For example:
<script>
function receiveData( data ) {
// Do something with the data here.
// For demonstration purposes, we'll simply log it.
console.log( data );
}
</script>
<script src="https://example.com/wp-json/?_jsonp=receiveData"></script>
Pattern 3: Overview A schema is metadata that tells us how our data is structured. Most databases implement some form of schema which enables us to reason about our data in a more structured manner. The WordPress REST API utilizes JSON Schema to handle the structuring of its data. You can implement endpoints without using a schema, but you will be missing out on a lot of things. It is up to you to decide what suits you best. JSON First, let’s talk about JSON a bit. JSON is a human readable data format that resembles JavaScript objects. JSON stands for JavaScript Object Notation. JSON is growing wildly in popularity and seems to be taking the world of data structure by storm. The WordPress REST API uses a special specification for JSON known as JSON schema. To learn more about JSON Schema please check out the JSON Schema website and this easier to understand introduction to JSON Schema. Schema affords us many benefits: improved testing, discoverability, and overall better structure. Let’s look at a JSON blob of data. { "shouldBeArray": 'LOL definitely not an array', "shouldBeInteger": ['lolz', 'you', 'need', 'schema'], "shouldBeString": 123456789 } A JSON parser will go through that data no problem and won’t complain about anything, because it is valid JSON. The clients and servers know nothing about the data and what to expect they just see the JSON. By implementing schema we can actually simplify our codebase. Schema will help structure our data better so our applications can more easily reason about our interactions with the WordPress REST API. The WordPress REST API does not force you to use schema, but it is encouraged. There are two ways in which schema data is incorporated into the API; schema for resources and schema for our registered arguments. Resource Schema The schema for a resource indicates what fields are present for a particular object. When we register our routes we can also specify the resource schema for the route. Let’s look at what a simple comment schema might look like in a PHP representation of JSON schema. // Register our routes. function prefix_register_my_comment_route() { register_rest_route( 'my-namespace/v1', '/comments', array( // Notice how we are registering multiple endpoints the 'schema' equates to an OPTIONS request. array( 'methods' => 'GET', 'callback' => 'prefix_get_comment_sample', ), // Register our schema callback. 'schema' => 'prefix_get_comment_schema', ) ); } add_action( 'rest_api_init', 'prefix_register_my_comment_route' ); /** * Grabs the five most recent comments and outputs them as a rest response. * * @param WP_REST_Request $request Current request. / function prefix_get_comment_sample( $request ) { $args = array( 'number' => 5, ); $comments = get_comments( $args ); $data = array(); if ( empty( $comments ) ) { return rest_ensure_response( $data ); } foreach ( $comments as $comment ) { $response = prefix_rest_prepare_comment( $comment, $request ); $data[] = prefix_prepare_for_collection( $response ); } // Return all of our comment response data. return rest_ensure_response( $data ); } /* * Matches the comment data to the schema we want. * * @param WP_Comment $comment The comment object whose response is being prepared. / function prefix_rest_prepare_comment( $comment, $request ) { $comment_data = array(); $schema = prefix_get_comment_schema(); // We are also renaming the fields to more understandable names. if ( isset( $schema['properties']['id'] ) ) { $comment_data['id'] = (int) $comment->comment_ID; } if ( isset( $schema['properties']['author'] ) ) { $comment_data['author'] = (int) $comment->user_id; } if ( isset( $schema['properties']['content'] ) ) { $comment_data['content'] = apply_filters( 'comment_text', $comment->comment_content, $comment ); } return rest_ensure_response( $comment_data ); } /* * Prepare a response for inserting into a collection of responses. * * This is copied from WP_REST_Controller class in the WP REST API v2 plugin. * * @param WP_REST_Response $response Response object. * @return array Response data, ready for insertion into collection data. / function prefix_prepare_for_collection( $response ) { if ( ! ( $response instanceof WP_REST_Response ) ) { return $response; } $data = (array) $response->get_data(); $links = rest_get_server()::get_compact_response_links( $response ); if ( ! empty( $links ) ) { $data['_links'] = $links; } return $data; } /* * Get our sample schema for comments. / function prefix_get_comment_schema() { $schema = array( // This tells the spec of JSON Schema we are using which is draft 4. '$schema' => 'http://json-schema.org/draft-04/schema#', // The title property marks the identity of the resource. 'title' => 'comment', 'type' => 'object', // In JSON Schema you can specify object properties in the properties attribute. 'properties' => array( 'id' => array( 'description' => esc_html__( 'Unique identifier for the object.', 'my-textdomain' ), 'type' => 'integer', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'author' => array( 'description' => esc_html__( 'The id of the user object, if author was a user.', 'my-textdomain' ), 'type' => 'integer', ), 'content' => array( 'description' => esc_html__( 'The content for the object.', 'my-textdomain' ), 'type' => 'string', ), ), ); return $schema; } If you notice, each comment resource now matches up to our schema that we specified. We made this switch in prefix_rest_prepare_comment(). By creating schema for our resources, we can now view this schema by making OPTIONS requests. Why is this useful? If we wanted other languages, JavaScript for example, to interpret our data and validate the data from our endpoint, JavaScript would need to know how our data is structured. When we provide schema, we open the doors for other authors, and ourselves, to build on top of our endpoints in a consistent manner. Schema provides machine readable data, so potentially anything that can read JSON can understand what kind of data it is looking at. When we look at the API index by making a GET request to https://ourawesomesite.com/wp-json/`, we are returned the schema of our API, enabling others to write client libraries to interpret our data. This process of reading schema data is known as discovery. When we have provided schema for a resource we make that resource discoverable viaOPTIONSrequests to that route. Exposing resource schema is only one part of our schema puzzle. We also want to use schema for our registered arguments. Argument Schema When we register request arguments for an endpoint, we can also use JSON Schema to provide us data about what the arguments should be. This enables us to write validation libraries that can be reused as our endpoints expand. Schema is more work upfront, but if you are going to write a production application that will grow, you should definitely consider using schema. Let’s look at an example of using argument schema and validation. // Register our routes. function prefix_register_my_arg_route() { register_rest_route( 'my-namespace/v1', '/schema-arg', array( // Here we register our endpoint. array( 'methods' => 'GET', 'callback' => 'prefix_get_item', 'args' => prefix_get_endpoint_args(), ), ) ); } // Hook registration into 'rest_api_init' hook. add_action( 'rest_api_init', 'prefix_register_my_arg_route' ); /** * Returns the request argumentmy-argas a rest response. * * @param WP_REST_Request $request Current request. */ function prefix_get_item( $request ) { // If we didn't use required in the schema this would throw an error when my arg is not set. return rest_ensure_response( $request['my-arg'] ); } /** * Get the argument schema for this example endpoint. */ function prefix_get_endpoint_args() { $args = array(); // Here we add our PHP representation of JSON Schema. $args['my-arg'] = array( 'description' => esc_html__( 'This is the argument our endpoint returns.', 'my-textdomain' ), 'type' => 'string', 'validate_callback' => 'prefix_validate_my_arg', 'sanitize_callback' => 'prefix_sanitize_my_arg', 'required' => true, ); return $args; } /** * Our validation callback formy-argparameter. * * @param mixed $value Value of the my-arg parameter. * @param WP_REST_Request $request Current request object. * @param string $param The name of the parameter in this case, 'my-arg'. * @return true|WP_Error True if the data is valid, WP_Error otherwise. */ function prefix_validate_my_arg( $value, $request, $param ) { $attributes = $request->get_attributes(); if ( isset( $attributes['args'][ $param ] ) ) { $argument = $attributes['args'][ $param ]; // Check to make sure our argument is a string. if ( 'string' === $argument['type'] && ! is_string( $value ) ) { return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%1$s is not of type %2$s', 'my-textdomain' ), $param, 'string' ), array( 'status' => 400 ) ); } } else { // This code won't execute because we have specified this argument as required. // If we reused this validation callback and did not have required args then this would fire. return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%s was not registered as a request argument.', 'my-textdomain' ), $param ), array( 'status' => 400 ) ); } // If we got this far then the data is valid. return true; } /** * Our sanitization callback formy-arg` parameter. * * @param mixed $value Value of the my-arg parameter. * @param WP_REST_Request $request Current request object. * @param string $param The name of the parameter in this case, 'my-arg'. * @return mixed|WP_Error The sanitize value, or a WP_Error if the data could not be sanitized. / function prefix_sanitize_my_arg( $value, $request, $param ) { $attributes = $request->get_attributes(); if ( isset( $attributes['args'][ $param ] ) ) { $argument = $attributes['args'][ $param ]; // Check to make sure our argument is a string. if ( 'string' === $argument['type'] ) { return sanitize_text_field( $value ); } } else { // This code won't execute because we have specified this argument as required. // If we reused this validation callback and did not have required args then this would fire. return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%s was not registered as a request argument.', 'my-textdomain' ), $param ), array( 'status' => 400 ) ); } // If we got this far then something went wrong don't use user input. return new WP_Error( 'rest_api_sad', esc_html__( 'Something went terribly wrong.', 'my-textdomain' ), array( 'status' => 500 ) ); } In the example above we have abstracted away from using the 'my-arg' name. We can use these validation and sanitizing functions for any other argument that should be a string we have specified schema for. As your codebase and endpoints grow, schema will help keep your code lightweight and maintainable. Without schema you can validate and sanitize, however it will be more difficult to keep track of which functions should be validating what. By adding schema to request arguments we can also expose our argument schema to clients, so validation libraries can be built client side which can help performance by preventing invalid requests from ever being sent to the API. If you are uncomfortable with using schema, it is still possible to have validate/sanitize callbacks for each of your arguments, and in some cases it will make the most sense to do a custom validation. Summary Schema can seem silly at points and possibly like unnecessary work, but if you want maintainable, discoverable, and easily extensible endpoints, it is essential to use schema. Schema also helps to self document your endpoints both for humans and computers! JSON Schema Basics WordPress implements a validator that uses a subset of the JSON Schema Version 4 specification. The RFC is recommended reading to gain a deeper understanding of how JSON Schema works, but this article will describe the basics of JSON Schema and what features WordPress supports. API The REST API defines two main functions for using JSON Schema: rest_validate_value_from_schema and rest_sanitize_value_from_schema. Both functions accept the request data as the first parameter, the parameter’s schema definition as the second parameter, and optionally the parameter’s name as the third parameter. The validate function returns either true or a WP_Error instance depending on if the data successfully validates against the schema. The sanitize function returns a sanitized form of the data passed to the function, or a WP_Error instance if the data cannot be safely sanitized. When calling these functions, you should take care to always first validate the data using rest_validate_value_from_schema, and then if that function returns true, sanitize the data using rest_sanitize_value_from_schema. Not using both can open up your endpoint to security vulnerabilities. If your endpoint is implemented using a subclass of WP_REST_Controller, the WP_REST_Controller::get_endpoint_args_for_item_schema method will automatically mark your arguments as using the built-in validate and sanitize callbacks. As such, there is no need to manually specify the callbacks. If your endpoint does not follow the controller class pattern, args returned from WP_REST_Controller::get_collection_params(), or any other instance where callbacks are not specified, the WP_REST_Request object will apply sanitization and validation using the rest_parse_request_arg function. Importantly, this is only applied when the sanitize_callback is not defined. As such, if you specify a custom sanitize_callback for your argument definition, the built-in JSON Schema validation will not apply. If you need this validation, you should manually specify rest_validate_request_arg as the validate_callback in your argument definition. Caching Schema Schema may be complex, and can take time to generate. You should consider caching generated schema in your plugin’s custom endpoints to avoid repeatedly generating the same schema object. If you are defining your endpoint using a a subclass of WP_REST_Controller, that might look like this: / * Retrieves the attachment's schema, conforming to JSON Schema. * * @return array Item schema as an array. / public function get_item_schema() { // Returned cached copy whenever available. if ( $this->schema ) { return $this->add_additional_fields_schema( $this->schema ); } $schema = parent::get_item_schema(); // Add endpoint-specific properties to Schema. $schema['properties']['field_name'] = array( / ... / ); $schema['properties']['etcetera'] = array( / ... */ ); // Cache generated schema on endpoint instance. $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); } This pattern was introduced into WordPress core in version 5.3 in #47871, and yielded up to 40% speed improvement when generating some API responses. Schema Documents A basic schema document consists of a few properties. $schema A reference to a meta schema that describes the version of the spec the document is using. title The title of the schema. Normally this is a human readable label, but in WordPress this is machine readable string . The posts endpoint, for example, has a title of ‘post’. Comments has a title of ‘comment’. type This refers to the type of the value being described. This can be any one of the seven primitive types. In WordPress the top-level type will almost always be an object, even for collection endpoints that return an array of objects. properties A list of the known properties contained in the object and their definitions. Each property definition itself is also a schema, but without the $schema top-level property, more accurately described as a sub-schema. Primitive Types JSON Schema defines a list of seven allowed primitive types. string A string value. null A null value. number Any number. Decimals are allowed. Equivalent to a float in PHP. integer A number, but decimals or exponents are not allowed. boolean A true or false value. array A list of values. This is equivalent to a JavaScript array. In PHP this is referred to as a numerical array, or an array without defined keys. object A map of keys to values. This is equivalent to a JavaScript object. In PHP this is referred to as an associative array, or an array with defined keys. A value’s primitive type is specified using the type keyword. For example, this is how you would define a string value with JSON Schema. array( 'type' => 'string', ); JSON Schema allows for defining values that can be multiple types. For example, this is how you would define a value that can be either a string or a boolean. array( 'type' => array( 'boolean', 'string' ), ); Type Juggling Because the WordPress REST API accepts URL form encoded data both in the POST body or as the query portion of the URL, many primtive types perform type juggling to convert these string values into their proper native types. string Only strings according to is_string are allowed. null Only a properly typed null is accepted. This means that submitting a null value in the URL or as a URL form encoded post body is not possible, a JSON request body must be used. number Floats, integers, and strings that pass is_numeric are allowed. Values will be casted to a float. integer Integers or strings that can be casted to a float with a fractional part that is equivalent to 0. boolean Booleans, the integers 0 and 1, or the strings "0", "1", "false", and "true". 0 is treated as false and 1 is treated as true. array Numeric arrays according to wp_is_numeric_array or a string. If the string is comma separated it will be split into an array, otherwise it will be an array containing the string value. For example: "red,yellow" becomes array( "red", "yellow" ) and "blue" becomes array( "blue" ). object An array, an stdClass object, an object implementing JsonSerializable or an empty string. Values will be converted to a native PHP array. When using multiple types, types will be evaluated in the order they are specified. This can have an impact on the sanitized data received by your REST API endpoint. For instance, in the previous example, if the value submitted was "1", it would be sanitized to the boolean true value. However, if the order was flipped, the value would remain as the string "1". The JSON Schema specification allows for defining schemas without a type field. The WordPress implementation however requires a type to be defined, and will issue a _doing_it_wrong notice if a type is ommitted. Format The seven primitive types are just that, primitive, so how do you define more complex value types? One of the ways to do that is using the format keyword. The format keyword allows for defining additional semantic-level validation of values that have a well-defined structure. For example, to require a date value, you would use the date-time format. array( 'type' => 'string', 'format' => 'date-time', ); WordPress supports the following formats: date-time A date formatted according to RFC3339. uri A uri according to esc_url_raw. email An email address according to is_email. ip A v4 or v6 ip address. uuid A uuid of any version. hex-color A 3 or 6 character hex color with a leading #. A value must match its format even if the value is an empty string. If allowing an “empty” value is desired, add null as a possible type. For example, the following schema would allow 127.0.0.1 or null as possible values. array( 'type' => array( 'string', 'null' ), 'format' => 'ip', ); Strings The string types supports three additional keywords. minLength and maxLength The minLength and maxLength keywords can be used to constrain the acceptable length of a string. Importantly multi-byte characters are counted as a single character and bounds are inclusive. For instance, given the following schema, ab, abc, and abcd are valid, while a, and abcde are invalid. array( 'type' => 'string', 'minLength' => 2, 'maxLength' => 4, ); The exclusiveMinimum and exclusiveMaximum keywords do not apply, they are only valid for numbers. pattern The JSON Schema keyword pattern can be used to validate that a string field matches a regular expression. For instance, given the following schema, #123 would be valid, but #abc would not. array( 'type' => 'string', 'pattern' => '#[0-9]+', ); The regex is not automatically anchored. Regex flags, for instance /i to make the match case insensitive are not supported. The JSON Schema RFC recommends limiting yourself to the following regex features so the schema can be interoperable between as many different programming languages as possible. individual Unicode characters, as defined by the JSON specification [RFC4627]. simple character classes [abc], range character classes [a-z]. complemented character classes [^abc], [^a-z]. simple quantifiers: + (one or more), * (zero or more), ? (zero or one), and their lazy versions +?, *?, ??. range quantifiers: {x} (exactly x occurrences), {x,y} (at least x, at most y, occurrences), {x,} (x occurrences or more), and their lazy versions. the beginning-of-input ^ and end-of-input $ anchors. simple grouping (...) and alternation |. The pattern should be valid according to the ECMA 262 regex dialect. Numbers The number and integer types support four additional keywords. minimum and maximum The minimum and maximum keyword allow constraining the range of acceptable numbers. For example, 2 would be valid according to this schema, but 0 and 4 would not be. array( 'type' => 'integer', 'minimum' => 1, 'maximum' => 3, ); JSON Schema also allows using the exclusiveMinimum and exclusiveMaximum keywords to denote that the value cannot equal the defined minimum or maximum respectively. For example, in this case only 2 would be an acceptable value. array( 'type' => 'integer', 'minimum' => 1, 'exclusiveMinimum' => true, 'maximum' => 3, 'exclusiveMaximum' => true, ); multipleOf The multipleOf keyword allows for asserting an integer or number type is a multiple of the given number. For example, this schema will only accept even integers. array( 'type' => 'integer', 'multipleOf' => 2, ); multipleOf also supports decimals. For example, this schema could be used to accept a percentage with a maximum of 1 decimal point. array( 'type' => 'number', 'minimum' => 0, 'maximum' => 100, 'multipleOf' => 0.1, ); Arrays Specifying a type of array requires data to be an array, but that is only half the validation story. You’ll also want to enforce the format of each item in the array. This is done by specifying a JSON Schema that each array item must conform to using the items keyword. For example, the following schema requires an array of IP addresses. array( 'type' => 'array', 'items' => array( 'type' => 'string', 'format' => 'ip', ), ); This would pass validation. [ "127.0.0.1", "255.255.255.255" ] While this would fail validation. [ "127.0.0.1", 5 ] The items schema can be any schema, it could even be an array itself! array( 'type' => 'array', 'items' => array( 'type' => 'array', 'items' => array( 'type' => 'string', 'format' => 'hex-color', ), ), ); This would pass validation. [ [ "#ff6d69", "#fecc50" ], [ "#0be7fb" ] ] While this would fail validation. [ [ "#ff6d69", "#fecc50" ], "george" ] minItems and maxItems The minItems and maxItems keywords can be used to constrain the acceptable number of items included in the array. For instance, given the following schema, [ "a" ] and [ "a", "b" ] are valid, while [] and [ "a", "b", "c" ] are invalid. array( 'type' => 'array', 'minItems' => 1, 'maxItems' => 2, 'items' => array( 'type' => 'string', ), ); The exclusiveMinimum and exclusiveMaximum keywords do not apply, they are only valid for the number and integer types. uniqueItems The uniqueItems keyword can be used to require that all items in the array are unique. For instance, given the following schema, [ "a", "b" ] is valid, while [ "a", "a" ] is not. array( 'type' => 'array', 'uniqueItems' => true, 'items' => array( 'type' => 'string', ), ); Uniqueness Items of different types are considered unique, for instance, "1", 1 and 1.0 are different values. When arrays are compared, the order of items matters. So the given array is considered to have all unique items. [ [ "a", "b" ], [ "b", "a" ] ] When objects are compared, the order the members appear in does not matter. So the given array is considered to have duplicate items since the values are the same, they just appear in a different order. [ { "a": 1, "b": 2 }, { "b": 2, "a": 1 } ] Uniqueness is checked in both rest_validate_value_from_schema and rest_sanitize_value_from_schema. This is to prevent instances where items would be considered unique before sanitization is applied, but after sanitization the items would converge to identical values. Take for instance the following schema: array( 'type' => 'array', 'uniqueItems' => true, 'items' => array( 'type' => 'string', 'format' => 'uri', ), ); A request with [ "https://example.org/hello world", "https://example.org/hello%20world" ] would pass validation because each string value is different. However, after esc_url_raw converted the space in the first url to %20 the values would be identical. In this case rest_sanitize_value_from_schema would return an error. As such, you should always validate and sanitize parameters. Objects Specifying a type of object requires data to be an object, but that is only half the validation story. You’ll also want to enforce the format of each object’s property. This is done by specifying a map of property names to JSON Schemas that object members must conform to using the properties keyword. For example, the following schema requires an object where the property name is a string and color is a hex color. array( 'type' => 'object', 'properties' => array( 'name' => array( 'type' => 'string', ), 'color' => array( 'type' => 'string', 'format' => 'hex-color', ), ), ); This would pass validation. { "name": "Primary", "color": "#ff6d69" } While this would fail validation. { "name": "Primary", "color": "orange" } Required Properties By default, any properties listed for an object are optional, so while it might be unexpected, the following would also pass validation for the previous schema. { "name": "Primary" } There are two mechanisms for requiring a property to be provided. Version 3 Syntax While WordPress mostly follows JSON Schema Version 4, one way that it doesn’t is with the syntax for defining required properties. The predominant way is using JSON Schema Version 3 syntax by adding the required keyword to each property’s definition. array( 'type' => 'object', 'properties' => array( 'name' => array( 'type' => 'string', 'required' => true, ), 'color' => array( 'type' => 'string', 'format' => 'hex-color', 'required' => true, ), ), ); Version 4 syntax WordPress also supports JSON Schema Version 4 required property syntax where the list of required properties for an object is defined as an array of property names. This can be particularly helpful when specifying that a meta value has a list of required properties. Given the following meta field. register_post_meta( 'post', 'fixed_in', array( 'type' => 'object', 'show_in_rest' => array( 'single' => true, 'schema' => array( 'required' => array( 'revision', 'version' ), 'type' => 'object', 'properties' => array( 'revision' => array( 'type' => 'integer', ), 'version' => array( 'type' => 'string', ), ), ), ), ) ); The following request would fail validation. { "title": "Check required properties", "content": "We should check that required properties are provided", "meta": { "fixed_in": { "revision": 47089 } } } If the fixed_in meta field was omitted entirely, no error would be generated. An object that defines a list of required properties does not indicate that the object itself is required to be submitted. Just that if the object is included, that the listed properties must also be included as well. Version 4 syntax is not supported for an endpoint’s top level schema in WP_REST_Controller::get_item_schema(). Given the following schema, a user could successfully submit a request without the title or content properties. This is because the schema document is not itself used for validation, but instead transformed to a list of parameter definitions. array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'my-endpoint', 'type' => 'object', 'required' => array( 'title', 'content' ), 'properties' => array( 'title' => array( 'type' => 'string', ), 'content' => array( 'type' => 'string', ), ), ); additionalProperties Perhaps unintuitively, by default JSON Schema also allows providing additional properties that are not specified in the schema. As such, the following would pass validation. { "name": "Primary", "color": "#ff6d69", "description": "The primary color to use in the theme." } This can be customized using the additionalProperties keyword. Setting additionalProperties to false will reject data that has unknown properties. array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'name' => array( 'type' => 'string', ), 'color' => array( 'type' => 'string', 'format' => 'hex-color', ), ), ); The additionalProperties keyword can also be set to a schema of its own. This would require that the values for any unknown properties match the given schema. One way this can be helpful is when you want to accept a list of values that each have their own unique key. For example: array( 'type' => 'object', 'properties' => array(), 'additionalProperties' => array( 'type' => 'object', 'properties' => array( 'name' => array( 'type' => 'string', 'required' => true, ), 'color' => array( 'type' => 'string', 'format' => 'hex-color', 'required' => true, ), ), ), ); This would pass validation. { "primary": { "name": "Primary", "color": "#ff6d69" }, "secondary": { "name": "Secondary", "color": "#fecc50" } } While this would fail validation. { "primary": { "name": "Primary", "color": "#ff6d69" }, "secondary": "#fecc50" } patternProperties The patternProperties keyword is similar to the additionalProperties keyword, but allows for asserting the property matches a regex pattern. The keyword is an object where each property is a regex pattern and its value is the JSON Schema used to validate properties that match that pattern. For example, this schema requires that each value is a hex color and the property must only contain “word” characters. array( 'type' => 'object', 'patternProperties' => array( '^\w+$' => array( 'type' => 'string', 'format' => 'hex-color', ), ), 'additionalProperties' => false, ); This would pass validation. { "primary": "#ff6d69", "secondary": "#fecc50" } While this would fail validation. { "primary": "blue", "$secondary": "#fecc50" } When the REST API validates the patternProperties schema, if a property doesn’t match any of the patterns, the property will be allowed and not have any validation applied to its contents. While this may be confusing, it behaves the same as the properties keyword. If this logic isn’t desired, add additionalProperties to the schema to disallow non-matching properties. minProperties and maxProperties The minItems and maxItems keywords can be used for the array type. The minProperties and maxProperties introduces this same functionality for the object type. This can be helpful when using additionalProperties to have a list of objects with unique keys. array( 'type' => 'object', 'additionalProperties' => array( 'type' => 'string', 'format' => 'hex-color', ), 'minProperties' => 1, 'maxProperties' => 3, ); This would pass validation. { "primary": "#52accc", "secondary": "#096484" } While this would fail validation. { "primary": "#52accc", "secondary": "#096484", "tertiary": "#07526c" } The exclusiveMinimum and exclusiveMaximum keywords do not apply, they are only valid for the number and integer types. Type agnostic keywords oneOf and anyOf These are advanced keywords that allow for the JSON Schema validator to choose one of many schemas to use when validating a value. The anyOf keyword allows for a value to match at least one of the given schemas. Whereas, the oneOf keyword requires the value match exactly one schema. For example, this schema allows for submitting an array of “operations” to an endpoint. Each operation can either be a “crop” or a “rotation”. array( 'type' => 'array', 'items' => array( 'oneOf' => array( array( 'title' => 'Crop', 'type' => 'object', 'properties' => array( 'operation' => array( 'type' => 'string', 'enum' => array( 'crop', ), ), 'x' => array( 'type' => 'integer', ), 'y' => array( 'type' => 'integer', ), ), ), array( 'title' => 'Rotation', 'type' => 'object', 'properties' => array( 'operation' => array( 'type' => 'string', 'enum' => array( 'rotate', ), ), 'degrees' => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => 360, ), ), ), ), ), ); The REST API will loop over each schema specified in the oneOf array and look for a match. If exactly one schema matches, then validation will succeed. If more than one schema matches, validation will fail. If no schemas match, then the validator will try to find the closest matching schema and return an appropriate error message. operations[0] is not a valid Rotation. Reason: operations[0][degrees] must be between 0 (inclusive) and 360 (inclusive) To generate more helpful error messages, it is highly recommended to give each oneOf or anyOf schema a title property. Changelog WordPress 5.6 Support the multipleOf JSON Schema keyword. r49063 Support the minProperties and maxProperties JSON Schema keywords. r49053 Support the patternProperties JSON Schema keyword. r49082 Support the anyOf and oneOf JSON Schema keywords. r49246 WordPress 5.5 Improve multi-type JSON Schema support. r48306 Check required properties are provided when validating an object. r47809 Only validate the format keyword if the type is a string. r48300 Support the uuid JSON Schema format. 47753 Support the hex-color JSON Schema format. r47450 Support the pattern JSON Schema keyword. r47810 Support the minItems, maxItems, and uniqueItems JSON Schema keywords. r47923 r48357 Support the minLength and maxLength JSON Schema keywords. r47627 WordPress 5.4 Support type juggling an empty string to an empty object. r47362 WordPress 5.3 Support the null primitive type and implement basic multi-type handling. r46249 Support validating additionalProperties against a schema. r45807 WordPress 4.9 Support the object primitive type. r41727
{
"shouldBeArray": 'LOL definitely not an array',
"shouldBeInteger": ['lolz', 'you', 'need', 'schema'],
"shouldBeString": 123456789
}
Pattern 4: The REST API includes a JavaScript/Backbone client library. The library provides an interface for the WP REST API by providing Backbone Models and Collections for all endpoints exposed through the API Schema. Using Activate the WP-API plugin. Enqueue the script directly: wp_enqueue_script( 'wp-api' ); or as a dependency for your script: wp_enqueue_script( 'my_script', 'path/to/my/script', array( 'wp-api' ) ); The library parses the root endpoint (the ‘Schema’) and creates matching Backbone models and collections. You will now have two root objects available to you: wp.api.models and wp.api.collections. The models and collections include: Models: * Category * Comment * Media * Page * PageMeta * PageRevision * Post * PostMeta * PostRevision * Schema * Status * Tag * Taxonomy * Type * User Collections: * Categories * Comments * Media * PageMeta * PageRevisions * Pages * Posts * Statuses * Tags * Taxonomies * Types * Users You can use these endpoints as-is to read, update, create and delete items using standard Backbone methods (fetch, sync, save & destroy for models, sync for collections). You can also extend these objects to make them your own, and build your views on top of them. Default values Each model and collection includes a reference to its default values, for example: wp.api.models.Post.prototype.args author: null comment_status: null content: null date: null date_gmt: null excerpt: null featured_media: null format: null modified: null modified_gmt: null password: null ping_status: null slug: null status: null sticky: null title: null Available methods Each model and collection contains a list of methods the corresponding endpoint supports. For example, models created from wp.api.models.Post have a methods array of: ["GET", "POST", "PUT", "PATCH", "DELETE"] Accepted options Each model and collection contains a list of options the corresponding endpoint accepts (note that options are passed as the second parameter when creating models or collections), for example: wp.api.collections.Posts.prototype.options author context filter order orderby page per_page search status Localizing the API Schema The client will accept and use a localized schema as part of the wpApiSettings object. The Schema is currently not passed by default; instead the client makes an ajax request to the API to load the Schema, then caches it in the browser’s session storage (if available). Activating the client-js plugin with SCRIPT_DEBUG enabled uses a localized Schema. Check the client-js example or this branch which attempts to only localize the schema once per client. Waiting for the client to load Client startup is asynchronous. If the api schema is localized, the client can start immediately; if not the client makes an ajax request to load the schema. The client exposes a load promise for provide a reliable wait to wait for client to be ready: wp.api.loadPromise.done( function() { //... use the client here } ) Model examples: To create a post and edit its categories, make sure you are logged in, then: // Create a new post var post = new wp.api.models.Post( { title: 'This is a test post' } ); post.save(); // Load an existing post var post = new wp.api.models.Post( { id: 1 } ); post.fetch(); // Get a collection of the post's categories (returns a promise) // Uses _embedded data if available, in which case promise resolves immediately. post.getCategories().done( function( postCategories ) { // ... do something with the categories. // The new post has an single Category: Uncategorized console.log( postCategories[0].name ); // response -> "Uncategorized" } ); // Get a posts author User model. post.getAuthorUser().done( function( user ){ // ... do something with user console.log( user.get( "name" ) ); } ); // Get a posts featured image Media model. post.getFeaturedMedia().done( function( image ){ // ... do something with image console.log( image ); } ); // Set the post categories. post.setCategories( [ "apples", "oranges" ] ); // Get all the categories var allCategories = new wp.api.collections.Categories() allCategories.fetch(); var appleCategory = allCategories.findWhere( { slug: "apples" } ); // Add the category to the postCategories collection we previously fetched. appleCategory.set( "parent_post", post.get( "id" ) ); // Use the POST method so Backbone will not PUT it even though it has an id. postCategories.create( appleCategory.toJSON(), { type: "POST" } ); // Remove the Uncategorized category postCategories.at( 0 ).destroy(); // Check the results - re-fetch postCategories = post.getCategories(); postCategories.at( 0 ).get( "name" ); // response -> "apples" Collection examples: To get the last 10 posts: var postsCollection = new wp.api.collections.Posts(); postsCollection.fetch(); To get the last 25 posts: postsCollection.fetch( { data: { per_page: 25 } } ); Use filter to change the order & orderby options: postsCollection.fetch( { data: { 'filter': { 'orderby': 'title', 'order': 'ASC' } } } ); All collections support pagination automatically, and you can get the next page of results using more: postsCollection.more(); To get page 5 of a collection: posts.fetch( { data: { page: 5 } } ); Check if the collection has any more posts: posts.hasMore(); Working With Revisions You can access post or page revisions using the PostRevisions or PageRevisions collections or through the Post or Page collection. For example, to get a collection of all revisions of post ID 1: var revisions = new wp.api.collections.PostRevisions({}, { parent: 1 }); Revision collections can also be accessed via their parent’s collection. This example makes 2 HTTP requests instead of one, but now the original post and its revisions are available: var post = new wp.api.models.Post( { id: 1 } ); post.fetch(); post.getRevisions().done( function( revisions ){ console.log( revisions ); }); If you add custom endpoints to the API they will also become available as models/collections. For example, you will get new models and collections when you add REST API support to your custom post type. To access custom endpoints created, use the item’s JSON endpoint name as CamelCase; for example if the item is found at the JSON path of /wp-json/wp/v2/my-custom-post, it can be accessed via the api at wp.api.collections.MyCustomPost. Note: Because the schema is stored in the user’s session cache to avoid re-fetching, you may need to open a new tab to get a new read of the Schema. // Extend wp.api.models.Post and wp.api.collections.Posts to load a custom post type const CustomPost = wp.api.models.Post.extend( { urlRoot: wpApiSettings.root + 'wp/v2/custom_post_slug', defaults: { type: 'custom_post_slug', }, } ); const CustomPosts = wp.api.collections.Posts.extend( { url: wpApiSettings.root + 'wp/v2/custom_post_slug', model: BLProduct, } ); const someCustomPosts = new CustomPosts(); someCustomPosts.fetch().then( ( posts ) => { // do something with the custom posts } );
wp_enqueue_script( 'wp-api' );
Pattern 5: Each model and collection includes a reference to its default values, for example:
wp.api.models.Post.prototype.args
Pattern 6: Each model and collection contains a list of options the corresponding endpoint accepts (note that options are passed as the second parameter when creating models or collections), for example:
wp.api.collections.Posts.prototype.options
Reference Files
This skill includes comprehensive documentation in references/:
- rest-api.md - Rest-Api documentation
Use view to read specific reference files when detailed information is needed.
Working with This Skill
For Beginners
Start with the getting_started or tutorials reference files for foundational concepts.
For Specific Features
Use the appropriate category reference file (api, guides, etc.) for detailed information.
For Code Examples
The quick reference section above contains common patterns extracted from the official docs.
Resources
references/
Organized documentation extracted from official sources. These files contain:
- Detailed explanations
- Code examples with language annotations
- Links to original documentation
- Table of contents for quick navigation
scripts/
Add helper scripts here for common automation tasks.
assets/
Add templates, boilerplate, or example projects here.
Notes
- This skill was automatically generated from official documentation
- Reference files preserve the structure and examples from source docs
- Code examples include language detection for better syntax highlighting
- Quick reference patterns are extracted from common usage examples in the docs
Updating
To refresh this skill with updated documentation:
- Re-run the scraper with the same configuration
- The skill will be rebuilt with the latest information