WordPress REST API – custom routes & endpoints


The WordPress REST API is more than just a set of default routes. It is also a tool for creating custom routes and endpoints. In this tutorial, you’ll learn how to create custom routes and endpoints, and test them using Postman.

Learning outcomes

  1. Create a custom table to store form submissions
  2. Register a custom WP REST API route to fetch submissions
  3. Register a custom WP REST API route to post submissions
  4. Register a custom WP REST API endpoint to fetch a single submission

Comprehension questions

  1. In which action hook should your custom routes be created?
  2. What is the function used to register a custom route?
  3. What are the three main arguments you need to pass to this function?
View video transcript

Hey there, and welcome to Learn WordPress. In this tutorial, you’re going to learn how to extend the WordPress REST API. By creating your own routes and endpoints. You will learn how to use the register rest route function to register routes, and how to create specific endpoints by setting the route method. You’ll also learn how it’s possible to specify route arguments, and how to use them to fetch specific data. Let’s consider a requirement to build a simple form submissions plugin, which allows a name and email field to be captured via the WordPress REST API. The plugin should allow for a form submissions REST API route, which has three endpoints. One to fetch all form submissions want to post a form submission to create it and want to fetch a specific form submission. To start because you’re only storing a few simple fields, you might create a custom table to store the form submissions. The plugin code to create the custom table might look something like this. Here in a function hooked into the plug in activation hook, you’re using the global WP DB object and the DB delta function to create a new table in the WordPress database called form submissions. The table has three fields ID name and email. The ID field is set to auto increment, and is the primary key. Once the plugin is activated, this will create the form submissions table in the database with the three fields. To register a custom REST API route, you use the register rest route function. This function requires the following three parameters. The namespace, which is the portion of the root URL that comes before the route itself. The routes, which is the portion of the route URL that comes off of the namespace, and the route arguments, which is an array of arguments that specify things like the route method, and any other arguments that you need to pass to the callback function. Register restaurants should only be called when the REST API NET action is filed. This ensures that the route is only registered when the WordPress REST API is loaded. So in your plugin, you might hook a callback function into the register into the REST API and get hook like this and set up the function to register the routes. Let’s look at what the code would look like to register a custom route to fetch all form submissions. In this routes, the namespace is WP learn form submission slash v one. The route is form submissions, and the route arguments specify the route method as a get method and the callback function as WP learn get form submissions. It also specifies a permission callback function, which is used to check if the user has permission to access this route. In this case, it’s using the built in return true function, which returns true so that anyone can access this route. Once the route is created, you’ll need to create the callback function. In this case, WP learning get form submissions.

This function will be called when the route is requested and should return the form submissions. The code for this function could look something like this.

Here this function uses the global WP DB variable to access the database, and then uses its get results function to fetch all the rows from the form submission table. Based on the SQL query. It returns the results as an array. Because you’ve registered this callback against a WP REST API route using register rest route. The array will automatically be sent in the REST API response as a JSON object. To test this, you can activate the plugin

then check if the form submissions table has been created using your favourite database tool. In this case, I’m using PHP myadmin. Now next, add a few rows of data. So let’s just add one row now. So Jonathan, and jonathan@gmail.com then open a REST API testing tool like postman and create a new request to test the route. So here we have our existing postman workspace for this website, we’ll use the get posts request to create the get form submissions, so we can duplicate that. And let’s rename this to get form sub missions. And then we need to change the URL. So we need to include the namespace and the route. A namespace is learn from submissions, fee one and the route is from submissions.

And if we save and send that request, we can see the response from the database. Now that you can fetch form submissions, you can register a route to create them. To do this, register a new route to use the POST method and specify a callback function which will handle the post request. quickest way to do that is to copy the existing registered route and make a few changes. Shows shown is the method to post and change the callback to something that makes more sense. As you can see the namespace and root name are the same. But the method is set to post and a different callback function is specified. This callback function will be called when the route is accessed using the post method and could look something like this. Start here we can say function Create from submission. And it will receive quest and actual code. Do this the callback function accepts a single parameter which is a WP REST request object and contains all the data that was sent to the root. In this case, the name and email fields will be available in the request object, which can be used to create the form submission. Again, the callback function uses the global WP DB variable in order to access the database, and then uses its insert function to insert a new row into the form submissions table using the name and email fields sent to the root. Notice how this code accesses the name and email properties using an array like syntax. This is because the WP REST request object implements a PHP interface called array access, which allows you to access object properties using the array syntax. Finally, the code returns the number of rows inserted. Note that in this example, there’s no validation of the name and email inputs, which is a security vulnerability. For more information on how to validate inputs, check out the introduction to security developing plugins tutorial at learn wordpress.org. Note also that in this example, the callback function does not check if the fields have been sent in the request, or even if they contain data. For the purposes of this example, let’s assume they will all always have been passed. To test this create a new post request in your API testing tool and submit the name and email data as a JSON object in the request body. So again, we can copy the get form submissions request. Update the name

