WordPress plugin that extends the REST API for headless CMS architecture. Used to power polo.blue blog built with Astro framework.
This plugin transforms WordPress into a powerful headless CMS by enhancing the REST API with additional endpoints and data fields. It enables building fast static sites with modern frameworks like Astro, Next.js, or Nuxt.
- Pattern: Singleton + Feature Factory
- PHP Version: 8.3+
- WordPress: 5.0+
- Namespace:
Spoko\EnhancedRestAPI
src/
├── Core/
│ ├── Plugin.php # Main orchestrator (singleton)
│ └── Singleton.php # Base singleton class
├── Features/ # Feature modules (14 features)
│ ├── PostFields.php # Extended post data (author, images, read_time)
│ ├── TaxonomyFields.php # Extended taxonomy data
│ ├── MenusEndpoint.php # Navigation menus REST API
│ ├── RelatedPosts.php # Related posts endpoint
│ ├── TableOfContents.php # TOC generation from headings
│ ├── GA4PopularPosts.php # Google Analytics popular posts
│ ├── PolylangSupport.php # Multilingual support (PL/EN)
│ ├── HeadlessMode.php # Redirect frontend to Astro
│ └── ...
├── Services/
│ ├── ErrorLogger.php # Debug logging
│ ├── TranslationCache.php # Object/Redis cache wrapper
│ └── GA4Client.php # Google Analytics 4 API client
└── Helpers/
└── LinkHelper.php # URL utilities
All custom endpoints use the spoko/v1 namespace (following WordPress REST API best practices).
| Endpoint | Description |
|---|---|
GET /wp-json/spoko/v1/navbar/{lang} |
Navigation menu (pl/en) |
GET /wp-json/spoko/v1/menus |
List all menus |
GET /wp-json/spoko/v1/menus/{slug} |
Get menu by slug |
GET /wp-json/spoko/v1/posts/{id}/related |
Related posts by tags/categories |
GET /wp-json/spoko/v1/posts/{id}/toc |
Table of contents |
GET /wp-json/spoko/v1/pages/{id}/toc |
Table of contents for pages |
GET /wp-json/spoko/v1/posts/popular |
GA4 popular posts |
Posts and pages include additional fields:
author_data- Full author object with avatarfeatured_image_urls- All image sizesread_time- Estimated reading timetranslations_data- Polylang translationscategories_data/tags_data- Full taxonomy datarelative_link- Relative URL for frontend routing
The blog supports Polish (PL) and English (EN) languages via Polylang plugin. Menu endpoints and content filtering respect language context.
- Create class in
src/Features/ - Implement one or more methods:
register()- Called on init (global features)registerRestRoutes()- REST route registrationregisterRestFields()- REST field registrationregisterAdmin()- Admin UI (metaboxes, columns)
- Add to
Plugin::initFeatures()array
<?php
declare(strict_types=1);
namespace Spoko\EnhancedRestAPI\Features;
class MyFeature
{
private const REST_NAMESPACE = 'spoko/v1';
public function registerRestRoutes(): void
{
if (!get_option('spoko_rest_myfeature_enabled', true)) {
return;
}
register_rest_route(self::REST_NAMESPACE, '/myendpoint', [
'methods' => 'GET',
'callback' => [$this, 'handleRequest'],
'permission_callback' => '__return_true'
]);
}
}All features can be toggled via WordPress admin panel: SPOKO REST API menu.
Options are stored with prefix spoko_rest_*:
spoko_rest_menus_enabledspoko_rest_menus_navbar_pl/spoko_rest_menus_navbar_enspoko_rest_related_posts_enabledspoko_rest_toc_enabledspoko_rest_headless_mode_enabledspoko_rest_headless_client_url
The Astro frontend at polo.blue fetches data from these endpoints:
// Example: Fetching navigation menu
const response = await fetch(`${WP_API}/spoko/v1/navbar/${lang}`);
const menu = await response.json();
// Example: Fetching related posts
const related = await fetch(`${WP_API}/spoko/v1/posts/${postId}/related`);
// Example: Fetching posts with extended data (native WP endpoint with custom fields)
const posts = await fetch(`${WP_API}/wp/v2/posts?_embed`);- Uses WordPress Object Cache (Redis when available)
- Cache group:
spoko_rest_api - Configurable TTL per feature
- Manual cache clear via admin panel
- All files use
declare(strict_types=1) - Error handling with try-catch and graceful fallbacks
- ErrorLogger writes to
debug.logwhenWP_DEBUGis enabled - No external composer dependencies