12

I'm trying to get the navigation menu from JSON response using WP REST API v2 plugin.

There is no navigation menu plugin extension for the REST API v2, but only for V1.

From codex WordPress Post Types, I learned that the navigation menu is treated as a post type.

From Rest API Doc, this is how we get posts of a type:

GET http://demo.wp-api.org/wp-json/wp/v2/types/<type>

I tried to get it like so:

URL : http://localhost/wptest/wp-json/wp/v2/types/nav_menu_item

I received 403 error.

{"code":"rest_cannot_read_type","message":"Cannot view type.","data":{"status":403}}

the server understood my request but it refused to give the data.

Q: How can I fix this?

  • All of these answers are just terrible. Install this, extend that. It should already be built-in, the community should open an issue on GitHub. – SacWebDeveloper Dec 20 '18 at 1:16
6

There is a navigation menu plugin extension for the REST API v2: https://wordpress.org/plugins/wp-api-menus/

40

Since I don't like it myself when the top answer is "Install plugin X", here is how I solved it:

Menus are currently not available in the WP Rest. So what you need to do is register your own custom endpoint and then just call that route from your application that needs it.

So you would include something like this (in your functions.php, plugin, wherever):

function get_menu() {
    # Change 'menu' to your own navigation slug.
    return wp_get_nav_menu_items('menu');
}

add_action( 'rest_api_init', function () {
        register_rest_route( 'myroutes', '/menu', array(
        'methods' => 'GET',
        'callback' => 'get_menu',
    ) );
} );

For the example above you would access the data from:

http://your-domain.dev/wp-json/myroutes/menu

You can use the above method to create any routes you like to get any type of data that is not available in the WP Rest. Also good if you need to process some data before sending it to your application.

  • 4
    thanks for sharing your workaround, with more than just a plugin link ;-) It would be better though to prefix your function names or use namespace, to avoid possible name collision, as get_menu() is pretty general. – birgire Jul 24 '17 at 21:30
  • Awesome, people doesn't realize that most of the people have already from 30 to 70 plugins already installed. They even have plugins to keep deactivated others! it is crazy. I think i am going to install a plugin to keep this thread. – Ignacio Bustos Jul 9 '18 at 22:49
  • it does only output false – moesphemie Nov 16 '18 at 20:57
0

You need to add 'show_in_rest' => true, while registering post type.

See details here http://v2.wp-api.org/extending/custom-content-types/

0

I don't think a plugin should be used for these kind of tasks. Also hkc's answer is actually not that bad, it only needs some further explanation to make this work with the nav_menu_item post type (the one used for wp navigation menus).

This post type is already registered and thus we need to alter it, this is easily done by hooking into the register_post_type_args filter. This filter allows us to change the arguments for a specific post type. The code below shows just that for the nav_menu_item post type.

add_filter('register_post_type_args', function ($args, $post_type) {
    if ($post_type == 'nav_menu_item' &&
        class_exists('WP_REST_Posts_Controller') &&
        !class_exists('WP_REST_NavMenuItem_Controller')) {

        class WP_REST_NavMenuItem_Controller extends WP_REST_Posts_Controller {
            public function get_items( $request ) {
                $args = wp_parse_args($request, [
                    'order' => 'ASC',
                    'orderby' => 'menu_order',
                ]);

                $output = [];

                if (empty($request['menu'])) {
                    $menus = get_registered_nav_menus();

                    foreach ( $menus as $location => $description ) {
                        $items = wp_get_nav_menu_items($location, $args);
                        $output = array_merge($output, is_array($items) ? $items : []);
                    }
                } else {
                    $items = wp_get_nav_menu_items($request['menu'], $args);
                    $output = array_merge($output, is_array($items) ? $items : []);
                }

                return rest_ensure_response($output);
            }

            public function get_collection_params() {
                $query_params = parent::get_collection_params();
                $query_params['menu'] = [
                    'description' => __( 'The name or also known as theme_location of the menu' ),
                    'type' => 'string',
                ];
                return $query_params;
            }
        }

        // Alter the post type arguments
        $args['show_in_rest'] = true;
        $args['rest_controller_class'] = 'WP_REST_NavMenuItem_Controller';
    }
    return $args;
}, 10, 2);

As you might have noticed from the code above, the code does a little bit more than just showing the post type in the REST. It also alters the default Posts REST controller to show a somewhat similar output in the REST as described in Liren's answer. Although next to that it also does what all post type REST controllers do and thus gives you more control and functionality. Also consider this as a more stable options as it wouldn't conflict with other REST routes and last but not least, it also way more convenient to work with.

0

I agree with @Lirens answer, but menus should be called by ID, not slug. Also, the slash before the menu path is not needed. So it becomes something more like this:

function get_menu() {
    # Change '2' to your own navigation ID.
    return wp_get_nav_menu_items(2);
}

add_action( 'rest_api_init', function () {
    register_rest_route( 'myroutes', 'menu', array(
        'methods' => 'GET',
        'callback' => 'get_menu',
    ) );
} );

Like this it worked for me.

0

@Liren answer works well. However few beginners may not able to adjust the route. Here is the code that works well with WordPress Rest API v2 with minimum modification.

Replace your menu name only in wp_get_nav_menu_items() function. If menu name and slug does not work (Return false), use Menu ID (visible in Dashboard while editing that Menu).

function get_my_menu() {
    // Replace your menu name, slug or ID carefully
    return wp_get_nav_menu_items('Main Navigation');
}

add_action( 'rest_api_init', function () {
    register_rest_route( 'wp/v2', 'menu', array(
        'methods' => 'GET',
        'callback' => 'get_my_menu',
    ) );
} );

Route URL:

https://website.com/wp-json/wp/v2/menu

More details covered in Tutorial: WordPress Rest API – Get Navigational Menu Items

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.