you can leave the URI as is but changed the method to post and then in the body switcher to raw change the formatting to JSON and then pass in the required fields which in this case are name and email.

When you save and send the requests, you should see the number of rows inserted returned to the response. Notice how you didn’t need to pass any authentication credentials to this route. This is because you set the permission callback to return true on the post request. This means that anyone can currently create POST request to this route and create a new form submission. If you want to restrict access to this route, you can specify a specific permissions callback function. So let’s say something like the repeat learn check permissions. You can then use the current user can function to check if the user has the required permissions. servi specify the callback function, the permissions callback. And re can return it has to return either true or false. So we can say return current current user can and it’s COVID. The Edit posts permission. In this case, this will check for a valid user with permissions edit posts, try to save as by submitting the post regret request using the root again. If we go back to postman, and we try and send this again, you should see an authentication error, which means you don’t have permission to access the route. To fix this, set up an application password for your current user configured in postman under authorization, and try the request again. So let’s get into the admin user and specify an application password.

And then we will copy that password. Switch back to postman, and click on the authorization tab. Select basic auth. The username is admin. And let’s make sure we’ve got the correct password. There it is. And let’s save. And then just to verify the testing, let’s change the values and send it it should work this time and you see the return rows in the response. To verify this, you can also check the database to make sure the data has been saved. So let’s pop on over to the database and browse the form submissions table. And there’s John and Jane. The last requirement for your custom API route was an endpoint that could fetch a single submission. One way you could do this is set up the WP learn get form submissions callback to also accept the request parameter and then check if an ID value is passed in the request body. So you could do something like this, you could say, crest here. And then you could check for Request ID and then pass that ID in the request part. A better solution is to use something called Path variables. You’ve seen REST API variables before in the using the WP REST API tutorial where global variables were discussed. Path variables are similar, but they’re used to pass data to the route. To implement a path variable, you add the PATH variable to the route name when you register the route. For example, if you wanted to implement a path variable for the ID field to fetch a single submission, you could register a route something like this. So let us pop this below our post. And that’s what it would look like. The key thing to note is the format of the PATH variable starts with a question mark and then an uppercase P that it has the ID in greater than or smaller than signs. And then it has a slash d and a plus. The start of the PATH variable is a query string using an uppercase p is the query parameter. The next part is the name, which is the name of the PATH variable. This will be used to create the property on the request object to access the PATH variable value. In this case we’re specifying ID next is a regex pattern, which is a regular expression pattern that the value should match. In this case, the slash d plus means that the value should be a number. This tells the root that a value will be passed to the root and that it should be numeric. This numeric value will then be set up as a property with the name ID on the request object. Now you just need to create the WP learn get form submission function to get the form submission based on the ID which could look something like this. So we will pop this just put it Load the Create Form. There it is directly learn get from submission. As you can see, in the first line of this callback function, it’s expecting the ID to be passed to the route. It then uses that ID to retrieve a specific record from the form submissions table. Because you have set this up as a path variable, you only need to append the ID value at the end of the route URI. When you make the request, the PATH variable will handle getting the value and setting it up inside the ID property of the request object. To test this, create a new GET request in your API testing tool, and add the ID value to the end of the route. So to check this, let’s verify the IDs in our database. So we have ID one, two, and three. Again, the quickest way to create this is to duplicate the good form submissions request. And then make some changes. So we’ll make this the get form submission. By ID, we’ll leave it as a get request, but the root name is form submission. And we can verify that by looking at the root form submission, and then the ID form submission and then the ID. And we can save and sent as Id one. And let’s check if it’ll work for two. And let’s test for we’re not expecting it to be a false, so we should see some kind of error. So the reason we don’t see a response here is because we’re not actually checking in the code. What to do if that record doesn’t exist. You can play around with this a little to test it to create a few more submissions, use the gallery to fetch them and use the Git single route to fetch a single submission by ID. This has been a fairly brief introduction to what you can achieve using register rest route. For more information and examples, check out the extending the REST API chapter of the WordPress REST API handbook@developer.wordpress.org. Specifically, it’s a good idea to read the adding custom endpoints page and the routes and endpoints section. Happy coding

Introduction

Hey there, and welcome to Learn WordPress.

In this tutorial, you’re going to learn how to extend the WP REST API by creating your own routes and endpoints.

You will learn how to use the register_rest_route function to register routes, and how to create specific endpoints by setting the route method. You will also learn how it’s possible to specify route arguments, and how to use them fetch specific data.

Creating a custom table to store form submissions

