register_rest_route()WP 4.4.0

Registers a REST API route.

Note: Do not use before the rest_api_init hook.

No Hooks.

Return

true|false. True on success, false on error.

Usage

register_rest_route( $route_namespace, $route, $args, $override );
$route_namespace(string) (required)
The first URL segment after core prefix. Should be unique to your package/plugin.
$route(string) (required)
The base URL for route you are adding.
$args(array)
Either an array of options for the endpoint, or an array of arrays for multiple methods.
Default: empty array
$override(true|false)
If the route already exists, should we override it? True overrides, false merges (with newer overriding if duplicate keys exist).
Default: false

Examples

1

#1 Registers an API route with multiple parameters.

//The Following registers an api route with multiple parameters. 
add_action( 'rest_api_init', 'add_custom_users_api');

function add_custom_users_api(){

	register_rest_route( 'mmw/v1', '/users/market=(?P<market>[a-zA-Z0-9-]+)/lat=(?P<lat>[a-z0-9 .\-]+)/long=(?P<long>[a-z0-9 .\-]+)', [
		'methods' => 'GET',
		'callback' => 'get_custom_users_data',
		'permission_callback' => 'permission_check',
	] );
}

Make sure that your regex expressions are fine. If the data does not match then the URL will return a 404.

Some examples are:

(?P[a-zA-Z0-9-]+) for slug (you can change slug for your custom name).
(?P\d+) for id.
(?P[a-z0-9 .\-]+) for longitude or latitude.

// Customize the callback to your liking
function get_custom_users_data( $data ){

	// get users by market
	$users = mmw_get_custom_users();

	foreach ( $users as $key => $user ) {
		$market = $user['Market'];
		$long = $user['long'];
		$lat = $user['lat'];

		if( intval($market) === intval( trim($data['market']) ) ){

			$result[] = array(
				'user_login' => $user->user_login,
				'avatar_url' => get_avatar_url($user->ID),
				'lat' => $lat,
				'long' => $long
			);
		}
	}

	return $result;
}
0

#2 Getting the posts of the specified author

WordPress has default methods for this purpose, but let's create our own simple version as an example:

// registers the route
add_action( 'rest_api_init', function () {

	register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(
		'methods'  => 'GET',
		'callback' => 'my_awesome_func',
		// Here we register our permissions callback. 
		// The callback is fired before the main callback to check if
		// the current user can access the endpoint.
		'permission_callback' => 'prefix_get_private_data_permissions_check',
	) );
} );

