How to create your own REST routes in WordPress
Tags: wordpress, php, javascript
Published: 2021-08-30 21:44:58

---
title: How to create your own REST routes in WordPress
published: true
date: 2021-08-30 10:00:00 UTC
tags: wordpress,php,javascript
canonical_url: https://indexforwp.com/how-to-create-your-own-rest-routes-in-wordpress/
---

WordPress makes registering custom rest routes really easy with a function called `register_rest_route`. This function provides options to create a namespace (which you can also use to version your APIs), all of your route names, an args array to create CRUD operations and check security, and even set your route to override or merge with existing routes.

## Function definition

Let’s review the function parameters and see what we can do:

```php
register_rest_route(
	$namespace, 
	$route, 
	$args, 
	$override
);
```

- **namespace** : a required string which should be the same for all of your routes in the api you are creating
- **route:**  the required string for the singular API endpoint, which would include some of the following methods: GET, POST, PUT, DELETE
- **args:**  optional array of options or array of array of options (we’ll go over this in detail)
- **override** : optional true or false. True will replace an existing route while false will merge it and override any duplicate methods (defaults to false, so you can normally omit it)

## Registering a route

To register a route, we need to call it inside of the `rest_api_init` hook:

```php
add_action('rest_api_init', 'register_your_routes');

function register_your_routes() {
	register_rest_route(
		//...we'll work in here
	);
}
```

The first argument is the namespace. It’s important to note that your namespace should not include a beginning or trailing slash, and should be composed of your namespace and version number (for example, WordPress’s built-in routes use `wp/v2`):

```php
// we'll omit the action hook line from the rest of the examples:
function register_your_routes() {
	register_rest_route(
		'ndx/v1',
		// route name
		// args
	);
}
```

Next, we will create our route name. This will be the last part of the URL we call to access this route. Because this is a URL, you should name it the way you would see a URL (dashes instead of underscores, etc) and avoid characters like `, % [] { } # ( ) * &` and spaces:

```php
function register_your_routes() {
	register_rest_route(
		'ndx/v1',
		'my-endpoint',
		// args
	);
}
```

Finally, we add our args. Because of the scope of the args parameter, we’ll be going over the basic pieces in the next section

## Adding methods, permissions, and a callback function

The most basic use of the args parameter is this:

```php
function register_your_routes() {
	register_rest_route(
		'ndx/v1',
		'my-endpoint',
		array(
			'methods' => 'GET',
			'callback' => 'callback_function' // this can any function name,
		)
	);
}
```

This small amount of code will create a new route with a GET method that calls the `callback_function` function. However, there are a couple of recommendations to make this better:

### Permissions

With newer versions of WordPress, you are asked to set the permissions for your routes. It’s not required but you will get a warning message. To create a public route, just add this line to your args array:

```php
array(
	'methods' => 'GET',
	'callback' => 'callback_function',
	'permission_callback' => '__return_true' // new permissions line
);
```

### Another way to set methods

Because there are multiple methods that seem very similar (POST vs PUT etc), WordPress provides some constants as part of the `WP_REST_Server` class to make your method names more clear:

```markdown
WP_REST_Server::READABLE // methods: GET
WP_REST_Server::EDITABLE // methods: POST, PUT, PATCH
WP_REST_Server::DELETABLE // methods: DELETE
```

You don’t need to instantiate the class so to use these just update the first line of your args statement:

```php
array(
	'methods' => WP_REST_Server::READABLE // was previously 'GET'
	'callback' => 'callback_function',
	'permission_callback' => '__return_true'
);
```

That’s all you need to declare a basic route (minus the callback function of course). Let’s see the code all together:

```php
add_action('rest_api_init', 'register_your_routes');

function register_your_routes() {
	register_rest_route(
		'ndx/v1',
		'my-endpoint',
		array(
			'methods' => WP_REST_Server::READABLE,
			'callback' => 'callback_function',
			'permission_callback' => '__return_true'
		)
	);
}
```

## Creating your callback function

The callback function for your route is a normal PHP function, but it receives a full `$request` object as it’s parameter:

```php
function callback_function($request) {
	// do stuff here
}
```

The `$request` parameter is a `WP_Rest_Request` instance and can contain body data, url parameters, and more. Now, let’s look at how we can return some data.

### Returning data correctly