Let’s consider a requirement to build a simple form submissions plugin, which allows a name and email field to be captured via the WP REST API. The plugin should allow for a form-submissions REST API route, which has three endpoints:

  1. One to fetch all form submissions
  2. One to post a form submission
  3. One to fetch a specific form submission

To start, because you’re only storing a few simple fields, you might create a custom table to store the form submissions.

The plugin code to create this custom table might look something like this:

register_activation_hook( __FILE__, 'wp_learn_setup_table' );
function wp_learn_setup_table() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'form_submissions';

    $sql = "CREATE TABLE $table_name (
      id mediumint(9) NOT NULL AUTO_INCREMENT,
      name varchar (100) NOT NULL,
      email varchar (100) NOT NULL,
      PRIMARY KEY  (id)
    )";

    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );
}

Here, in a function hooked into the plugin activation hook, you’re using the global $wpdb object and the dbDelta function to create a new table in the WordPress database called form_submissions. The table has three fields: id, name, and email. The id field is set to auto-increment, and is the primary key.

Once the plugin is activated, this will create the form_submissions table in the database, with the three fields.

Registering a custom WP REST API route to fetch submissions

To register a custom WP REST API route you can use the register_rest_route function. This function requires the following parameters:

  1. The namespace – this is the portion of the route URL that comes before the route itself. For example, in the route /wp/v2/posts, the namespace is wp/v2.
  2. The route – this is the portion of the route URL that comes after the namespace. For example, in the route /wp/v2/posts, the route is posts.
  3. The route arguments – this is an array of arguments that specify the route method, and any other arguments that you want to pass to the callback function.

register_rest_route should only be called when the rest_api_init action is fired. This ensures that the route is only registered when the WP REST API is loaded.

/**
 * Register the REST API wp-learn-form-submissions-api/v1/form-submission routes
 */
add_action( 'rest_api_init', 'wp_learn_register_routes' );
function wp_learn_register_routes() {
    // Register the routes
}

Let’s look at what the code might look like to register a custom route to fetch all form submissions:

    register_rest_route(
        'wp-learn-form-submissions-api/v1',
        '/form-submissions/',
        array(
            'methods'  => 'GET',
            'callback' => 'wp_learn_get_form_submissions',
            'permission_callback' => '__return_true'
        )
    );
  1. The namespace is wp-learn-form-submissions-api/v1
  2. The route is /form-submissions/
  3. The route arguments specify the route method as GET, and the callback function as wp_learn_get_form_submissions. It also specifies a permission callback function, which is used to check if the user has permission to access the route. In this case, we’re using the built-in __return_true function, which returns true, so anyone can access the route.

Once the route is created, you’ll need to create the callback function, wp_learn_rest_get_form_submissions. This function will be called when the route is requested, and it will return the form submissions.

/**
 * GET callback for the wp-learn-form-submissions-api/v1/form-submission route
 *
 * @return array|object|stdClass[]|null
 */
function wp_learn_get_form_submissions() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'form_submissions';

    $results = $wpdb->get_results( "SELECT * FROM $table_name" );

    return $results;
}

This function uses the global $wpdb variable to access the database, and then uses the get_results function to fetch all the rows from the form_submissions table, based on the sql query. It then returns the results as an array.

Because you’ve registered this callback against a WP REST API route using register_rest_route, the array will automatically be sent in the REST API response as a JSON object.

To test this, activate the plugin.

Then check if the form_submissions table has been created using your favourite database tool.

Next add a few rows of data.

Then open a REST API testing tool like Postman, and create a new request to test the route.

GET https://example.test/wp-json/wp-learn-form-submissions-api/v1/form-submissions

You should see the results returned as a JSON object.

Registering a custom WP REST API route to post submissions

Now that you can fetch form submissions, you can register a route to create them. To do this, you’ll register a new route to use the POST method, and specify a callback function which will handle the POST request.

    /**
     * POST
     */
    register_rest_route(
        'wp-learn-form-submissions-api/v1',
        '/form-submission/',
        array(
            'methods'  => 'POST',
            'callback' => 'wp_learn_create_form_submission',
            'permission_callback' => '__return_true'
        )
    );

As you can see, the namespace and route are the same, but the method is set to POST, and a different callback function is specified. This callback function will be called when the route is accessed using the POST method, and could look like this:

/**
 * POST callback for the wp-learn-form-submissions-api/v1/form-submission route
 *
 * @param $request
 *
 * @return void
 */
function wp_learn_create_form_submission( $request ){
    global $wpdb;
    $table_name = $wpdb->prefix . 'form_submissions';

    $rows = $wpdb->insert(
        $table_name,
        array(
            'name' => $request['name'],
            'email' => $request['email'],
        )
    );

    return $rows;
}

The callback function accepts a single parameter which is a WP_REST_Request object and contains all the data that was sent to the route.

