WordPress 4.7 Introduced register_rest_field, which enables us to add custom fields to object types. Here is how to add one to a custom post type.

The basics

Custom post types can be made part of the REST API by explicitly declaring them “public”. Opted to declare this outside of register_post_type to separate concerns.

<?php

/**
 * Add REST API support to a post type
 */
function my_post_type_show_in_rest() {
  global $wp_post_types;
  $post_type_name = 'my_post_type';
  if( ! isset( $wp_post_types[ $post_type_name ] ) ) {
    return;
  }
  $my_post_type = $wp_post_types[ $post_type_name ];
    $my_post_type->show_in_rest = true;
    $my_post_type->rest_base = 'my-post-types';
}

add_action( 'init', 'my_post_type_show_in_rest', 25 );

This creates the endpoint wp/v2/my-post-types. What is missing is our custom fields (meta or otherwise).

Adding a custom field

Here is an example of adding a custom field using register_rest_field

<?php

function my_post_type_register_custom_fields() {
	register_rest_field( 'my_post_type', '_my_custom_field', array(
		'get_callback'    => 'get_my_custom_field',
		'update_callback' => null,
		'schema'          => as_item_schema_property(),
	) );
}

/**
 * @return string
 */
function get_my_custom_field() {
	global $post;
	return get_post_meta( $post->ID, '_my_custom_field', true );
}

function as_item_schema_property() {
  return array(
    'type' => 'string',
    'description' => 'A Description',
  );
}

add_action( 'rest_api_init', 'my_post_type_register_custom_fields' );