**Project page:** https://www.drupal.org/project/universal_entity_api

**Branch to review:** 1.1.x

**Clone (non-maintainer):**

git clone --branch 1.1.x https://git.drupalcode.org/project/universal_entity_api.git

### What it does

Universal Entity API exposes Drupal content and configuration entities as
deeply normalized JSON, for decoupled front ends, mobile apps and content
hubs. It resolves references inline so a client consumes one payload rather
than following relationship links.

Endpoints:

- /api/{entity_type}/{bundle}/{id} — any entity, references resolved
- /api/node/{bundle}/list — published nodes, with filtering, sorting,
pagination and field pruning
- /api/block/{bundle}/list — custom blocks, paginated
- /api/menu/{menu_id} — menu tree, filtered to links the caller may access
- /api/entity-type/list — entity type and bundle discovery
- /api/config/basic — public site configuration
- /api/layout/{entity_type}/{id} — Layout Builder sections and component
field values
- /openapi.json and /api-docs — OpenAPI 3.0 schema and Swagger UI

Normalization resolves the media type's configured source field, so image,
document, audio, local video and oEmbed remote video all return usable data.
Taxonomy terms, files, images and paragraphs each have dedicated handling,
and config entities are serialized from their exported configuration.
Reference cycles are cut at a fixed depth.

### How it differs from existing projects

- JSON:API returns raw relationships that the client must follow. This module
resolves references inline and shapes media, taxonomy and file fields into
forms a front end can render directly.
- GraphQL is more flexible but requires schema and client work. This is
plug-and-play with no configuration.
- REST UI exposes entities without recursion or field filtering.
- Decoupled Router resolves routes rather than entities, and is complementary
rather than overlapping.

### Access model

Every endpoint applies Drupal's access system: entity access before
serialization, field access per field, per-item filtering of referenced
entities, and the standard access manipulator on menu trees. Responses carry
cacheability metadata rather than a fixed public max-age.

### Current state

- 2,647 lines of PHP across 18 files in src/, plus 443 lines of tests
- 17 functional tests covering access control and normalization
- PHPCS (Drupal, DrupalPractice) and PHPStan level 5 clean
- Full GitLab CI pipeline green: phpunit, phpcs, phpstan, cspell, eslint,
stylelint, composer, composer-lint
- Verified against Drupal 11.4.6 and Drupal 10.6.16 on PHP 8.3
- No third-party libraries are committed. Swagger UI is loaded from a locally
installed library when present, otherwise from a version-pinned CDN build,
so no Licensing Working Group exception is required.

### Disclosure

I want to be upfront about this rather than have a reviewer find it in the
history. Release 1.0.0 shipped without entity or field access checks:
/api/user/user/{uid} returned account email addresses and password hashes to
anonymous users, and unpublished entities were readable. This was found
during a full audit of the module and fixed in 1.1.0, released 7 September
2026, which enforces access throughout and adds regression tests that fail
against the old code. The project was not covered by the security advisory
policy at the time. There are no open issues tagged security.

### Reviews of other applications

-
-
-

### Background

[a few lines on your Drupal experience]

Comments

kunal_sahu created an issue. See original summary.

kunal_sahu’s picture

Status: Active » Closed (duplicate)

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.