From c6b9bc057f7432c598163c93bbdbe930c14a8456 Mon Sep 17 00:00:00 2001 From: Fadhlaoui Raed Date: Mon, 2 Nov 2026 20:35:30 -0500 Subject: [PATCH] fix: parse application/*+json request bodies as JSON The default JSON parser was registered without a type, so body-parser matched only application/json. Bodies sent with a structured syntax suffix type, such as application/vnd.api+json (required by JSON:API) or application/merge-patch+json, fell through to the raw parser and reached the function as a Buffer. Give the JSON parser its own options with type ['application/json', 'application/*+json']. The CloudEvents parser still runs first for application/cloudevents+json, and the text parser keeps its existing options. Fixes #809 --- src/server.ts | 9 ++++++++- test/integration/http.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index 7710088d1..72f2b6722 100644 --- a/src/server.ts +++ b/src/server.ts @@ -69,6 +69,13 @@ export function getServer( limit: requestLimit, verify: rawBodySaver, }; + // Parse structured syntax suffix types such as application/vnd.api+json as + // JSON too (RFC 6839). application/cloudevents+json is matched by the + // CloudEvents parser first. + const jsonBodySavingOptions = { + ...defaultBodySavingOptions, + type: ['application/json', 'application/*+json'], + }; const cloudEventsBodySavingOptions = { type: 'application/cloudevents+json', limit: requestLimit, @@ -89,7 +96,7 @@ export function getServer( // Apply middleware app.use(bodyParser.json(cloudEventsBodySavingOptions)); - app.use(bodyParser.json(defaultBodySavingOptions)); + app.use(bodyParser.json(jsonBodySavingOptions)); app.use(bodyParser.text(defaultBodySavingOptions)); app.use(bodyParser.urlencoded(urlEncodedOptions)); // The parser will process ALL content types so MUST come last. diff --git a/test/integration/http.ts b/test/integration/http.ts index ec78d4f91..400eaf11e 100644 --- a/test/integration/http.ts +++ b/test/integration/http.ts @@ -117,4 +117,18 @@ describe('HTTP Function', () => { assert.strictEqual(callCount, test.expectedCallCount); }); }); + + ['application/vnd.api+json', 'application/merge-patch+json'].forEach( + contentType => { + it(`parses a ${contentType} body as JSON`, async () => { + const response = await supertest(getTestServer('testHttpFunction')) + .post('/') + .set('Content-Type', contentType) + .send(JSON.stringify({text: 'hello'})); + + assert.deepStrictEqual(response.body, {result: 'hello'}); + assert.strictEqual(response.status, 200); + }); + }, + ); });