Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

EndPointy Menus

A WordPress plugin that exposes navigation menus via custom REST API endpoints. Perfect for headless WordPress, decoupled applications, and external integrations.

πŸš€ Features

  • βœ… Expose all WordPress menus via REST API
  • βœ… Get menus by ID, slug, or location (e.g., primary, footer)
  • βœ… Nested menu hierarchy support with ?nested=true
  • βœ… Rich item meta with ?meta=true (description, featured image, excerpt)
  • βœ… Field filtering with ?fields=id,title,url for lean payloads
  • βœ… List all registered menu locations
  • βœ… Built-in response caching (auto-flushed when menus change)
  • βœ… Admin settings page (Settings β†’ EndPointy Menus)
  • βœ… Optional API-key authentication + CORS allow-list
  • βœ… Developer filter hook (endpointy_menus_item)
  • βœ… Clean, structured JSON output
  • βœ… Lightweight and performant

πŸ“¦ Installation

  1. Download or clone this repository
  2. Copy the endpointy-menus folder to your WordPress wp-content/plugins directory
  3. Activate the plugin from WP Admin β†’ Plugins
  4. Ensure you have menus configured under Appearance β†’ Menus

πŸ”Œ API Endpoints

Base namespace: endpointy-menus/v1

Get All Menus

GET /wp-json/endpointy-menus/v1/menus

Returns all registered menus with their locations and items.

Example Response:

[
  {
    "id": 2,
    "name": "Main Menu",
    "slug": "main-menu",
    "locations": ["primary"],
    "items": [...]
  }
]

Get Single Menu by ID

GET /wp-json/endpointy-menus/v1/menus/{id}

Returns a specific menu and its items.

Example:

GET /wp-json/endpointy-menus/v1/menus/2

Get Single Menu by Slug

GET /wp-json/endpointy-menus/v1/menus/slug/{slug}

Returns a specific menu and its items by slug.

Example:

GET /wp-json/endpointy-menus/v1/menus/slug/main-menu

Get All Menu Locations

GET /wp-json/endpointy-menus/v1/locations

Returns all registered menu locations with assigned menus.

Example Response:

[
  {
    "location": "primary",
    "description": "Primary Menu",
    "menu_id": 2,
    "menu_name": "Main Menu"
  }
]

Get Menu by Location

GET /wp-json/endpointy-menus/v1/locations/{location}

Returns the menu assigned to a specific location.

Example:

GET /wp-json/endpointy-menus/v1/locations/primary

🌳 Nested Menu Structure

Add ?nested=true to any menu endpoint to get a hierarchical tree structure:

GET /wp-json/endpointy-menus/v1/menus/2?nested=true
GET /wp-json/endpointy-menus/v1/locations/primary?nested=true

Flat structure (default):

{
  "items": [
    {"id": 1, "title": "Home", "parent": 0},
    {"id": 2, "title": "About", "parent": 0},
    {"id": 3, "title": "Team", "parent": 2}
  ]
}

Nested structure (?nested=true):

{
  "items": [
    {
      "id": 1,
      "title": "Home",
      "children": []
    },
    {
      "id": 2,
      "title": "About",
      "children": [
        {
          "id": 3,
          "title": "Team",
          "children": []
        }
      ]
    }
  ]
}

βš™οΈ Query Parameters

All menu endpoints accept these optional parameters (combinable):

Param Example Description
nested ?nested=true Hierarchical tree with children arrays
meta ?meta=true Adds description, attr_title, current, featured_image, excerpt
fields ?fields=id,title,url Return only the listed item fields (children always kept in nested mode)
GET /wp-json/endpointy-menus/v1/menus/2?nested=true&meta=true&fields=id,title,url,featured_image,children

πŸ›‘οΈ Settings, Caching & Auth

A settings page lives at Settings β†’ EndPointy Menus:

  • Response caching β€” menu responses are cached as transients with a configurable lifetime and automatically flushed whenever a menu is edited or deleted.
  • API key β€” turn on Require API key to protect the endpoints. Send the key as an X-API-Key header or an api_key query parameter:
    curl -H "X-API-Key: YOUR_KEY" https://your-site.com/wp-json/endpointy-menus/v1/menus
  • CORS β€” add allowed origins (one per line, or *) to send Access-Control-Allow-Origin headers for browser clients.
  • Rate limiting β€” cap requests per minute per IP (0 = off). Responses include X-RateLimit-Limit / X-RateLimit-Remaining; over-limit requests get 429 with Retry-After.
  • ETag / Last-Modified β€” conditional-request support. Send If-None-Match or If-Modified-Since and unchanged menus return 304 Not Modified:
    curl -H 'If-None-Match: "<etag>"' https://your-site.com/wp-json/endpointy-menus/v1/menus

