diff --git a/.changeset/vast-paws-attack.md b/.changeset/vast-paws-attack.md new file mode 100644 index 000000000..126294007 --- /dev/null +++ b/.changeset/vast-paws-attack.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": patch +--- + +fix: AWS ApiGW v1 (REST) converter incorrectly parses all query parameters as arrays diff --git a/packages/open-next/src/overrides/converters/aws-apigw-v1.ts b/packages/open-next/src/overrides/converters/aws-apigw-v1.ts index 07e909ac5..67b681269 100644 --- a/packages/open-next/src/overrides/converters/aws-apigw-v1.ts +++ b/packages/open-next/src/overrides/converters/aws-apigw-v1.ts @@ -53,6 +53,24 @@ function normalizeAPIGatewayProxyEventQueryParams( return value ? `?${value}` : ""; } +function normalizeAPIGatewayProxyEventMultiValueQueryStringParameters( + event: APIGatewayProxyEvent, +): Record { + const params: Record = {}; + for (const [key, value] of Object.entries( + event.multiValueQueryStringParameters || {}, + )) { + if (value !== undefined && Array.isArray(value)) { + if (value.length === 1) { + params[key] = value[0]; + } else { + params[key] = value; + } + } + } + return params; +} + async function convertFromAPIGatewayProxyEvent( event: APIGatewayProxyEvent, ): Promise { @@ -67,7 +85,7 @@ async function convertFromAPIGatewayProxyEvent( headers, remoteAddress: requestContext.identity.sourceIp, query: removeUndefinedFromQuery( - event.multiValueQueryStringParameters ?? {}, + normalizeAPIGatewayProxyEventMultiValueQueryStringParameters(event), ), cookies: event.multiValueHeaders?.cookie?.reduce( diff --git a/packages/tests-unit/tests/converters/aws-apigw-v1.test.ts b/packages/tests-unit/tests/converters/aws-apigw-v1.test.ts index 74b879d83..45716b853 100644 --- a/packages/tests-unit/tests/converters/aws-apigw-v1.test.ts +++ b/packages/tests-unit/tests/converters/aws-apigw-v1.test.ts @@ -137,7 +137,7 @@ describe("convertFrom", () => { }); }); - it("Should handle queryStringParameters and multiValueQueryStringParameters", async () => { + it("Should handle queryStringParameters and multiValueQueryStringParameters for single value parameters", async () => { const event: APIGatewayProxyEvent = { body: JSON.stringify({ message: "Hello, world!" }), headers: {}, @@ -172,7 +172,48 @@ describe("convertFrom", () => { headers: {}, remoteAddress: "::1", query: { - test: ["test"], + test: "test", + }, + cookies: {}, + }); + }); + + it("Should handle queryStringParameters and multiValueQueryStringParameters for multi value parameters", async () => { + const event: APIGatewayProxyEvent = { + body: JSON.stringify({ message: "Hello, world!" }), + headers: {}, + multiValueHeaders: {}, + httpMethod: "POST", + isBase64Encoded: false, + path: "/", + pathParameters: null, + queryStringParameters: { + test: "testB", + }, + multiValueQueryStringParameters: { + test: ["testA", "testB"], + }, + stageVariables: null, + requestContext: { + identity: { + sourceIp: "::1", + }, + } as any, + resource: "", + }; + + const response = await converter.convertFrom(event); + + expect(response).toEqual({ + type: "core", + method: "POST", + rawPath: "/", + url: "https://on/?test=testA&test=testB", + body: Buffer.from('{"message":"Hello, world!"}'), + headers: {}, + remoteAddress: "::1", + query: { + test: ["testA", "testB"], }, cookies: {}, });