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 see  PostTypeToREST::class and  TaxonomyToREST::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:  stringintegernumberboolean. 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_callbackvalidate_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.

Still need help? Contact Us Contact Us