PHPackages                             brocode/module-webapi-yaml - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. brocode/module-webapi-yaml

ActiveMagento2-module[Parsing &amp; Serialization](/categories/parsing)

brocode/module-webapi-yaml
==========================

Registers a YAML renderer and deserializer for the Magento 2 REST API alongside the built-in JSON and XML formats, via content negotiation (Accept / Content-Type) - no core edits.

1.0.0(today)00MITPHPPHP ~8.1.0||~8.2.0||~8.3.0||~8.4.0

Since Aug 24Pushed todayCompare

[ Source](https://github.com/brosenberger/module-webapi-yaml)[ Packagist](https://packagist.org/packages/brocode/module-webapi-yaml)[ Docs](https://github.com/brosenberger/module-webapi-yaml)[ Fund](https://www.buymeacoffee.com/brosenberger)[ RSS](/packages/brocode-module-webapi-yaml/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (3)Versions (2)Used By (0)

BroCode\_WebapiYaml
===================

[](#brocode_webapiyaml)

Registers a YAML renderer and deserializer for the Magento 2 REST API alongside the built-in JSON and XML formats, via the same content-negotiation mechanism core already uses (`Accept` / `Content-Type` headers) — no core edits, no override.

```
composer require brocode/module-webapi-yaml
bin/magento module:enable BroCode_WebapiYaml
bin/magento cache:flush
bin/magento setup:di:compile
```

Built as the reference implementation for [a companion article on brocode.at](https://brocode.at/blog/magento-rest-api-yaml/) about Magento's REST content-negotiation mechanism in general — this module is the "and here's how you'd add your own format" half of that article.

What this is
------------

[](#what-this-is)

- `GET` any REST endpoint with `Accept: application/yaml` and get a YAML response instead of JSON.
- `POST`/`PUT` any endpoint with `Content-Type: application/yaml` and a YAML body, and it decodes exactly like a JSON or XML body would.
- Two small classes (`Model/Webapi/Rest/Response/Renderer/Yaml.php`, `Model/Webapi/Rest/Request/Deserializer/Yaml.php`), each under 90 lines, wired via one `etc/di.xml`.

The gotcha this module exists to document
-----------------------------------------

[](#the-gotcha-this-module-exists-to-document)

Adding a **response** format is genuinely as simple as it sounds: implement `RendererInterface`, add one `` to `RendererFactory`'s `renders`argument. That argument's base declaration lives in a normal module di.xml (`vendor/magento/module-webapi/etc/di.xml`), and Magento's DI array-merge unions a module's additive `` with core's existing ones cleanly.

Adding a **request** format is not the same shape of easy. `DeserializerFactory`'s `deserializers` argument is declared in the *root* `app/etc/di.xml`, not a module di.xml — and that file does not participate in the same merge-by-item-name behavior. Confirmed live, via reflection on the compiled factory objects with this module enabled:

```
RendererFactory->_renders     → 6 entries (default, json, xml×3, yaml)   ✅ merged
DeserializerFactory->_deserializers → 1 entry (yaml only)                ❌ replaced

```

A module that adds only its own `application_yaml` entry silently **deletes**core's `application_json`, `application_xml`, `text_xml`, and `application_xhtml_xml` entries from the runtime array. Every existing REST consumer's `POST`/`PUT` with a JSON or XML body starts failing with:

```
{"message":"Server cannot understand Content-Type HTTP header media type application/json","trace":null}
```

— on every route, site-wide, the moment this module is enabled, until it also redeclares those four entries. `etc/di.xml` in this repo does exactly that; `Test/Unit/Etc/DiConfigTest.php` is a regression guard against someone "simplifying" it back down to just the new entry.

The bulk API's XML quirk
------------------------

[](#the-bulk-apis-xml-quirk)

Not this module's problem to solve (this module only registers YAML), but worth documenting since it was verified alongside this module and is the kind of thing nobody finds until it costs an afternoon: Magento's core XML parser (`Magento\Framework\Xml\Parser::_xmlToArray()`) collapses **repeated sibling elements with the same tag name** into one array key rather than a positional list. For a normal single-entity request body that's irrelevant. For a **bulk** request body (`/async/bulk/V1/...`), which needs N distinct entities in one payload, naive `......` XML does not parse into two items — it fails outright.

Live-verified against `POST /async/bulk/V1/customers/isEmailAvailable` on a Magento 2.4.8-p5 instance:

```
# JSON baseline — 2 items accepted
curl -X POST ".../rest/async/bulk/V1/customers/isEmailAvailable" \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  --data-raw '[{"customerEmail":"a@example.com"},{"customerEmail":"b@example.com"}]'
# → 202, request_items: [{id:0,status:accepted},{id:1,status:accepted}]

# naive XML — fails, and the error doesn't point at the real problem
curl -X POST ".../rest/async/bulk/V1/customers/isEmailAvailable" \
  -H "Content-Type: application/xml" -H "Authorization: Bearer $TOKEN" \
  --data-raw 'a@example.comb@example.com'
# → 400, {"message":"\"%fieldName\" is required.","parameters":{"fieldName":"customerEmail"}}

# XML with uniquely-named items — works
curl -X POST ".../rest/async/bulk/V1/customers/isEmailAvailable" \
  -H "Content-Type: application/xml" -H "Authorization: Bearer $TOKEN" \
  --data-raw 'a@example.comb@example.com'
# → 202, request_items: [{id:0,status:accepted},{id:0,status:accepted}]
```

Two things worth flagging about that last response: it works (both items are genuinely accepted, confirmed 202 with `errors: false`), but the reported `id` field is `0` for *both* items rather than `0`/`1` — unlike the JSON baseline. If a caller correlates async results back to input items by that `id`, the XML path doesn't give it the same guarantee JSON does. Not independently root-caused in this pass; flagged here rather than glossed over.

Runnable copies of all three request bodies above — re-verified live, not just pasted from a terminal history — are in [`samples/`](samples/), along with the exact curl invocations.

Why this is a narrow capability, not a recommendation
-----------------------------------------------------

[](#why-this-is-a-narrow-capability-not-a-recommendation)

Real-world demand for a custom REST wire format is almost entirely XML — SAP/legacy-ERP integrations that already speak XML natively, which core already covers. YAML earns its place here as a clean worked example (real, typed, no character-restriction baggage compared to XML — see `Test/Unit/Model/Webapi/Rest/Response/Renderer/YamlTest.php`'s `code: 'NO'`-vs-YAML-1.1-boolean-literal test for a concrete case where that matters), not as a suggestion that Magento merchants need YAML support.

Verification
------------

[](#verification)

`composer install && vendor/bin/phpunit` — no Magento install needed. Covers:

- `DiConfigTest`: asserts `etc/di.xml` still redeclares all four core deserializer entries alongside the new one (the regression this module exists to prevent), and that the renderer side stays additive-only.
- `Renderer/YamlTest`: native-type round-trip, the YAML-boolean-literal auto-quoting case, `DataObject` unwrapping.
- `Deserializer/YamlTest`: mapping decode, native-type decode, no root-element unwrap needed, malformed-input and non-string-input error paths.

License
-------

[](#license)

MIT

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9453d161c28a18e817351e00d7ebe81ada31842a249ffb3389830b3483584e60?d=identicon)[brosenberger](/maintainers/brosenberger)

---

Top Contributors

[![brosenberger](https://avatars.githubusercontent.com/u/2969243?v=4)](https://github.com/brosenberger "brosenberger (4 commits)")

---

Tags

yamlmagentoREST APIcontent negotiationmagento2webapibrocode

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/brocode-module-webapi-yaml/health.svg)

```
[![Health](https://phpackages.com/badges/brocode-module-webapi-yaml/health.svg)](https://phpackages.com/packages/brocode-module-webapi-yaml)
```

###  Alternatives

[magewirephp/magewire

A framework that makes building reactive and dynamic interfaces simple in Magento 2

2621.5M47](/packages/magewirephp-magewire)[rcsofttech/audit-trail-bundle

Enterprise-grade, high-performance Symfony audit trail bundle. Automatically track Doctrine entity changes with split-phase architecture, multiple transports (HTTP, Queue, Doctrine), and sensitive data masking.

12017.1k](/packages/rcsofttech-audit-trail-bundle)[run-as-root/magento2-prometheus-exporter

Magento2 Prometheus Exporter

69362.0k](/packages/run-as-root-magento2-prometheus-exporter)[myparcelnl/magento

A Magento 2 module that creates MyParcel labels

1861.2k](/packages/myparcelnl-magento)[opengento/module-category-import-export

This module add the capability to import and export the categories from the back-office.

1312.0k3](/packages/opengento-module-category-import-export)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
