diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index a9fc26f3d3..10a0a9077b 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -106,13 +106,15 @@ You can extend every built-in schema to include your own schema, and yet have al === "Example payload" ```json - --8<-- "examples/snippets/parser/examplePayload.json" + --8<-- "examples/snippets/parser/samples/examplePayload.json" ``` +### JSON stringified payloads + If you want to extend a schema and transform a JSON stringified payload to an object, you can use helper function `JSONStringified`: === "AlbSchema with JSONStringified" - ```typescript hl_lines="12" + ```typescript hl_lines="11" --8<-- "examples/snippets/parser/extendAlbSchema.ts" ``` @@ -121,20 +123,34 @@ If you want to extend a schema and transform a JSON stringified payload to an ob === "ALB example payload" ```json hl_lines="26" - --8<-- "examples/snippets/parser/exampleAlbPayload.json" + --8<-- "examples/snippets/parser/samples/exampleAlbPayload.json" + ``` + +=== "APIGatewayProxyEventV2Schema with JSONStringified" + ```typescript hl_lines="6" + --8<-- + examples/snippets/parser/extendAPIGatewayProxyEventV2Schema.ts::4 + examples/snippets/parser/extendAPIGatewayProxyEventV2Schema.ts:6: + --8<-- + ``` + + 1. This is compatible also with API Gateway REST API schemas + +=== "API Gateway HTTP API example payload" + + ```json hl_lines="39" + --8<-- "examples/snippets/parser/samples/exampleAPIGatewayProxyEventV2.json" ``` === "SQS Schema with JSONStringified" - ```typescript hl_lines="23-25 30 34" + ```typescript hl_lines="16" --8<-- "examples/snippets/parser/extendSqsSchema.ts" ``` - 1. make sure to set your schema to the correct key in the JSON payload - === "SQS example payload" ```json hl_lines="6 28" - --8<-- "examples/snippets/parser/exampleSqsPayload.json" + --8<-- "examples/snippets/parser/samples/exampleSqsPayload.json" ``` ## Envelopes diff --git a/examples/snippets/parser/extendAPIGatewayProxyEventV2Schema.ts b/examples/snippets/parser/extendAPIGatewayProxyEventV2Schema.ts new file mode 100644 index 0000000000..7282564a0b --- /dev/null +++ b/examples/snippets/parser/extendAPIGatewayProxyEventV2Schema.ts @@ -0,0 +1,14 @@ +import { JSONStringified } from '@aws-lambda-powertools/parser/helpers'; +import { APIGatewayProxyEventV2Schema } from '@aws-lambda-powertools/parser/schemas/api-gatewayv2'; +import { z } from 'zod'; + +// biome-ignore format: we need the comment in the next line to stay there to annotate the code snippet in the docs +const extendedSchema = APIGatewayProxyEventV2Schema.extend({ // (1)! + body: JSONStringified( + z.object({ + name: z.string(), + age: z.number(), + }) + ), +}); +type ExtendedAPIGatewayEvent = z.infer; diff --git a/examples/snippets/parser/extendAlbSchema.ts b/examples/snippets/parser/extendAlbSchema.ts index 8e3a62ce42..ed7c8cf35b 100644 --- a/examples/snippets/parser/extendAlbSchema.ts +++ b/examples/snippets/parser/extendAlbSchema.ts @@ -1,5 +1,5 @@ import { JSONStringified } from '@aws-lambda-powertools/parser/helpers'; -import { AlbSchema } from '@aws-lambda-powertools/parser/schemas'; +import { AlbSchema } from '@aws-lambda-powertools/parser/schemas/alb'; import { z } from 'zod'; const customSchema = z.object({ @@ -8,5 +8,7 @@ const customSchema = z.object({ }); const extendedSchema = AlbSchema.extend({ - body: JSONStringified(customSchema), // (1)! + body: JSONStringified(customSchema), }); + +type ExtendedAlbEvent = z.infer; diff --git a/examples/snippets/parser/extendSqsSchema.ts b/examples/snippets/parser/extendSqsSchema.ts index f9a6ce11ac..0a1d36c072 100644 --- a/examples/snippets/parser/extendSqsSchema.ts +++ b/examples/snippets/parser/extendSqsSchema.ts @@ -2,7 +2,7 @@ import { JSONStringified } from '@aws-lambda-powertools/parser/helpers'; import { SqsRecordSchema, SqsSchema, -} from '@aws-lambda-powertools/parser/schemas'; +} from '@aws-lambda-powertools/parser/schemas/sqs'; import { z } from 'zod'; const customSchema = z.object({ @@ -17,3 +17,5 @@ const extendedSchema = SqsSchema.extend({ }) ), }); + +type ExtendedSqsEvent = z.infer; diff --git a/examples/snippets/parser/samples/exampleAPIGatewayProxyEventV2.json b/examples/snippets/parser/samples/exampleAPIGatewayProxyEventV2.json new file mode 100644 index 0000000000..c3cbd7fc40 --- /dev/null +++ b/examples/snippets/parser/samples/exampleAPIGatewayProxyEventV2.json @@ -0,0 +1,41 @@ +{ + "version": "2.0", + "routeKey": "POST /lambda", + "rawPath": "/lambda", + "rawQueryString": "", + "headers": { + "accept": "*/*", + "accept-encoding": "gzip, deflate", + "authorization": "Bearer foo", + "content-length": "0", + "host": "lsw1ro4ipb.execute-api.eu-west-1.amazonaws.com", + "user-agent": "HTTPie/3.2.2", + "x-amzn-trace-id": "Root=1-66705bc7-2b4257df30cbee696ef2cf28", + "x-forwarded-for": "15.248.3.126", + "x-forwarded-port": "443", + "x-forwarded-proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "apiId": "lsw1ro4ipb", + "authorizer": { + "lambda": null + }, + "domainName": "lsw1ro4ipb.execute-api.eu-west-1.amazonaws.com", + "domainPrefix": "lsw1ro4ipb", + "http": { + "method": "POST", + "path": "/lambda", + "protocol": "HTTP/1.1", + "sourceIp": "15.248.3.126", + "userAgent": "HTTPie/3.2.2" + }, + "requestId": "ZhNHJhhLjoEEPiw=", + "routeKey": "POST /lambda", + "stage": "$default", + "time": "17/Jun/2024:15:52:39 +0000", + "timeEpoch": 1718639559080 + }, + "body": "{\"name\":\"John\",\"age\":42}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/examples/snippets/parser/exampleAlbPayload.json b/examples/snippets/parser/samples/exampleAlbPayload.json similarity index 100% rename from examples/snippets/parser/exampleAlbPayload.json rename to examples/snippets/parser/samples/exampleAlbPayload.json diff --git a/examples/snippets/parser/examplePayload.json b/examples/snippets/parser/samples/examplePayload.json similarity index 100% rename from examples/snippets/parser/examplePayload.json rename to examples/snippets/parser/samples/examplePayload.json diff --git a/examples/snippets/parser/exampleSqsPayload.json b/examples/snippets/parser/samples/exampleSqsPayload.json similarity index 100% rename from examples/snippets/parser/exampleSqsPayload.json rename to examples/snippets/parser/samples/exampleSqsPayload.json diff --git a/packages/parser/src/helpers.ts b/packages/parser/src/helpers.ts index 39cd29a019..10915620d7 100644 --- a/packages/parser/src/helpers.ts +++ b/packages/parser/src/helpers.ts @@ -1,13 +1,30 @@ import { type ZodTypeAny, z } from 'zod'; +/** + * @typedef {import('../schemas/alb').AlbSchema} AlbSchema + */ /** * A helper function to parse a JSON string and validate it against a schema. - * Use it for built-in schemas like `AlbSchema`, `ApiGatewaySchema`, etc. to extend them with your customer schema. + * + * Use it for built-in schemas like `AlbSchema`, `ApiGatewaySchema`, etc. that have some fields that are JSON stringified + * and extend them with your custom schema. + * + * For example, if you have an event with a JSON stringified body similar to the following: + * + * ```json + * { + * // ... other fields + * "body": "{\"name\": \"John\", \"age\": 30}", + * "isBase64Encoded": false, + * } + * ``` + * + * You can extend any built-in schema with your custom schema using the `JSONStringified` helper function. * * @example * ```typescript * import { JSONStringified } from '@aws-lambda-powertools/parser/helpers'; - * import { AlbSchema } from '@aws-lambda-powertools/parser/schemas'; + * import { AlbSchema } from '@aws-lambda-powertools/parser/schemas/alb'; * import { z } from 'zod'; * * const customSchema = z.object({ @@ -19,6 +36,7 @@ import { type ZodTypeAny, z } from 'zod'; * body: JSONStringified(customSchema), * }); * + * type ExtendedAlbEvent = z.infer; * ``` * * @param schema - The schema to validate the JSON string against diff --git a/packages/parser/typedoc.json b/packages/parser/typedoc.json index 5a14ab2b5f..bda7b8332e 100644 --- a/packages/parser/typedoc.json +++ b/packages/parser/typedoc.json @@ -5,7 +5,8 @@ "./src/middleware/parser.ts", "./src/types/index.ts", "./src/envelopes/index.ts", - "./src/schemas/index.ts" + "./src/schemas/index.ts", + "./src/helpers.ts" ], "readme": "README.md" }