diff --git a/packages/express-wrapper/src/middleware.ts b/packages/express-wrapper/src/middleware.ts index 947760f7..3d9e7a8b 100644 --- a/packages/express-wrapper/src/middleware.ts +++ b/packages/express-wrapper/src/middleware.ts @@ -189,20 +189,25 @@ export async function runMiddlewareChain< } /** - * Runs a middleware chain, but does not modify the decoded request (input) with properties from any middleware. + * Runs a middleware chain and merges standard Express properties (body, query, params, headers) with the decoded request. + * Does not incorporate other modifications middleware may make to the request object. * This primarily exists to preserve backwards-compatible behavior for RouteHandlers defined without the `routeHandler` function. * - * @param input - the decoded request properties (just passed through) + * Note: The name "IgnoringResults" refers to ignoring typed middleware return values (via middlewareFn), + * NOT ignoring standard Express request modifications. Middleware can still modify req.body/query/params/headers + * and those changes WILL be included in the returned object. + * + * @param _input - the decoded request properties (unused, decoded data is read from req) * @param chain - the middleware chain * @param req - express request object * @param res - express response object - * @returns `input` unmodified + * @returns decoded request merged with req.body, req.query, req.params, and req.headers */ export async function runMiddlewareChainIgnoringResults< Input, Chain extends MiddlewareChain, >( - input: Input, + _input: Input, chain: Chain, req: express.Request, res: express.Response, @@ -216,5 +221,11 @@ export async function runMiddlewareChainIgnoringResults< } }); } - return input; + return { + ...(req as any).decoded, + ...(req.body || {}), + ...(req.query || {}), + ...(req.params || {}), + ...(req.headers || {}), + }; } diff --git a/packages/express-wrapper/test/server.test.ts b/packages/express-wrapper/test/server.test.ts index 03461f2e..8e573f5c 100644 --- a/packages/express-wrapper/test/server.test.ts +++ b/packages/express-wrapper/test/server.test.ts @@ -276,3 +276,66 @@ test('should return a 400 when request fails to decode', async () => { assert(response.body.error.startsWith('Invalid value undefined supplied to')); }); + +test('middleware that modifies req.body should reach handler even without routeHandler()', async () => { + const PostWithData = httpRoute({ + path: '/data', + method: 'POST', + request: httpRequest({ + body: { + originalField: t.string, + addedByMiddleware: optional(t.string), + }, + }), + response: { + 200: t.type({ + originalField: t.string, + addedByMiddleware: optional(t.string), + }), + }, + }); + + const testApiSpec = apiSpec({ + 'test.route': { + post: PostWithData, + }, + }); + + const modifyBodyMiddleware: express.RequestHandler = (req, _res, next) => { + req.body.addedByMiddleware = 'ADDED'; + next(); + }; + + const handler = async (params: { + originalField: string; + addedByMiddleware?: string; + }) => { + return { + type: 200, + payload: { + originalField: params.originalField, + addedByMiddleware: params.addedByMiddleware, + }, + } as const; + }; + + const app = createServer(testApiSpec, (app: express.Application) => { + app.use(express.json()); + return { + 'test.route': { + post: { middleware: [modifyBodyMiddleware], handler }, + }, + }; + }); + + const response = await supertest(app) + .post('/data') + .send({ originalField: 'test' }) + .expect(200); + + assert.equal( + response.body.addedByMiddleware, + 'ADDED', + 'addedByMiddleware should be present because req.body is part of req.decoded', + ); +});