Ajax API and REST API – WordPress Plugin Development [Part 3]

This is the third part of my WordPress Plugin Development tutorial. Check also the first and second parts.


You can choose between two APIs to implement Ajax on WordPress: Ajax API or REST API.

The Ajax API was the first and only option for many years. It consist in sending a POST request to /wp-admin/admin-ajax.php. The “admin” in the URL is misleading as it can be used either in the admin or in the front-end.

The REST API is more recent. It was launched on version 4.4 and it can be considered a more modern approach for Ajax requests. It also allows custom endpoints.

Some people consider the REST API more complex and difficult to understand than the Ajax API, but it is up to you to choose one over another. In the end, they both do the same.


1. Ajax API:

To implement your Ajax method, use wp_ajax_* (for authenticated access) or wp_ajax_nopriv_* (for unauthenticated access):


/** Ajax route. */
add_action( 'wp_ajax_my_action', 'my_action_callback' );
add_action( 'wp_ajax_nopriv_my_action', 'my_action_callback' );

/** Ajax callback. */
function my_action_callback() {
  /** Dump $_REQUEST as JSON. */
  echo json_encode( $_REQUEST );
  wp_die();
}

Go to the blog main page (ex. http://localhost:3000/), open the browser console (Ctrl+F12) and execute this:


/** Ajax call. */
fetch('/wp-admin/admin-ajax.php', {  
  method: 'POST', 
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },  
  body: 'action=my_action' 
})
.then( response => response.json() )
.then( json => console.log(json) )
.catch( error => console.error( 'error:', error ) );

It will return the dump of the arguments we sent:


Object { action: "my_action" }

Done!


In the PHP code, notice that we assigned the same callback for both authenticated and unauthenticated access. This guarantees that we will always answer to the given action.

If you want to do the same (use the same callback for both states), use a is_user_logged_in() in the callback to differentiate between these states. Alternatively, you can set two callbacks, one for each state.

If you don’t need to respond to unauthenticated users, you can set the “wp_ajax_*” action only.


[SECURITY] Check permissions:

  1. NEVER allow sensitive actions to be executed by unauthenticated users (wp_ajax_nopriv with no permission checking).
  2. ALWAYS check if the user has permissions (ex. is_admin() or current_user_can()) for authenticated actions.

[SECURITY] Nonces:

Nonces prevent hackers from tricking you into clicking on a link that does a destructive action. If you are logged in as an admin user, a permission checking won’t help you if you are tricked to click on a link that deletes all your blog posts.

There are two methods to work with nonces: wp_create_nonce() and check_ajax_referer( $nonce ). The first method creates a random string to be sent by the Ajax call. The second method verifies if the hash is valid.

I personally don’t think it is necessary to use nonces for authenticated users with the Ajax API. Clicking on links won’t cause any harm as they generate a GET request and Ajax API requires a POST. Even if you enter in a shady website that does a POST call to your website (through JS), the CORS protection of your browser will block the request. But that’s my opinion. Use your best judgment to decide.

2. REST API:

Let’s create an AJAX method: GET /my-namespace/v1/something:

Now open it in your browser:

http://localhost:3000/index.php?rest_route=/my-namespace/v1/something (change “localhost:3000” to your actual host)

Done!


The format is: {namespace}/{version}/{resource}. For example, the core WP “users” endpoint is: wp/v2/users.

NEVER add methods to the wp namespace. This namespace is reserved to the WordPress core.

2.1. Pretty URLs:

If your website has pretty permalinks support, then you can call the route directly. Just add a /wp-json prefix:

http://localhost:3000/wp-json/my-namespace/v1/my-method

Use rest_url( $path ) whenever you need to print the URL. This method detects if the installation supports pretty URLs and writes the correct URL:

The $url will be:

  • No pretty URL: http://localhost:3000/index.php?rest_route=/my-namespace/v1/something
  • With pretty URL: http://localhost:3000/wp-json/my-namespace/v1/something

2.2. RESTful structure:

RESTful means to have a resource (ex. “/users”) and allow CRUD operations (create, read, update, delete) on it.

The URL is the same for all CRUD operations. Which operation will be executed depends on the HTTP verb (GET, POST, etc):

  • Index (get all items): GET /users
  • Create an item: POST /users
  • Read an item: GET /users/:id
  • Update an item: PATCH /users/:id or PUT /users/:id or POST /users/:id
  • Delete an item: DELETE /users/:id

POST or PUT? There’s an intense debate of which verb should be used to create an item. Some prefer POST, some prefer PUT. WordPress constants imply that you should use POST to create items, so that’s my preference.

Defining multiple actions:

Let’s say you want to add a POST operation to your “/something”:

In other words, just pass your methods in an array.

2.3. Verb Constants:

Instead of POST/GET/etc, use the API constants:

List of constants:

  • READABLE (GET)
  • CREATABLE (POST)
  • EDITABLE (PUT, PATCH)
  • DELETABLE (DELETE)

2.4. Success:

Alternatively, you can return a WP_REST_Response object. This allows you to customize the headers and other things in the response:

2.5. Error:

2.6. [SECURITY] Restricting access to the endpoint:

Use permission_callback to validate a request:

2.7. Arguments (parameters):

For an id, use the regex (?P<id>\d+):

Adapt the <id> to whatever your parameter is called. Also, change the \d+ if you accept non-numeric values.

2.8. [SECURITY] Validation and Sanitization:

Use args to configure your arguments settings. You don’t need to define them all, just the ones you need custom settings (validation, sanitization, etc):

2.9. [SECURITY] Nonces:

I strongly recommend using Nonces to protect your API, especially for the GET methods.

For nonces, you need to use these 2 methods wp_create_nonce()and check_ajax_referer( $nonce ).

See more information at the beginning of this tutorial.

3. WP_REST_Controller:

Starting on WordPress version 4.7, you can define your REST API by creating a class inherited from the WP_REST_Controller class. Then, implement a register_routes() public method that will define your routes:

Notice that you don’t need the rest_api_init action.

Close Menu