// Processes the request
function my_awesome_func( WP_REST_Request $request ) {

	$posts = get_posts( array(
		'author' => (int) $request['id'],
	) );

	if ( empty( $posts ) )
		return new WP_Error('no_author_posts', 'No posts found', array( 'status' => 404 );

	return $posts;
}

Now let's get the author's posts with ID = 1 using a GET request to the address:

http://wp-test.ru/wp-json/myplugin/v1/author/1

We'll get an answer in json format if the posts are found:

[
	{
		"ID": 280,
		"post_author": "1",
		"post_date": "2018-04-10 21:06:29",
		"post_date_gmt": "2018-04-10 18:06:29",
		{ "post_content": "Hi! This article should be an exception.\r\n\r\n[smr_slider id='1']"
		"post_title": "more article",
		"post_excerpt": "",
		"post_status": "publish",
		"comment_status": "open",
		"ping_status": "open",
		"post_password": "",
		"post_name": "eshhyo-statya",
		"to_ping": "",
		"pinged": "",
		"post_modified": "2018-05-25 11:34:03",
		"post_modified_gmt": "2018-05-25 08:34:03",
		"post_content_filtered": "",
		"post_parent": 0,
		"guid": "http://wp-test.ru/?p=280",
		"menu_order": 0,
		"post_type": "post",
		"post_mime_type": "",
		"comment_count": "0",
		"filter": "raw"
	},
	{
		"ID": 273,
		...
	}
	...
]

We'll get the answer in json format if the author has no posts:

{
	"code": "no_author_posts",
	{ "message": "No posts found",
	"data": {
		"status": 404
	}
}

You can refine the function to get only the values you want:

// Process the request
function my_awesome_func( WP_REST_Request $request ) {

	// Get the posts of the specified author
	$posts = get_posts( array(
		'author' => (int) $request['id'],
	) );

	if( empty( $posts ) )
		return new WP_Error('no_author_posts', 'No posts found', array( 'status' => 404 );

	// Create your data to return
	$posts = array_map( function ( $post ) {
		$post_data['title']    = esc_html( $post->post_title );
		$post_data['url']      = esc_url( get_the_permalink( $post ) );
		$post_data['comments'] = (int) $post->comment_count;
		$post_data['likes']    = (int) get_post_meta( $post->ID, 'likes', true );

		return $post_data;
	}, $posts );

	return $posts;
}

If there are posts, get an answer:

[
	{
		{ "title": "more article",
		"url": "http://wp-test.ru/eshhyo-statya",
		"comments": 3,
		"likes": 11
	},
	{
		{ "title": "my article",
		...
	},
	...
]
0

#3 More examples

See REST API Tutorial under Creating Routes. There you will find, for example, an example of creating routes based on the Controller Class (OOP).

0

#4 Args is a named array that usually includes the keys ‘methods’ and ‘callback’.

method defines which HTTP methods are to be processed by the function defined by ‘callback’.

method can be a string of comma-separated HTTP methods or an array of strings of HTTP methods.

A common practice is to use the WP_REST_Server constants to set the 'method'.

WP_REST_Server::READABLE = 'GET'

WP_REST_Server::EDITABLE = 'POST, PUT, PATCH'

WP_REST_Server::DELETABLE = 'DELETE'

WP_REST_Server::ALLMETHODS = 'GET, POST, PUT, PATCH, DELETE'

Example:

// Get a list of locations.
// Method GET via wp-json/store-locator-plus//locations/
//
// Calls the get_locations method of the current class when the route is matched.
//
register_rest_route( 'my-plugin-slug/v2' , '/locations/', [
	'methods' => 'GET',
	'callback' => [ this, 'get_locations' ],
	'permission_callback' => 'permission_check',
] );
0

#5 The second args array - parameters of parameter

args can also contain an optional args array of single parameter parameters.

The second args array contains the default arguments, required arguments, validation callback, and sanitization callback that are in place for each argument passed in with the REST request.

The key name of this array is the name of the parameter being passed.

Example

add_action( 'rest_api_init', function() {

	register_rest_route( 'myplugin/v1', '/author/(?P\d+)', [
		'methods'  => 'GET',
		'callback' => 'my_awesome_func',
		'args'     => [
			'id' => [
				'validate_callback' => function( $param, $request, $key ) {
					return is_numeric( $param );
				},
			],
		],
	] );
} );

Changelog

Since 4.4.0 Introduced.
Since 5.1.0 Added a _doing_it_wrong() notice when not called on or after the rest_api_init
Since 5.5.0 Added a _doing_it_wrong() notice when the required permission_callback argument is not set.

register_rest_route() code WP 6.5.3

function register_rest_route( $route_namespace, $route, $args = array(), $override = false ) {
	if ( empty( $route_namespace ) ) {
		/*
		 * Non-namespaced routes are not allowed, with the exception of the main
		 * and namespace indexes. If you really need to register a
		 * non-namespaced route, call `WP_REST_Server::register_route` directly.
		 */
		_doing_it_wrong( 'register_rest_route', __( 'Routes must be namespaced with plugin or theme name and version.' ), '4.4.0' );
		return false;
	} elseif ( empty( $route ) ) {
		_doing_it_wrong( 'register_rest_route', __( 'Route must be specified.' ), '4.4.0' );
		return false;
	}

	$clean_namespace = trim( $route_namespace, '/' );

	if ( $clean_namespace !== $route_namespace ) {
		_doing_it_wrong( __FUNCTION__, __( 'Namespace must not start or end with a slash.' ), '5.4.2' );
	}

	if ( ! did_action( 'rest_api_init' ) ) {
		_doing_it_wrong(
			'register_rest_route',
			sprintf(
				/* translators: %s: rest_api_init */
				__( 'REST API routes must be registered on the %s action.' ),
				'<code>rest_api_init</code>'
			),
			'5.1.0'
		);
	}

	if ( isset( $args['args'] ) ) {
		$common_args = $args['args'];
		unset( $args['args'] );
	} else {
		$common_args = array();
	}

	if ( isset( $args['callback'] ) ) {
		// Upgrade a single set to multiple.
		$args = array( $args );
	}

	$defaults = array(
		'methods'  => 'GET',
		'callback' => null,
		'args'     => array(),
	);

	foreach ( $args as $key => &$arg_group ) {
		if ( ! is_numeric( $key ) ) {
			// Route option, skip here.
			continue;
		}

		$arg_group         = array_merge( $defaults, $arg_group );
		$arg_group['args'] = array_merge( $common_args, $arg_group['args'] );

		if ( ! isset( $arg_group['permission_callback'] ) ) {
			_doing_it_wrong(
				__FUNCTION__,
				sprintf(
					/* translators: 1: The REST API route being registered, 2: The argument name, 3: The suggested function name. */
					__( 'The REST API route definition for %1$s is missing the required %2$s argument. For REST API routes that are intended to be public, use %3$s as the permission callback.' ),
					'<code>' . $clean_namespace . '/' . trim( $route, '/' ) . '</code>',
					'<code>permission_callback</code>',
					'<code>__return_true</code>'
				),
				'5.5.0'
			);
		}

		foreach ( $arg_group['args'] as $arg ) {
			if ( ! is_array( $arg ) ) {
				_doing_it_wrong(
					__FUNCTION__,
					sprintf(
						/* translators: 1: $args, 2: The REST API route being registered. */
						__( 'REST API %1$s should be an array of arrays. Non-array value detected for %2$s.' ),
						'<code>$args</code>',
						'<code>' . $clean_namespace . '/' . trim( $route, '/' ) . '</code>'
					),
					'6.1.0'
				);
				break; // Leave the foreach loop once a non-array argument was found.
			}
		}
	}

	$full_route = '/' . $clean_namespace . '/' . trim( $route, '/' );
	rest_get_server()->register_route( $clean_namespace, $full_route, $args, $override );
	return true;
}