REST API
The Plugin API can auto-magically make your custom posts, meta fields and taxonomies available via REST API.
Basic Setup
Simply set CustomPostType:$show_in_rest
to true
. All your custom fields and taxonomies registered with your custom post type will be available via REST.
Not only all the post related data will be available in REST, it will support CRUD (Create/Read/Update/Delete) functionality via custom post type's endpoint, while obeying user roles, permissions and capabilities.
Custom fields and taxonomies inherit REST API visibility setting from the Post Type by default, but can be configured explicitly on their own level as well.
namepace hji\myplugin\models; use hji\common\postType\CustomPostType; class MyPostType extends CustomPostType { public $post_type = 'my-post-type'; public $show_in_rest = true; }
A list of custom posts from the example above will be available via this endpoint: /wp-json/wp/v2/my-post-types/
Example with custom fields:
namepace hji\myplugin\models; use hji\common\postType\CustomPostType; class MyPostType extends CustomPostType { public $post_type = 'my-post-type'; public $show_in_rest = true; public $fieldsSchema = [ [ 'id' => 'metabox-one', 'title' => 'Metabox One', 'fields' => [ [ 'id' => 'field_one', 'name' => 'Field One', 'type' => 'text_medium' ], [ 'id' => 'field_two', 'name' => 'Field Two', 'type' => 'text_medium' ] ] ] ]; }
GET /wp-json/wp/v2/my-post-types/
[ { "id": 3, "date": "2018-05-02T22:12:37", "date_gmt": null, "guid": { "rendered": "http:\/\/wptests.dev\/?p=3", "raw": "http:\/\/wptests.dev\/?p=3" }, "modified": "2018-05-02T22:12:37", "modified_gmt": "2018-05-02T22:12:37", "password": "", "slug": "", "status": "draft", "type": "my-post-type", "link": "http:\/\/wptests.dev\/?post_type=my-post-type&p=3", "title": { "raw": "", "rendered": "" }, "content": { "raw": "", "rendered": "", "protected": false }, "featured_media": 0, "template": "", "field_one": "testing api", "field_two": "" } ]
As you can see above, our custom meta fields are available within result object.
Note: custom fields available in REST API without the field prefix defined in
CustomPostType::$fieldPrefix
Example with custom taxonomies:
namepace hji\myplugin\models; use hji\common\postType\CustomPostType; class MyPostType extends CustomPostType { public $post_type = 'my-post-type'; public $show_in_rest = true; public $taxonomiesSchema = [ 'custom_taxonomy_one' => [ 'labels' => [ 'name' => 'Custom Taxonomy 1' ] ], 'custom_taxonomy_two' => [ 'labels' => [ 'name' => 'Custom Taxonomy 2' ] ] ]; }
GET /wp-json/wp/v2/my-post-types/*{post_id}***
{ "id": 3, "date": "2018-05-02T22:27:00", "date_gmt": "2018-05-02T22:27:00", "guid": { "rendered": "http:\/\/wptests.dev\/?p=3", "raw": "http:\/\/wptests.dev\/?p=3" }, "modified": "2018-05-02T22:27:00", "modified_gmt": "2018-05-02T22:27:00", "password": "", "slug": "3", "status": "publish", "type": "my-post-type", "link": "http:\/\/wptests.dev\/?test-post=3", "title": { "raw": "", "rendered": "" }, "content": { "raw": "", "rendered": "", "protected": false }, "featured_media": 0, "template": "", "custom_taxonomy_one": [ { "term_id": 5, "name": "default term", "slug": "default-term", "term_group": 0, "term_taxonomy_id": 5, "taxonomy": "custom_taxonomy_one", "description": "", "parent": 0, "count": 1, "filter": "raw" } ], "custom_taxonomy_two": [ { "term_id": 6, "name": "unit test", "slug": "unit-test", "term_group": 0, "term_taxonomy_id": 6, "taxonomy": "custom_taxonomy_two", "description": "", "parent": 0, "count": 1, "filter": "raw" } ] }
Create Post
POST /wp-json/wp/v2/my-post-types/
{ "title": "Post Title Goes Here", "content": "Post content goes here.", "status": "publish", "field_one": "Field one value", "custom_taxonomy_one": ["single tag"], "custom_taxonomy_two": ["multiple", "tags"] }
Response
{ "id": 4, "date": "2018-05-04T18:39:48", "date_gmt": null, "guid": { "rendered": "http:\/\/wptests.dev\/?post_type=my-post-types&p=4", "raw": "http:\/\/wptests.dev\/?post_type=my-post-types&p=4" }, "modified": "2018-05-04T18:39:48", "modified_gmt": null, "password": "", "slug": "", "status": "publish", "type": "my-post-types", "link": "http:\/\/wptests.dev\/?post_type=my-post-types&p=4", "title": { "raw": "Post Title Goes Here", "rendered": "Post Title Goes Here" }, "content": { "raw": "Post content goes here.", "rendered": "<p>Post content goes here.<\/p>\n", "protected": false }, "featured_media": 0, "template": "", "custom_taxonomy_one": [ { "term_id": 18, "name": "single tag", "slug": "single-tag", "term_group": 0, "term_taxonomy_id": 18, "taxonomy": "custom_taxonomy_one", "description": "", "parent": 0, "count": 0, "filter": "raw" } ], "custom_taxonomy_two": [ { "term_id": 19, "name": "multiple", "slug": "multiple", "term_group": 0, "term_taxonomy_id": 19, "taxonomy": "custom_taxonomy_two", "description": "", "parent": 0, "count": 0, "filter": "raw" }, { "term_id": 20, "name": "tags", "slug": "tags", "term_group": 0, "term_taxonomy_id": 20, "taxonomy": "custom_taxonomy_two", "description": "", "parent": 0, "count": 0, "filter": "raw" } ], "field_one": "Field one value", "field_two": "" }
Update Post
POST /wp-json/wp/v2/my-post-types/*{post_id}***
{ "field_two": "Field two value" }
Response
{ "id": 4, "date": "2018-05-04T18:39:48", "date_gmt": null, "guid": { "rendered": "http:\/\/wptests.dev\/?post_type=my-post-types&p=4", "raw": "http:\/\/wptests.dev\/?post_type=my-post-types&p=4" }, "modified": "2018-05-04T19:40:01", "modified_gmt": null, "password": "", "slug": "", "status": "publish", "type": "my-post-types", "link": "http:\/\/wptests.dev\/?post_type=my-post-types&p=4", "title": { "raw": "Post Title Goes Here", "rendered": "Post Title Goes Here" }, "content": { "raw": "Post content goes here.", "rendered": "<p>Post content goes here.<\/p>\n", "protected": false }, "featured_media": 0, "template": "", "custom_taxonomy_one": [ { "term_id": 18, "name": "single tag", "slug": "single-tag", "term_group": 0, "term_taxonomy_id": 18, "taxonomy": "custom_taxonomy_one", "description": "", "parent": 0, "count": 0, "filter": "raw" } ], "custom_taxonomy_two": [ { "term_id": 19, "name": "multiple", "slug": "multiple", "term_group": 0, "term_taxonomy_id": 19, "taxonomy": "custom_taxonomy_two", "description": "", "parent": 0, "count": 0, "filter": "raw" }, { "term_id": 20, "name": "tags", "slug": "tags", "term_group": 0, "term_taxonomy_id": 20, "taxonomy": "custom_taxonomy_two", "description": "", "parent": 0, "count": 0, "filter": "raw" } ], "field_one": "Field one value", "field_two": "Field two value" }
Delete Post
DELETE /wp-json/wp/v2/my-post-types/*{post_id}***
The DELETE request will move the post into the trash and return the original post object with status
- trash and slug
- __trashed.
Response
{ "id": 4, "date": "2018-05-04T18:39:48", "date_gmt": null, "guid": { "rendered": "http:\/\/wptests.dev\/?post_type=my-post-types&p=4", "raw": "http:\/\/wptests.dev\/?post_type=my-post-types&p=4" }, "modified": "2018-05-04T19:45:01", "modified_gmt": null, "password": "", "slug": "__trashed", "status": "trash", "type": "my-post-types", "link": "http:\/\/wptests.dev\/?custom-post-type=__trashed", "title": { "raw": "Post Title Goes Here", "rendered": "Post Title Goes Here" }, "content": { "raw": "Post content goes here.", "rendered": "<p>Post content goes here.<\/p>\n", "protected": false }, "featured_media": 0, "template": "", "custom_taxonomy_one": [ { "term_id": 18, "name": "single tag", "slug": "single-tag", "term_group": 0, "term_taxonomy_id": 18, "taxonomy": "custom_taxonomy_one", "description": "", "parent": 0, "count": 0, "filter": "raw" } ], "custom_taxonomy_two": [ { "term_id": 19, "name": "multiple", "slug": "multiple", "term_group": 0, "term_taxonomy_id": 19, "taxonomy": "custom_taxonomy_two", "description": "", "parent": 0, "count": 0, "filter": "raw" }, { "term_id": 20, "name": "tags", "slug": "tags", "term_group": 0, "term_taxonomy_id": 20, "taxonomy": "custom_taxonomy_two", "description": "", "parent": 0, "count": 0, "filter": "raw" } ], "field_one": "Field one value", "field_two": "Field two value" }
Advanced Setup
In general REST API does not differentiate between fields and taxonomies when it comes to their registration. These items are simply abstracted as REST fields, and rest-related parameters apply to both the same way.
Field-Level REST Configuration
While $show_in_rest
option is inherited by fields and taxonomies of the custom post type, they can be individually configured on the field/taxonomy level.
Example:
public $show_in_rest = true; public $fieldsSchema = [ [ 'id' => 'metabox-one', 'title' => 'Metabox One', 'fields' => [ [ 'id' => 'field_one', 'name' => 'Field One', 'type' => 'text_medium', 'show_in_rest' => false ], [ 'id' => 'field_two', 'name' => 'Field Two', 'type' => 'text_medium' ] ] ] ]; public $taxonomiesSchema = [ 'custom_taxonomy_one' => [ 'labels' => [ 'name' => 'Custom Taxonomy 1' ], 'show_in_rest' => false ], 'custom_taxonomy_two' => [ 'labels' => [ 'name' => 'Custom Taxonomy 2' ] ] ];
REST Field Schema
The Custom Post Type REST API supports standard WP arguments used by the core register_rest_field()
method.
Which are:
- get_callback - (string|array|null) Optional. The callback function used to retrieve the field value. Default is 'null', the field will not be returned in the response.
- update_callback - (string|array|null) Optional. The callback function used to set and update the field value. Default is 'null', the value cannot be set or updated.
- schema - (string|array|null) Optional. The callback function used to create the schema for this field. Default is 'null', no schema entry will be returned.
All or any of these parameters can be assigned to the custom fields and/or taxonomies with their schemas via rest_field_args
property:
public $show_in_rest = true; public $fieldsSchema = [ [ 'id' => 'metabox-one', 'title' => 'Metabox One', 'fields' => [ [ 'id' => 'field_one', 'name' => 'Field One', 'type' => 'text_medium', 'rest_field_args' => [ 'get_callback' => 'callableMethod', 'update_callback => 'callableMethod', 'schema' => [] ] ] ] ] ]; public $taxonomiesSchema = [ 'custom_taxonomy_one' => [ 'labels' => [ 'name' => 'Custom Taxonomy 1' ], 'rest_field_args' => [ 'get_callback' => 'callableMethod', 'update_callback => 'callableMethod', 'schema' => [] ] ] ];
Overriding Membership REST callbacks for fields and taxonomies is not recommended and may lead to unexpected results. For implementation details seePostTypeToREST::class
andTaxonomyToREST::class
.
rest_field_args.schema
gives you control over the schema of the field itself. The following properties are supported:
rest_field_args.schema
PARAMETER | DESCRIPTION | TYPE | REQUIRED |
---|---|---|---|
description | Field description. Defaults to the field/taxonomy definition if not specified. | string | |
type | Field value type. Accepted types: string , integer , number , boolean . Default string . |
string | ✔ |
items | Item types in the field value array. Only if type is set to array . Accepted properties: type . Same as above. |
array | † |
context | Default ['view', 'edit'] . Need more info |
array | |
properties | Need more info | array | |
arg_options | Accepted properties: sanitize_callback , validate_callback . |
array | |
† The items is only required when type is set to array . In that case you need to specify items.type , so the WP knows how to sanitize and validate items within the array. |