Post Meta Generator
Typed meta keys with a sanitiser, an auth callback and a REST schema — so a block, the API and your PHP all read the same value the same way.
<?php // Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Plugin Name: post meta * Description: Registers 3 meta keys for post. * Version: 1.0.0 * Requires PHP: 7.4 * Text Domain: acme */ defined( 'ABSPATH' ) || exit; /** * Who may read and write these keys through the API. * * @param bool $allowed Whether the user can act. * @param string $meta_key The key. * @param int $post_id The post. * @return bool */ function acme_meta_auth( $allowed, $meta_key, $post_id ) { return current_user_can( 'edit_post', $post_id ); } /** * Register the meta keys. */ function acme_register_meta() { register_post_meta( 'post', 'acme_reading_time', array( 'type' => 'integer', 'single' => true, 'description' => __( 'Estimated reading time in minutes.', 'acme' ), 'default' => 0, 'sanitize_callback' => 'absint', 'auth_callback' => 'acme_meta_auth', 'show_in_rest' => true, ) ); register_post_meta( 'post', 'acme_subtitle', array( 'type' => 'string', 'single' => true, 'description' => __( 'Shown under the title.', 'acme' ), 'default' => '', 'sanitize_callback' => 'sanitize_text_field', 'auth_callback' => 'acme_meta_auth', 'show_in_rest' => true, ) ); register_post_meta( 'post', 'acme_featured', array( 'type' => 'boolean', 'single' => true, 'description' => __( 'Pin this post to the top of archives.', 'acme' ), 'default' => false, 'sanitize_callback' => 'rest_sanitize_boolean', 'auth_callback' => 'acme_meta_auth', 'show_in_rest' => true, ) ); } add_action( 'init', 'acme_register_meta' ); /** * Read acme_reading_time. * * @param int $post_id Post ID. * @return mixed */ function acme_get_reading_time( $post_id = 0 ) { $post_id = $post_id ? $post_id : get_the_ID(); return get_post_meta( $post_id, 'acme_reading_time', true ); } /** * Read acme_subtitle. * * @param int $post_id Post ID. * @return mixed */ function acme_get_subtitle( $post_id = 0 ) { $post_id = $post_id ? $post_id : get_the_ID(); return get_post_meta( $post_id, 'acme_subtitle', true ); } /** * Read acme_featured. * * @param int $post_id Post ID. * @return mixed */ function acme_get_featured( $post_id = 0 ) { $post_id = $post_id ? $post_id : get_the_ID(); return get_post_meta( $post_id, 'acme_featured', true ); } /* * uninstall.php — remove the meta when the plugin is deleted. * * defined( 'WP_UNINSTALL_PLUGIN' ) || exit; * delete_post_meta_by_key( 'acme_reading_time' ); * delete_post_meta_by_key( 'acme_subtitle' ); * delete_post_meta_by_key( 'acme_featured' ); */
About this generator
Post Meta Generator Online
Register typed, documented, REST-visible custom fields with register_post_meta() instead of scattering bare get_post_meta() calls through your templates. Declare each key once — its type, whether it is single, its default, its sanitiser and who may write it — and this generator produces the registration, a shared auth_callback, optional typed accessor functions, and the uninstall.php cleanup most plugins never get round to.
A Reference tab lists every argument register_post_meta() accepts with a plain-English note on what it changes, so you can check a decision without leaving the page. Free, no account, and nothing leaves the browser.
Reviewed Output tested on WordPress 6.8 · PHP 8.3
Why registering post meta beats calling update_post_meta() and hoping
Unregistered meta has no type, no sanitiser, no default and no REST presence. It works until a block needs to read it, until an integer arrives as the string "12", or until an empty key returns an empty string where your template expected zero. Registration fixes all four at once — and gets the auth_callback right, which is the part that decides who can write your fields over the API.
One auth_callback, written explicitly
A single named callback is emitted and referenced by every key, checking edit_post for the specific post, edit_posts site-wide, or manage_options. That matters: leave auth_callback out entirely and WordPress falls back to __return_true for a normal key and __return_false for an underscore-prefixed one, which is rarely what anyone intended.
Sanitiser chosen from the declared type
string gets sanitize_text_field, integer absint, number floatval, boolean rest_sanitize_boolean, and array gets a generated closure that maps sanitize_text_field across the values. The sanitiser runs on every save, including writes that come in over REST.
REST schemas stubbed for arrays and objects
A scalar key is exposed with show_in_rest => true. An array or object key gets the array form with a schema block already in place, because the REST API rejects writes to a structured key that has no schema. The Checks tab reminds you to fill the item or property types in.
The single-versus-REST trap flagged
A key exposed to REST without single => true returns an array of every stored row through the API, which block code almost never expects. That is a warning with a Make it single fix.
Defaults validated against their type
An integer key with a non-numeric default is an error. A boolean with a default of anything other than 1, 0, true or false is a warning. Bad defaults are silent at runtime and painful to trace.
Accessors and uninstall cleanup
Optional helper functions wrap each key so templates stop passing string literals to get_post_meta(), and an uninstall.php block with the matching delete_post_meta_by_key() calls is generated so the postmeta table is left clean when the plugin is deleted.
How does the register_post_meta generator work?
Scope the keys, declare them, pick who may write them, then export.
- 01
Set the scope
Choose the post type the keys belong to and the prefix applied to each key. Meta keys are global across the site, so an unprefixed key will eventually meet a plugin using the same name — the Checks tab says so.
- 02
Declare each key
Add a key name, a type from string, integer, number, boolean, array or object, whether it is single, a default value, a description, and whether it appears in REST. The description becomes the field description in the REST schema, which is the only documentation an API consumer ever sees.
- 03
Choose the authorisation level
Pick
edit_postfor a per-post check,edit_postsfor a site-wide one, ormanage_optionsfor admin-only settings-style meta. The generator warns thatedit_postslets any contributor write these keys on a post they do not own, through the API. - 04
Add the extras, then export
Turn on helper accessors and the uninstall cleanup, work through the Checks tab, then copy the snippet or download it as a plugin file.
Worked example — a single integer reading-time key on posts
One key, prefixed, typed as an integer, exposed to REST with a per-post auth check. This is the Snippet output verbatim.
/**
* Who may read and write these keys through the API.
*
* @param bool $allowed Whether the user can act.
* @param string $meta_key The key.
* @param int $post_id The post.
* @return bool
*/
function acme_meta_auth( $allowed, $meta_key, $post_id ) {
return current_user_can( 'edit_post', $post_id );
}
/**
* Register the meta keys.
*/
function acme_register_meta() {
register_post_meta(
'post',
'acme_reading_time',
array(
'type' => 'integer',
'single' => true,
'description' => __( 'Estimated reading time in minutes.', 'acme' ),
'default' => 0,
'sanitize_callback' => 'absint',
'auth_callback' => 'acme_meta_auth',
'show_in_rest' => true,
)
);
}
add_action( 'init', 'acme_register_meta' );Registration has to happen on init, before the REST API builds its routes. Add more keys and each one becomes another register_post_meta() call inside the same function, all sharing the one auth_callback.
Registered post meta — frequently asked questions
What developers ask once meta needs to reach the block editor or the REST API.
What does register_post_meta() do that update_post_meta() does not?
It describes the key rather than storing a value. Registration attaches a type, a sanitiser that runs on every write, a default, a REST schema and an authorisation callback. update_post_meta() still does the writing; registration is what makes the key visible to the REST API and safe to write from anywhere.
Why is my custom field missing from the REST API response?
Three usual causes. The key was never registered with show_in_rest. The post type itself has show_in_rest => false, in which case there is no route at all. Or the key is registered for a different post type — register_post_meta() scopes to the type you pass, and an empty string registers it for every type instead.
Why does the REST API reject writes to my array meta key?
Because a non-scalar key needs an explicit schema. Pass show_in_rest as an array containing a schema key that declares type => array and an items definition (or type => object with properties). Without it the REST controller has nothing to validate against and refuses the write. The generator stubs the correct shape for you.
Do I have to prefix meta keys?
They are global, so yes in practice. Every plugin and theme on the site writes into the same postmeta table, and a generic key like subtitle or featured will eventually be claimed by something else. A short project prefix costs nothing; the generator warns when the prefix is missing.
What is the auth_callback for and what happens if I leave it out?
It decides who may read and write the key through the REST API and the meta API. Leave it out and WordPress fills in __return_true for an ordinary key or __return_false for a protected, underscore-prefixed one. Neither default is a considered decision, which is why this generator always writes an explicit callback.
Does the default value apply to posts that already exist?
Yes, in the sense that reads return it. The default argument, added in WordPress 5.5, is returned by get_post_meta() whenever no row exists for that post — including for posts created before the key was registered. It does not write anything to the database; the row still only appears when a value is saved.
Where do I paste the code from the Post Meta generator?
You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.
Can I use the generated code in client work or a commercial plugin?
Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.
The other Content generators in the library, plus the WordPress tools most often used alongside this one.