diff --git a/__tests__/integration/one-app.spec.js b/__tests__/integration/one-app.spec.js index 16435632..a594725c 100644 --- a/__tests__/integration/one-app.spec.js +++ b/__tests__/integration/one-app.spec.js @@ -1300,6 +1300,27 @@ describe('Tests that can run against either local Docker setup or remote One App sendingData: 'in POSTs', }); }); + test('app passes urlencoded POST data to modules via vitruvius', async () => { + const response = await fetch( + `${appInstanceUrls.fetchUrl}/vitruvius`, + { + ...defaultFetchOpts, + method: 'POST', + headers: { + 'content-type': 'application/x-www-form-urlencoded', + }, + body: 'legacy=application&sendingData=in POSTs', + } + ); + + const pageHtml = await response.text(); + const data = JSON.parse(pageHtml.match(/
([^<]+)<\/pre>/)[1].replace(/"/g, '"'));
+        expect(data).toHaveProperty('req.body');
+        expect(data.req.body).toEqual({
+          legacy: 'application',
+          sendingData: 'in POSTs',
+        });
+      });
 
       describe('routing', () => {
         test('IndexRedirect redirects', async () => {
diff --git a/__tests__/server/ssrServer.spec.js b/__tests__/server/ssrServer.spec.js
index b778f04b..7afba586 100644
--- a/__tests__/server/ssrServer.spec.js
+++ b/__tests__/server/ssrServer.spec.js
@@ -17,7 +17,10 @@
 import request from 'supertest';
 
 jest.mock('express');
-jest.mock('body-parser', () => ({ json: jest.fn(() => (req, res, next) => next()) }));
+jest.mock('body-parser', () => ({
+  json: jest.fn(() => (req, res, next) => next()),
+  urlencoded: jest.fn(() => (req, res, next) => next()),
+}));
 jest.mock('../../src/server/middleware/clientErrorLogger');
 jest.mock('../../src/server/middleware/setAppVersionHeader');
 jest.mock('../../src/server/middleware/addSecurityHeaders');
@@ -109,13 +112,14 @@ describe('ssrServer', () => {
     let addFrameOptionsHeader;
     let addCacheHeaders;
     let json;
+    let urlencoded;
     let forwardedHeaderParser;
     let serviceWorker;
     let webManifest;
     let offline;
 
     function loadServer() {
-      ({ json } = require('body-parser'));
+      ({ json, urlencoded } = require('body-parser'));
       clientErrorLogger = require('../../src/server/middleware/clientErrorLogger').default;
       setAppVersionHeader = require('../../src/server/middleware/setAppVersionHeader').default;
       addSecurityHeaders = require('../../src/server/middleware/addSecurityHeaders').default;
@@ -424,6 +428,25 @@ describe('ssrServer', () => {
             done();
           });
       });
+      it('should configure urlencoded parsing with a maximum limit for render post pre-flight options calls', (done) => {
+        request(loadServer())
+          .options('/route')
+          .end(() => {
+            expect(urlencoded).toBeCalled();
+            expect(json.mock.calls[1][0]).toHaveProperty('limit', '0kb');
+            done();
+          });
+      });
+
+      it('should configure json urlencoded with a maximum limit for render post calls', (done) => {
+        request(loadServer())
+          .post('/route')
+          .end(() => {
+            expect(urlencoded).toBeCalled();
+            expect(json.mock.calls[2][0]).toHaveProperty('limit', '15kb');
+            done();
+          });
+      });
 
       describe('cors for render post calls', () => {
         it('pre-flight OPTIONS should not respond with CORS headers', (done) => {
diff --git a/docs/recipes/Making-An-Api-Call.md b/docs/recipes/Making-An-Api-Call.md
index 9c4a0a17..a03cf117 100644
--- a/docs/recipes/Making-An-Api-Call.md
+++ b/docs/recipes/Making-An-Api-Call.md
@@ -6,4 +6,13 @@
 
 Recipe is forthcoming.
 
+## POST
+To enable post set [`ONE_ENABLE_POST_TO_MODULE_ROUTES`](../api/server/Environment-Variables.md#ONE_ENABLE_POST_TO_MODULE_ROUTES) environment variable.
+Request body must be either a JSON object or FormData of less than 15KB in size and is passed as props to your module.
+
+Supported media types:
+- `application/json`
+- `application/x-www-form-urlencoded`
+
+
 [☝️ Return To Top](#Making-An-Api-Call)
diff --git a/src/server/ssrServer.js b/src/server/ssrServer.js
index 2f6b42e1..f9df9eb3 100644
--- a/src/server/ssrServer.js
+++ b/src/server/ssrServer.js
@@ -23,7 +23,7 @@ import path from 'path';
 import express from 'express';
 import compression from 'compression';
 import cookieParser from 'cookie-parser';
-import { json } from 'body-parser';
+import { json, urlencoded } from 'body-parser';
 import helmet from 'helmet';
 import cors from 'cors';
 
@@ -96,6 +96,7 @@ export function createApp({ enablePostToModuleRoutes = false } = {}) {
       '*',
       addSecurityHeaders,
       json({ limit: '0kb' }), // there should be no body
+      urlencoded({ limit: '0kb' }), // there should be no body
       cors({ origin: false }) // disable CORS
     );
 
@@ -103,6 +104,7 @@ export function createApp({ enablePostToModuleRoutes = false } = {}) {
       '*',
       addSecurityHeaders,
       json({ limit: '15kb' }),
+      urlencoded({ limit: '15kb' }),
       addFrameOptionsHeader,
       createRequestStore(oneApp, { useBodyForBuildingTheInitialState: true }),
       createRequestHtmlFragment(oneApp),