universal_entity_api
Universal Entity API is a plug-and-play Drupal module that exposes deeply structured, normalized JSON APIs for all content entities β ideal for decoupled frontends, mobile apps, and content hubs.
Features
Universal Entity API transforms Drupal into a powerful backend for headless and decoupled architectures. It delivers structured, flexible APIs with deep reference resolution, optional rendering, and OpenAPI documentation β all with minimal setup.
β
Supported Entity Types:
This module fully supports all core content entity types out of the box:
π§± Nodes β Articles, pages, and all custom content types
π§© Paragraphs β Nested reusable components
πΌοΈ Media β Images, videos, remote media
π·οΈ Taxonomy Terms β Categories, tags, vocabularies
π§± Custom Blocks β Block content of any bundle
π¦ Menus β Complete menu trees with fields and hierarchy
βοΈ Config Entities β Basic site settings (site name, logo, theme)
π§ Layout Builder (beta) β Section layout, blocks, and nested data (under development)
π Key API Endpoints:
/api/{entity_type}/{bundle}/{id}β Fetch a single entity (node, block, media, term, etc.) with full reference normalization/api/node/{bundle}/listβ Paginated, sortable node list with filters and field pruning/api/block/{bundle}/listβ List all custom blocks of a given bundle/api/menu/{menu_name}?depth=nβ Load full menu tree with fields, weights, and descriptions/api/entity-type/listβ Discover all available entity types and bundles/api/config/basicβ Get basic configuration (site name, logo, theme)
β
Load any entity (node, taxonomy, block, media, etc.) with
/api/{entity_type}/{bundle}/{id} β Works for all core content entities
π Enhancements:
- Deep reference resolution: Paragraphs inside blocks, media inside paragraphs, blocks inside paragraphs
- Field-level filtering via
?fields=title,field_image - Sorting & pagination:
?sort=-created&page=1&limit=10 - OpenAPI Documentation:
- Schema JSON:
/openapi.json - Interactive Explorer:
/api-docs
Post-Installation
After enabling the module, youβre ready to go:
- Visit
/api-docsto explore the endpoints - Test with real content via URLs like
/api/node/article/1or/api/block_content/hero_banner/2 - Access control is handled via Drupal's standard permissions system
- No config screens β just plug and play, or extend via hooks/services
Additional Requirements
- Drupal 10 or 11 Core
- Layout Builder (optional, for layout structure API)
- Paragraphs module (optional, for nested reusable components)
- Media module (optional, for file/image/video embedding)
Recommended Modules/Libraries
- OpenAPI UI β For alternate Swagger rendering
- Subrequests β Enables batch fetching (experimental)
- Decoupled Router β Helpful for resolving paths and aliases
Similar Projects
Project Difference JSON:API Exposes raw entity structure. Limited in-depth resolution and no rendered output. GraphQL Requires schema definition and specialized clients. Complex for simple use cases. REST UI Basic entity output. Lacks nested reference support and field filtering. Decoupled Router Route-focused. Doesnβt expose full entity data.Universal Entity API excels at full-entity normalization for frontend-ready JSON with zero configuration.
Supporting this Module
- β Star on Drupal.org
- π Submit patches or enhancements
- π§ Share documentation and real-world integrations
- π€ Apply for co-maintainership if interested
Community Documentation
- π₯ Live walkthroughs β Coming soon
- πΊ Next.js frontend integration β Planned
- π§° GitLab decoupled starter kit β Coming soon
- π Swagger Explorer:
/api-docs
π§© Layout Builder API (Under Development)
/api/layout/node/{id}β Returns layout sections, components, and inline block data- Recursively normalizes Paragraphs, Media, and nested Blocks
- Includes structured fields + rendered HTML preview per section
- Great for rendering Layout Builder pages in headless environments
Enabling CORS for Frontend Access
To allow your decoupled frontend (e.g. React, Next.js) to access these APIs, add the following CORS config to your settings.php:
$settings['cors.config'] = [
'enabled' => TRUE,
'allowedHeaders' => ['*'],
'allowedMethods' => ['GET', 'POST', 'OPTIONS'],
'allowedOrigins' => ['*'], // Replace * with specific domain(s) in production
'exposedHeaders' => FALSE,
'maxAge' => 1000,
'supportsCredentials' => FALSE,
];π‘ You can restrict allowedOrigins to http://localhost:3000, https://yourfrontend.com, etc., for better security in production.