Is there a better way of handling AJAX requests in WordPress?

Use add_rewrite_rule to make your own API and handle the endpoints yourself.

Tools like Monkeyman Rewrite Analyzer will help you understand how your rewrite rules end up working with existing rules.


if ( ! class_exists( 'WPSEAPIEndpoint' ) ):

    class WPSEAPIEndpoint {

        // WordPress hooks
        public function __construct() {
            add_filter( 'query_vars', array ( $this, 'add_query_vars' ), 0 );
            add_action( 'parse_request', array ( $this, 'sniff_requests' ), 0 );
            add_action( 'init', array ( $this, 'add_endpoint' ), 0 );
        }

        // Add public query vars
        public function add_query_vars( $vars ) {
            $vars[] = '___my-api';
            $vars[] = 'action';
            return $vars;
        }

        // Add API Endpoint
        public function add_endpoint() {
            add_rewrite_rule( '^my-api/v1/([^/]*)/?', 'index.php?___my-api=1&action=$matches[1]', 'top' );
            flush_rewrite_rules( false ); //// <---------- REMOVE THIS WHEN DONE TESTING
        }

        // Sniff Requests
        public function sniff_requests( $wp_query ) {
            global $wp;

            if ( isset( $wp->query_vars[ '___my-api' ], $wp->query_vars[ 'action' ] ) ) {
                wp_die( "Action requested: " . $wp->query_vars[ 'action' ] );
            }
        }
    }

    $wptept = new WPSEAPIEndpoint();

endif; // WPSEAPIEndpoint

There are 3 main parts to this; add your custom query vars, add your custom endpoint, and handle the request when you see your custom query vars.

flush_rewrite_rules() really only needs to occur once after you’ve added the new rules but it makes testing much faster to put it inline like I’ve shown. Just remove it when you’re done. Or remove it early and flush the rewrite rules by saving your permalinks in the settings menu.