If you haven’t looked at our post on using `rest_ensure_response` I would give it a glance [here](https://indexforwp.com/function-breakdown-rest_ensure_response/). You can skip to the end to see an example. The function returns your data with a 200 OK response header and any type of data you pass back (strings, arrays, etc). Here is a useless return value for our new route:

```php
function callback_function($request) {
	return rest_ensure_response('hello world!');
}
```

If you are working along with this article, you can test this yourself by adding the following to your base URL: `/wp-json/ndx/v1/my-endpoint`

_Note: if you get a 404, it may be your permalinks. Go to Settings > Permalinks in the dashboard and turn on Pretty Permalinks. The specific style doesn’t matter, any of them will ensure /wp-json works correctly_

If your route is setup correctly, you should see `hello world!` in the browser.

### Testing URL parameters

Now that we can return data, it would be nice to read data sent along with the API. If you’ve ever used URL parameters before, this should be straightforward. Change your callback function to this:

```php
function callback_function($request) {
	$name = $request->get_param('name');

	return rest_ensure_response("hello {$name}!");
}
```

The`get_param` method is available from our `WP_Rest_Response` instance, and can be used to read any URL parameters passed with the route. To test this, add the following to your base URL:

`/wp-json/ndx/v1/my-endpoint?name=YOUR NAME`

You should see “hello YOUR NAME!”

### Basic error handling

If you remove the name parameter from the URL the result looks malformed. We can handle this by checking for the name param in our callback function and returning an error if it’s missing:

```php
function callback_function($request) {
	if(null !== $request->get_param('name')) {
		$name = $request->get_param('name');

		return rest_ensure_response("hello {$name}!");
	} else {
		return new WP_Error('missing_fields', 'please include name as a parameter');
	}
}
```

Note that there is a better way to handle required input, which we’ll cover in our next article about data sanitization, but this is a completely valid way to check for a value. Also, do not try to use `isset` with the `get_param` method, as it already checks for this and returns null if it can’t find the parameter.

## Adding additional methods to your route

Let’s go back to our route registration code:

```php
add_action('rest_api_init', 'register_your_routes');

function register_your_routes() {
	register_rest_route(
		'ndx/v1',
		'my-endpoint',
		array(
			'methods' => WP_REST_Server::READABLE,
			'callback' => 'callback_function',
			'permission_callback' => '__return_true'
		)
	);
}
```

If you wanted to add a POST method for your route, you might think of adding another `register_rest_route` function declaration. That would required duplicating a lot of code with the same values. Instead, let’s change our args array to be an _array of arrays:_

```php
add_action('rest_api_init', 'register_your_routes');

function register_your_routes() {
	register_rest_route(
		'ndx/v1',
		'my-endpoint',
		array(
			array(
				'methods' => WP_REST_Server::READABLE,
				'callback' => 'callback_function',
				'permission_callback' => '__return_true'
			),
			array(
				'methods' => WP_REST_Server::EDITABLE,
				'callback' => 'another_callback_function',
				'permission_callback' => '__return_true'
			)
		)
	);
}
```

Notice how we now have two arrays that are very similar, but the second one’s method is EDITABLE and the callback function is different. This means you’ll need to create another function to handle this endpoint:

```php
function another_callback_function($request) {
	return rest_ensure_response('I show up when you call `my-endpoint` with the POST, PUT, or PATCH method');
}
```

If you add this code and refresh your browser you’ll notice you don’t see this message. That’s because accessing a WordPress API endpoint in the browser is always a GET request. To test other types of requests, you’ll need to use a library or something like Postman. We just posted a thorough tutorial on how to use Postman [here](/using-postman-with-the-wordpress-rest-api).

_Note: generally non-GET requests require authentication, but we are passing in our public permission callback, so you can skip that section of the Postman article and just get set up to play around with it._

Once you get set up in Postman, just make sure you change the method to POST, click the “Send” button and you should see this at the bottom:

![](https://i0.wp.com/indexforwp.com/wp-content/uploads/2021/08/postman.png?resize=1024%2C402&ssl=1)

## Requiring values as part of the route

While URL parameters are a nice and flexible feature to send data to your route, you can also add data as part of the endpoint itself. For example, WordPress lets you access posts by going to `wp-jons/wp/v2/posts` but it also lets you look at a single post by going to `/wp-json/wp/v2/posts/<id>`. You can chain as many parameters as you want, although you will run into issues if some are required and some are not (what if the first parameter is optional, but the second isn’t?) It’s better to send multiple fields as URL parameters or body data.

If you do want to add a parameter as part of the endpoint, you do that to the `$route` argument in your `register_rest_route`function:

```php
add_action('rest_api_init', 'register_your_routes');

function register_your_routes() {
	register_rest_route(
		'ndx/v1',
		'my-endpoint/(?P<id>\d+)', // ADDED ID HERE
		// ...args
	);
}
```

The argument is wrapped in a regular expression, so let’s break it down:

```php
'(?P<id>\\d+)' // the full argument (parathenses are to group it)
'?P' // denotes that this is a parameter
'<id>' // the name of the parameter
'\\d+' // indicates the paramter should be an integer
```

While this is hard to read, it does make sure the parameter is defined and has to be an integer. This change means the following calls are either valid or invalid:

```php
/wp-json/ndx/v1/my-endpoint // not valid (missing the parameter)
/wp-json/ndx/v1/my-endpoint/10 // valid
/wp-json/ndx/v1/my-endpoint/hello // not valid (strings are not allowed)
```

To access the parameter in your callback function, just use `get_param` like before:

```php
$request->get_param('id');
```

What if you wanted to make the parameter completely optional, like how WordPress does for posts? Just make a new route! Here’s the full code with our routes we created before the example above, plus our new one:

```php
add_action('rest_api_init', 'register_your_routes');

function register_your_routes() {
	register_rest_route(
		'ndx/v1',
		'my-endpoint',
		array(
			array(
				'methods' => WP_REST_Server::READABLE,
				'callback' => 'callback_function',
				'permission_callback' => '__return_true'
			),
			array(
				'methods' => WP_REST_Server::EDITABLE,
				'callback' => 'another_callback_function',
				'permission_callback' => '__return_true'
			)
		)
	);

	// our new route
	register_rest_route(
		'ndx/v1',
		'my-endpoint/(?P<id>\d+)',
		array(
			array(
				'methods' => WP_REST_Server::READABLE,
				'callback' => 'callback_function_with_id',
				'permission_callback' => '__return_true'
			)
		)
	);
}
```

## Wrap Up

You are now ready to start creating your own routes! In our next article, we will go over the security features of `register_rest_route` like custom permissions, sanitizing data, and validating passed-in parameters, so you can create routes for real-world use.

**Author**

{% user david_woolf %}