I ran into an undocumented and unexpected problem when registering custom post type meta fields for the REST API.
As of WordPress 4.7, there’s no way to register a meta field for a specific post type. Registering a field registers it for all post types, and registering it with
show_in_rest => true exposes it in all REST API endpoints.
That may seem harmless if you don’t stop to think about, but doing it could lead to unintentional privacy leaks. For example, an
email_address field might be perfectly safe to expose in one endpoint (e.g., something that’s already public like firstname.lastname@example.org), but exposing that same field on a different post type could be leaking someone’s private information.
Right now, there’s no officially-supported way to avoid that, other than using
register_rest_field() instead of
register_rest_field() isn’t a logical solution for post meta, though. With it, meta fields would not show up in the
meta tree of the response, where other meta fields are located, and where a client would expect to find them. Instead, they’d show up as top-level fields in the response, as if they were first-class
post object fields, or as if they were arbitrary fields (which is what I think
register_rest_field() is really intended for).
Having meta fields at the top-level also clutters the post item, making it harder to read, and annoying the crap out of grumpy, old, anal-retentive developers like me :)
So, in order to safely add meta fields inside the
meta tree where they belong, but without exposing them on endpoints where they don’t belong, I came up with an ugly workaround.
We can call
register_meta() conditionally, so that the meta fields are only registered during the specific API responses where we want to expose them. For all other API and non-API requests, it won’t be called at all, and the fields won’t be exposed.
If you use something like this, though, I’d recommend keeping an eye on #38323-core, and migrating to the official solution when it becomes available.