In this case, the name and email fields will be available in the $request object, which can be used to create the form submission.

Again, the callback function uses the global $wpdb variable in order to access the database, and then uses its insert function to insert a new row into the form_submissions table, using the name and email fields sent to the route.

Notice how the code accesses the name and email properties using an array-like syntax. This is because the WP_REST_Request object implements a PHP interface called ArrayAccess, which allows you to access object properties using array syntax.

Finally, it returns the number of rows inserted.

Note that in this example there’s no validation of the name and email inputs, which is a security vulnerability. For more information on how to validate inputs, check out the Introduction to securely developing plugins tutorial at Learn.WordPress.org.

Note also that in this example the callback function does not check if the fields have been sent in the request, or if they contain data. For the purposes of this example, we’ll assume they will always been passed.

To test this, create a new POST request in your API testing tool, and submit the name and email data as a JSON object in the request body:

{
    "name": "John Doe",
    "email": "jon@gmail.com"
}

When you save and send the request you should see the number of rows inserted returned in the response.

Notice how you didn’t need to pass any authentication credentials to this route. This is because you set the permission_callback to __return_true. This means that anyone can currently create a POST request to the route, and create a new form submission.

If you want to restrict access to the route, you can specify a permissions_callback function.

'permission_callback' => 'wp_learn_require_permissions'

Then you can use the current_user_can function to check if the user has the required permissions.

function wp_learn_require_permissions() {
    return current_user_can( 'edit_posts' );
}

In this case this checks for a valid user, with permissions to edit posts.

Try testing this by submitting the POST request using the route.

You should see an authentication error, which means that you don’t have permission to access the route.

Now, set up an Application Password for your current user, configure it in Postman under Authorization, and try the request again.

It should work this time, and you’ll see the number of rows inserted in the response. To verify this, check the form_submissions table in your database, to make sure the data has been inserted.

Creating a custom WP REST API endpoint to fetch a single submission

The last requirement for your custom API route was an endpoint that could fetch a single submission. One way you could do this is to set up the wp_learn_get_form_submissions callback to also accept the $request parameter, and then check if an id value is passed in the request body.

A better solution is to use something called path variables. You’ve seen REST API variables before in the Using the WP REST API tutorial, where global variables were discussed. Path variables are similar, but they’re used to pass data to a route.

To implement a path variable, you add the path variable to the route name when you register the route. For example, if you wanted to implement a path variable for the id field to fetch a single submission, you could create a route like this:

    /**
     * GET single
     */
    register_rest_route(
        'wp-learn-form-submissions-api/v1',
        '/form-submission/(?P<id>\d+)',
        array(
            'methods'  => 'GET',
            'callback' => 'wp_learn_rest_get_form_submission',
            'permission_callback' => '__return_true'
        )
    );

They key thing to not is the format of the path variable:

?P<{name}>{regex-pattern}

  1. The start of the path variable is a query string using an upper case P as the query parameter
  2. name is the name of the placeholder. This will be used to create the property on the $request object, to access the path variable value
  3. regex-pattern is the regular expression pattern that the value should match. In this case, \d+ means that the value should be a number.

This tells the route that a value will be passed to the route and that it should be a numeric. This numeric value will then set up as a property with the name ‘id’ on the request object.

Now you just need to create the wp_learn_get_form_submission function, to get the form_submission based on the id, which could look something like this:

function wp_learn_rest_get_form_submission( $request ) {
    $id = $request['id'];
    global $wpdb;
    $table_name = $wpdb->prefix . 'form_submissions';

    $results = $wpdb->get_results( "SELECT * FROM $table_name WHERE id = $id" );

    return $results[0];
}

As you can see in the first line of this callback function, it’s expecting the id to be passed in the route.

It then uses that id to retrieve the specific record from the form_submissions table

Because you have set up this path variable, you only need to append the id value at the end of the route URI when you make the request. The path variable will handle getting the value, and setting it up inside the id property on the request object.

To test this, create a new GET request in your API testing tool, and add the id value to the end of the route.

https://example.test/wp-json/wp-learn-form-submissions-api/v1/form-submission/1

You should see the results returned as a JSON object.

Play around with this a little to test it, create a few more form submissions, use the GET route to fetch them, and then use the GET single route to fetch a single submission by id.

This has been a fairly brief introduction to what you can achieve using register_rest_route. For more information and examples, check out the Extending the REST API chapter of the WP REST API handbook on developer.wordpress.org, specifically the Adding Custom Endpoints and Routes and Endpoints sections.

Happy coding.

Workshop Details


Presenters

Jonathan Bossenger
@psykro

WordPress Developer Educator at Automattic, full-time sponsored member of the training team creating educational content for developers on Learn WordPress. Husband and father of two energetic boys.