🧩 Developer Hooks

// Add or modify fields on every menu item.
add_filter( 'endpointy_menus_item', function ( $data, $item, $meta ) {
    $data['acf'] = get_fields( $item );
    return $data;
}, 10, 3 );

πŸ“ Menu Item Properties

Each menu item includes:

Property Type Description
id int Menu item ID
title string Display title
url string Link URL
parent int Parent item ID (0 for top-level)
order int Menu order
type string Item type (post_type, taxonomy, custom, etc.)
object string Object type (page, post, category, etc.)
object_id int ID of the linked object
target string Link target (_blank, etc.)
classes array CSS classes
xfn string XFN relationship
children array Child items (only in nested mode)
description string Item description (only with meta=true)
attr_title string Title attribute (only with meta=true)
current bool Whether the item is the current page (only with meta=true)
featured_image string|null Featured image URL for post-type items (only with meta=true)
excerpt string Post excerpt for post-type items (only with meta=true)

πŸ’‘ Usage Examples

JavaScript (Fetch API)

// Get primary menu
fetch('https://your-site.com/wp-json/endpointy-menus/v1/locations/primary')
  .then(response => response.json())
  .then(menu => console.log(menu));

// Get nested menu structure
fetch('https://your-site.com/wp-json/endpointy-menus/v1/menus/2?nested=true')
  .then(response => response.json())
  .then(menu => console.log(menu));

React Example

import { useEffect, useState } from 'react';

function Navigation() {
  const [menu, setMenu] = useState(null);

  useEffect(() => {
    fetch('https://your-site.com/wp-json/endpointy-menus/v1/locations/primary?nested=true')
      .then(res => res.json())
      .then(data => setMenu(data));
  }, []);

  if (!menu) return <div>Loading...</div>;

  return (
    <nav>
      {menu.items.map(item => (
        <a key={item.id} href={item.url}>{item.title}</a>
      ))}
    </nav>
  );
}

cURL

# Get all menus
curl https://your-site.com/wp-json/endpointy-menus/v1/menus

# Get menu by location
curl https://your-site.com/wp-json/endpointy-menus/v1/locations/primary

# Get nested structure
curl "https://your-site.com/wp-json/endpointy-menus/v1/menus/2?nested=true"

πŸ› οΈ Development

Requirements

  • WordPress 5.0+
  • PHP 7.0+

File Structure

endpointy-menus/
β”œβ”€β”€ endpointy-menus.php  # Main plugin file
β”œβ”€β”€ readme.txt                # WordPress.org readme
└── README.md                 # GitHub readme

πŸ“„ License

GPL v2 or later

πŸ‘¨β€πŸ’» Author

Gunjan Jaswal

β˜• Support

If you find this plugin useful, consider supporting the developer:

Support on Ko-fi

πŸ“œ Changelog

1.2.0

  • Added admin settings page (Settings β†’ EndPointy Menus).
  • Added response caching with configurable lifetime and auto-flush on menu changes.
  • Added optional API-key authentication and configurable CORS allow-list.
  • Added per-IP rate limiting with X-RateLimit-* headers and 429 responses.
  • Added ETag / Last-Modified conditional requests (304 Not Modified).
  • Added /menus/slug/<slug> endpoint.
  • Added meta=true parameter (description, featured image, excerpt, current flag).
  • Added fields= parameter for partial responses.
  • Added endpointy_menus_item developer filter.
  • Added menu count and location menu_slug to responses.

1.1.1

1.1.0

  • Added support for filtering menus by location.
  • Added nested menu hierarchy with nested=true query parameter.
  • Added /locations endpoint to list all menu locations.
  • Added /locations/<location> endpoint to get menu by location.

1.0.0

  • Initial release.

🀝 Contributing

Contributions, issues, and feature requests are welcome! Feel free to check the issues page.

⭐ Show Your Support

Give a ⭐️ if this project helped you!


Made with ❀️ by Gunjan Jaswal

About

This plugin registers custom REST API routes to expose WordPress menus so they can be consumed by external applications.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages