This utility provides data validation and parsing for Standard Schema, together with a collection of built-in Zod schemas and envelopes to parse and unwrap popular AWS event sources payloads.
Key features¶npm install @aws-lambda-powertools/parser zod
Parse events¶
You can parse inbound events using parser
decorator, Middy.js middleware, or manually using built-in envelopes and schemas.
When using the decorator or middleware, you can specify a schema to parse the event, this can be a built-in Zod schema or a custom schema you defined. Custom schemas can be defined using Zod or any other Standard Schema compatible library.
Middy.js middleware with Zod schemaMiddy.js middleware with Valibot schemaDecorator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser/middleware';
import middy from '@middy/core';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number().positive(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
export const handler = middy()
.use(parser({ schema: orderSchema }))
.handler(async (event): Promise<void> => {
for (const item of event.items) {
// item is parsed as OrderItem
logger.info('Processing item', { item });
}
});
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser/middleware';
import middy from '@middy/core';
import {
array,
number,
object,
optional,
pipe,
string,
toMinValue,
} from 'valibot';
const logger = new Logger();
const orderSchema = object({
id: pipe(number(), toMinValue(0)),
description: string(),
items: array(
object({
id: pipe(number(), toMinValue(0)),
quantity: pipe(number(), toMinValue(1)),
description: string(),
})
),
optionalField: optional(string()),
});
export const handler = middy()
.use(parser({ schema: orderSchema }))
.handler(async (event): Promise<void> => {
for (const item of event.items) {
// item is parsed as OrderItem
logger.info('Processing item', { item });
}
});
Warning
The decorator and middleware will replace the event object with the parsed schema if successful. Be cautious when using multiple decorators that expect an event to have a specific structure, the order of evaluation for decorators is from the inner to the outermost decorator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser';
import type { Context } from 'aws-lambda';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
type Order = z.infer<typeof orderSchema>;
class Lambda implements LambdaInterface {
@parser({ schema: orderSchema })
public async handler(event: Order, _context: Context): Promise<void> {
// event is now typed as Order
for (const item of event.items) {
logger.info('Processing item', { item });
}
}
}
const myFunction = new Lambda();
export const handler = myFunction.handler.bind(myFunction);
Built-in schemas¶
Parser comes with the following built-in Zod schemas:
Looking for other libraries?
The built-in schemas are defined using Zod, if you would like us to support other libraries like valibot please open an issue and we will consider it based on the community's feedback.
Model name Description AlbSchema Lambda Event Source payload for Amazon Application Load Balancer APIGatewayProxyEventSchema Lambda Event Source payload for Amazon API Gateway APIGatewayRequestAuthorizerEventSchema Lambda Event Source payload for Amazon API Gateway Request Authorizer APIGatewayTokenAuthorizerEventSchema Lambda Event Source payload for Amazon API Gateway Token Authorizer APIGatewayProxyEventV2Schema Lambda Event Source payload for Amazon API Gateway v2 payload APIGatewayProxyWebsocketEventSchema Lambda Event Source payload for Amazon API Gateway WebSocket events APIGatewayRequestAuthorizerEventV2Schema Lambda Event Source payload for Amazon API Gateway v2 Authorizer AppSyncResolverSchema Lambda Event Source payload for AWS AppSync GraphQL API resolver AppSyncBatchResolverSchema Lambda Event Source payload for AWS AppSync GraphQL API batch resolver AppSyncEventsPublishSchema Lambda Event Source payload for AWS AppSync Events APIPUBLISH
operation AppSyncEventsSubscribeSchema Lambda Event Source payload for AWS AppSync Events API SUBSCRIBE
operation CloudFormationCustomResourceCreateSchema Lambda Event Source payload for AWS CloudFormation CREATE
operation CloudFormationCustomResourceUpdateSchema Lambda Event Source payload for AWS CloudFormation UPDATE
operation CloudFormationCustomResourceDeleteSchema Lambda Event Source payload for AWS CloudFormation DELETE
operation CloudwatchLogsSchema Lambda Event Source payload for Amazon CloudWatch Logs PreSignupTriggerSchema Lambda Event Source payload for Amazon Cognito Pre Sign-up trigger PostConfirmationTriggerSchema Lambda Event Source payload for Amazon Cognito Post Confirmation trigger PreTokenGenerationTriggerSchema Lambda Event Source payload for Amazon Cognito Pre Token Generation trigger CustomMessageTriggerSchema Lambda Event Source payload for Amazon Cognito Custom Message trigger MigrateUserTriggerSchema Lambda Event Source payload for Amazon Cognito User Migration trigger CustomSMSTriggerSchema Lambda Event Source payload for Amazon Cognito Custom SMS trigger CustomEmailTriggerSchema Lambda Event Source payload for Amazon Cognito Custom Email trigger DefineAuthChallengeTriggerSchema Lambda Event Source payload for Amazon Cognito Define Auth Challenge trigger CreateAuthChallengeTriggerSchema Lambda Event Source payload for Amazon Cognito Create Auth Challenge trigger VerifyAuthChallengeResponseTriggerSchema Lambda Event Source payload for Amazon Cognito Verify Auth Challenge Response trigger PreTokenGenerationTriggerSchemaV1 Lambda Event Source payload for Amazon Cognito Pre Token Generation trigger v1 PreTokenGenerationTriggerSchemaV2AndV3 Lambda Event Source payload for Amazon Cognito Pre Token Generation trigger v2 and v3 DynamoDBStreamSchema Lambda Event Source payload for Amazon DynamoDB Streams EventBridgeSchema Lambda Event Source payload for Amazon EventBridge KafkaMskEventSchema Lambda Event Source payload for AWS MSK payload KafkaSelfManagedEventSchema Lambda Event Source payload for self managed Kafka payload KinesisDataStreamSchema Lambda Event Source payload for Amazon Kinesis Data Streams KinesisFirehoseSchema Lambda Event Source payload for Amazon Kinesis Firehose KinesisDynamoDBStreamSchema Lambda Event Source payload for DynamodbStream record wrapped in Kinesis Data stream KinesisFirehoseSqsSchema Lambda Event Source payload for SQS messages wrapped in Kinesis Firehose records LambdaFunctionUrlSchema Lambda Event Source payload for Lambda Function URL payload S3EventNotificationEventBridgeSchema Lambda Event Source payload for Amazon S3 Event Notification to EventBridge. S3Schema Lambda Event Source payload for Amazon S3 S3ObjectLambdaEvent Lambda Event Source payload for Amazon S3 Object Lambda S3SqsEventNotificationSchema Lambda Event Source payload for S3 event notifications wrapped in SQS event (S3->SQS) SesSchema Lambda Event Source payload for Amazon Simple Email Service SnsSchema Lambda Event Source payload for Amazon Simple Notification Service SqsSchema Lambda Event Source payload for Amazon SQS TransferFamilySchema Lambda Event Source payload for AWS Transfer Family events VpcLatticeSchema Lambda Event Source payload for Amazon VPC Lattice VpcLatticeV2Schema Lambda Event Source payload for Amazon VPC Lattice v2 payload Extend built-in schemas¶
You can extend every built-in schema to include your own schema, and yet have all other known fields parsed along the way.
handler.tsExample payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser';
import { EventBridgeSchema } from '@aws-lambda-powertools/parser/schemas/eventbridge';
import type { Context } from 'aws-lambda';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
const orderEventSchema = EventBridgeSchema.extend({
detail: orderSchema, // (1)!
});
type OrderEvent = z.infer<typeof orderEventSchema>;
class Lambda implements LambdaInterface {
@parser({ schema: orderEventSchema }) // (2)!
public async handler(event: OrderEvent, _context: Context): Promise<void> {
for (const item of event.detail.items) {
// process OrderItem
logger.info('Processing item', { item }); // (3)!
}
}
}
const myFunction = new Lambda();
export const handler = myFunction.handler.bind(myFunction);
EventBridgeSchema
with your own detail schemaparser
decorator or middy middlewareevent
is validated including your custom schema and now available in your handler1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{
"version": "0",
"id": "6a7e8feb-b491-4cf7-a9f1-bf3703467718",
"detail-type": "OrderPurchased",
"source": "OrderService",
"account": "111122223333",
"time": "2020-10-22T18:43:48Z",
"region": "us-west-1",
"resources": ["some_additional"],
"detail": {
"id": 10876546789,
"description": "My order",
"items": [
{
"id": 1015938732,
"quantity": 1,
"description": "item xpto"
}
]
}
}
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 JSONStringifiedALB example payloadAPIGatewayProxyEventV2Schema with JSONStringifiedAPI Gateway HTTP API example payloadSQS Schema with JSONStringifiedSQS example payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import { JSONStringified } from '@aws-lambda-powertools/parser/helpers';
import { AlbSchema } from '@aws-lambda-powertools/parser/schemas/alb';
import { z } from 'zod';
const customSchema = z.object({
name: z.string(),
age: z.number(),
});
const extendedSchema = AlbSchema.extend({
body: JSONStringified(customSchema), // (1)!
});
type _ExtendedAlbEvent = z.infer<typeof extendedSchema>;
AlbSchema
using JSONStringified function to transform your payload1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
{
"requestContext": {
"elb": {
"targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a"
}
},
"httpMethod": "GET",
"path": "/lambda",
"queryStringParameters": {
"query": "1234ABCD"
},
"headers": {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip",
"accept-language": "en-US,en;q=0.9",
"connection": "keep-alive",
"host": "lambda-alb-123578498.us-east-2.elb.amazonaws.com",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
"x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476",
"x-forwarded-for": "72.12.164.125",
"x-forwarded-port": "80",
"x-forwarded-proto": "http",
"x-imforwards": "20"
},
"body": "{\"name\":\"Walter\", \"age\": 50}",
"isBase64Encoded": false
}
1 2 3 4 5 6 7 8 9 10 11 12 13
import { JSONStringified } from '@aws-lambda-powertools/parser/helpers';
import { APIGatewayProxyEventV2Schema } from '@aws-lambda-powertools/parser/schemas/api-gatewayv2';
import { z } from 'zod';
const extendedSchema = APIGatewayProxyEventV2Schema.extend({ // (1)!
body: JSONStringified(
z.object({
name: z.string(),
age: z.number(),
})
),
});
type _ExtendedAPIGatewayEvent = z.infer<typeof extendedSchema>;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
import { JSONStringified } from '@aws-lambda-powertools/parser/helpers';
import {
SqsRecordSchema,
SqsSchema,
} from '@aws-lambda-powertools/parser/schemas/sqs';
import { z } from 'zod';
const customSchema = z.object({
name: z.string(),
age: z.number(),
});
const extendedSchema = SqsSchema.extend({
Records: z.array(
SqsRecordSchema.extend({
body: JSONStringified(customSchema),
})
),
});
type _ExtendedSqsEvent = z.infer<typeof extendedSchema>;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
{
"Records": [
{
"messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
"receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
"body": "{\"name\": \"John Doe\", \"age\": 30}",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1545082649183",
"SenderId": "AIDAIENQZJOLO23YVJ4VO",
"ApproximateFirstReceiveTimestamp": "1545082649185"
},
"messageAttributes": {
"testAttr": {
"stringValue": "100",
"binaryValue": "base64Str",
"dataType": "Number"
}
},
"md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
"awsRegion": "us-east-2"
},
{
"messageId": "2e1424d4-f796-459a-8184-9c92662be6da",
"receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...",
"body": "{\"name\": \"foo\", \"age\": 10}",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1545082650636",
"SenderId": "AIDAIENQZJOLO23YVJ4VO",
"ApproximateFirstReceiveTimestamp": "1545082650649",
"DeadLetterQueueSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue-dead"
},
"messageAttributes": {},
"md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
"awsRegion": "us-east-2"
}
]
}
DynamoDB Stream event parsing¶
If you want to parse a DynamoDB stream event with unmarshalling, you can use the helper function DynamoDBMarshalled
:
DynamoDBStreamSchema with DynamoDBMarshalledDynamoDBStream event payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
import { DynamoDBMarshalled } from '@aws-lambda-powertools/parser/helpers/dynamodb';
import {
DynamoDBStreamChangeRecordBase,
DynamoDBStreamRecord,
DynamoDBStreamSchema,
} from '@aws-lambda-powertools/parser/schemas/dynamodb';
import { z } from 'zod';
const customSchema = z.object({
id: z.string(),
message: z.string(),
});
const extendedSchema = DynamoDBStreamSchema.extend({
Records: z.array(
DynamoDBStreamRecord.extend({
dynamodb: DynamoDBStreamChangeRecordBase.extend({
NewImage: DynamoDBMarshalled(customSchema).optional(),
}),
})
),
});
type _ExtendedDynamoDBStreamEvent = z.infer<typeof extendedSchema>;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
{
"Records": [
{
"eventID": "1",
"eventVersion": "1.0",
"dynamodb": {
"ApproximateCreationDateTime": 1693997155.0,
"Keys": {
"Id": {
"N": "101"
}
},
"NewImage": {
"Message": {
"S": "New item!"
},
"Id": {
"N": "101"
}
},
"StreamViewType": "NEW_AND_OLD_IMAGES",
"SequenceNumber": "111",
"SizeBytes": 26
},
"awsRegion": "us-west-2",
"eventName": "INSERT",
"eventSourceARN": "eventsource_arn",
"eventSource": "aws:dynamodb"
},
{
"eventID": "2",
"eventVersion": "1.0",
"dynamodb": {
"OldImage": {
"Message": {
"S": "New item!"
},
"Id": {
"N": "101"
}
},
"SequenceNumber": "222",
"Keys": {
"Id": {
"N": "101"
}
},
"SizeBytes": 59,
"NewImage": {
"Message": {
"S": "This item has changed"
},
"Id": {
"N": "101"
}
},
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"awsRegion": "us-west-2",
"eventName": "MODIFY",
"eventSourceARN": "source_arn",
"eventSource": "aws:dynamodb"
}
]
}
Envelopes¶
When trying to parse your payload you might encounter the following situations:
This can become difficult quite quickly. Parser simplifies the development through a feature named Envelope. Envelopes can be used via envelope parameter available in middy and decorator. Here's an example of parsing a custom schema in an event coming from EventBridge, where all you want is what's inside the detail key.
Middy.js middlewareDecorator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
import { Logger } from '@aws-lambda-powertools/logger';
import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes/eventbridge';
import { parser } from '@aws-lambda-powertools/parser/middleware';
import middy from '@middy/core';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
export const handler = middy()
.use(parser({ schema: orderSchema, envelope: EventBridgeEnvelope }))
.handler(async (event): Promise<void> => {
for (const item of event.items) {
// item is parsed as OrderItem
logger.info('Processing item', { item });
}
});
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser';
import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes/eventbridge';
import type { Context } from 'aws-lambda';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
type Order = z.infer<typeof orderSchema>;
class Lambda implements LambdaInterface {
@parser({ schema: orderSchema, envelope: EventBridgeEnvelope }) // (1)!
public async handler(event: Order, _context: Context): Promise<void> {
// event is now typed as Order
for (const item of event.items) {
logger.info('Processing item', item); // (2)!
}
}
}
const myFunction = new Lambda();
export const handler = myFunction.handler.bind(myFunction);
eventBridgeEnvelope
to parser
decoratorevent
is parsed and replaced as Order
objectThe envelopes are functions that take an event and the schema to parse, and return the result of the inner schema. Depending on the envelope it can be something simple like extracting a key. We have also complex envelopes that parse the payload from a string, decode base64, uncompress gzip, etc.
Envelopes vs schema extension
Use envelopes if you want to extract only the inner part of an event payload and don't use the information from the Lambda event. Otherwise, extend built-in schema to parse the whole payload and use the metadata from the Lambda event.
Built-in envelopes¶Parser comes with the following built-in Zod envelopes:
Looking for other libraries?
The built-in schemas are defined using Zod, if you would like us to support other libraries like valibot please open an issue and we will consider it based on the community's feedback.
Envelope name Behaviour ApiGatewayEnvelope 1. Parses data usingAPIGatewayProxyEventSchema
.
body
key using your schema and returns it. ApiGatewayV2Envelope 1. Parses data using APIGatewayProxyEventV2Schema
.
body
key using your schema and returns it. CloudWatchEnvelope 1. Parses data using CloudwatchLogsSchema
which will base64 decode and decompress it.
message
key using your schema and return them in a list. DynamoDBStreamEnvelope 1. Parses data using DynamoDBStreamSchema
.
NewImage
and OldImage
keys using your schema.
NewImage
and OldImage
keys EventBridgeEnvelope 1. Parses data using EventBridgeSchema
.
detail
key using your schema and returns it. KafkaEnvelope 1. Parses data using KafkaRecordSchema
.
value
key using your schema and returns it. KinesisEnvelope 1. Parses data using KinesisDataStreamSchema
which will base64 decode it.
Records
key using your schema and returns them in a list. KinesisFirehoseEnvelope 1. Parses data using KinesisFirehoseSchema
which will base64 decode it.
Records
key using your schema and returns them in a list. LambdaFunctionUrlEnvelope 1. Parses data using LambdaFunctionUrlSchema
.
body
key using your schema and returns it. SnsEnvelope 1. Parses data using SnsSchema
.
body
key using your schema and return them in a list. SnsSqsEnvelope 1. Parses data using SqsSchema
.
body
key using SnsNotificationSchema
.
Message
key using your schema and return them in a list. SnsEnvelope 1. Parses data using SqsSchema
.
body
key using your schema and return them in a list. VpcLatticeEnvelope 1. Parses data using VpcLatticeSchema
.
value
key using your schema and returns it. VpcLatticeV2Envelope 1. Parses data using VpcLatticeSchema
.
value
key using your schema and returns it. Safe parsing¶
If you want to parse the event without throwing an error, use the safeParse
option. The handler event
object will be replaced with ParsedResult<Input?, Oputput?>
, for example ParsedResult<SqsEvent, Order>
, where SqsEvent
is the original event and Order
is the parsed schema.
The ParsedResult
object will have success
, data
, or error
and originalEvent
fields, depending on the outcome. If the parsing is successful, the data
field will contain the parsed event, otherwise you can access the error
field and the originalEvent
to handle the error and recover the original event.
Middy.js middlewareDecorator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser/middleware';
import middy from '@middy/core';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
export const handler = middy()
.use(
parser({ schema: orderSchema, safeParse: true }) // (1)!
)
.handler(async (event): Promise<void> => {
if (event.success) {
for (const item of event.data.items) {
logger.info('Processing item', { item }); // (2)!
}
} else {
logger.error('Error parsing event', { event: event.error }); // (3)!
logger.error('Original event', { event: event.originalEvent }); // (4)!
}
});
safeParse
option to parse the event without throwing an errordata
to access the parsed event when successfulerror
to handle the error messageoriginalEvent
to get the original event and recover1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser';
import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes/eventbridge';
import type {
EventBridgeEvent,
ParsedResult,
} from '@aws-lambda-powertools/parser/types';
import type { Context } from 'aws-lambda';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
type Order = z.infer<typeof orderSchema>;
class Lambda implements LambdaInterface {
@parser({
schema: orderSchema,
envelope: EventBridgeEnvelope,
safeParse: true, // (1)!
})
public async handler(
event: ParsedResult<EventBridgeEvent, Order>,
_context: Context
): Promise<void> {
if (event.success) {
for (const item of event.data.items) {
logger.info('Processing item', { item }); // (2)!
}
} else {
logger.error('Failed to parse event', { error: event.error }); // (3)!
logger.error('Original event is ', { original: event.originalEvent }); // (4)!
}
}
}
const myFunction = new Lambda();
export const handler = myFunction.handler.bind(myFunction);
safeParse
option to parse the event without throwing an errordata
to access the parsed event when successfulerror
to handle the error messageoriginalEvent
to get the original event and recoverYou can use built-in envelopes and schemas to parse the incoming events manually, without using middy or decorator.
Manual parseManual safeParse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
import { Logger } from '@aws-lambda-powertools/logger';
import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes/eventbridge';
import { EventBridgeSchema } from '@aws-lambda-powertools/parser/schemas/eventbridge';
import type { EventBridgeEvent } from '@aws-lambda-powertools/parser/types';
import type { Context } from 'aws-lambda';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
type Order = z.infer<typeof orderSchema>;
export const handler = async (
event: EventBridgeEvent,
_context: Context
): Promise<void> => {
const parsedEvent = EventBridgeSchema.parse(event); // (1)!
logger.info('Parsed event', parsedEvent);
const orders: Order = EventBridgeEnvelope.parse(event, orderSchema); // (2)!
logger.info('Parsed orders', orders);
};
EventBridgeSchema
to parse the event, the details
fields will be parsed as a generic record.eventBridgeEnvelope
with a combination of orderSchema
to get Order
object from the details
field.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
import { Logger } from '@aws-lambda-powertools/logger';
import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes/eventbridge';
import { EventBridgeSchema } from '@aws-lambda-powertools/parser/schemas/eventbridge';
import type { EventBridgeEvent } from '@aws-lambda-powertools/parser/types';
import type { Context } from 'aws-lambda';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
export const handler = async (
event: EventBridgeEvent,
_context: Context
): Promise<void> => {
const parsedEvent = EventBridgeSchema.safeParse(event); // (1)!
parsedEvent.success
? logger.info('Event parsed successfully', parsedEvent.data)
: logger.error('Event parsing failed', parsedEvent.error);
const parsedEvenlope = EventBridgeEnvelope.safeParse(event, orderSchema); // (2)!
parsedEvenlope.success
? logger.info('Event envelope parsed successfully')
: logger.error('Event envelope parsing failed', parsedEvenlope.error);
};
safeParse
option to parse the event without throwing an errorsafeParse
is also available for envelopesBecause Parser uses Zod, you can use all the features of Zod to validate your data. For example, you can use refine
to validate a field or a combination of fields:
Custom validation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
import { z } from 'zod';
const orderItemSchema = z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
});
export const orderSchema = z
.object({
id: z.number().positive(),
description: z.string(),
items: z.array(orderItemSchema).refine((items) => items.length > 0, {
message: 'Order must have at least one item', // (1)!
}),
optionalField: z.string().optional(),
})
.refine((order) => order.id > 100 && order.items.length > 100, {
message:
'All orders with more than 100 items must have an id greater than 100', // (2)!
});
Zod provides a lot of other features and customization, see Zod documentation for more details.
Types¶ Schema and Type inference¶Use z.infer
to extract the type of the schema, so you can use types during development and avoid type errors.
Types
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser/middleware';
import middy from '@middy/core';
import type { Context } from 'aws-lambda';
import { z } from 'zod';
const logger = new Logger();
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
type Order = z.infer<typeof orderSchema>; // (1)!
const lambdaHandler = async (
event: Order, // (2)!
_context: Context
): Promise<void> => {
for (const item of event.items) {
// item is parsed as OrderItem
logger.info('Processing item', { item }); // (3)!
}
};
export const handler = middy(lambdaHandler).use(
parser({ schema: orderSchema })
);
z.infer
to extract the type of the schema, also works for nested schemasevent
is of type Order
@types/aws-lambda
¶
The package @types/aws-lambda
is a popular project that contains type definitions for many AWS service event invocations, support for these types is provided on a best effort basis.
We recommend using the types provided by the Parser utility under @aws-powertools/parser/types
when using the built-in schemas and envelopes, as they are inferred directly from the Zod schemas and are more accurate.
If you encounter any type compatibility issues with @types/aws-lambda
, please submit an issue.
When testing your handler with parser decorator you need to use double assertion to bypass TypeScript type checking in your tests. This is useful when you want to test the handler for invalid payloads or when you want to test the error handling. If you are you use middy middleware, you don't need to do this.
handlerDecorator.test.tshandlerDecorator.tsschema.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
import type { Context } from 'aws-lambda';
import { describe, expect, it } from 'vitest';
import { handler } from './decorator.js';
import type { Order } from './schema.js';
describe('Test handler', () => {
it('should parse event successfully', async () => {
const testEvent = {
id: 123,
description: 'test',
items: [
{
id: 1,
quantity: 1,
description: 'item1',
},
],
};
await expect(handler(testEvent, {} as Context)).resolves.toEqual(123);
});
it('should throw error if event is invalid', async () => {
const testEvent = { foo: 'bar' };
await expect(
handler(
testEvent as unknown as Order, // (1)!
{} as Context
)
).rejects.toThrow();
});
});
as unknown as X
to bypass TypeScript type checking in your tests1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser';
import type { Context } from 'aws-lambda';
import { type Order, orderSchema } from './schema.js';
const logger = new Logger();
class Lambda implements LambdaInterface {
@parser({ schema: orderSchema })
public async handler(event: Order, _context: Context): Promise<number> {
logger.info('Processing event', { event });
// ... business logic
return event.id;
}
}
const myFunction = new Lambda();
export const handler = myFunction.handler.bind(myFunction);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import { z } from 'zod';
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
type Order = z.infer<typeof orderSchema>;
export { orderSchema, type Order };
This also works when using safeParse
option.
handlerSafeParse.test.tshandlerSafeParse.tsschema.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
import type {
EventBridgeEvent,
ParsedResult,
} from '@aws-lambda-powertools/parser/types';
import type { Context } from 'aws-lambda';
import { describe, expect, it } from 'vitest';
import { handler } from './safeParseDecorator.js';
import type { Order } from './schema.js';
describe('Test handler', () => {
it('should parse event successfully', async () => {
const testEvent = {
version: '0',
id: '6a7e8feb-b491-4cf7-a9f1-bf3703467718',
'detail-type': 'OrderPurchased',
source: 'OrderService',
account: '111122223333',
time: '2020-10-22T18:43:48Z',
region: 'us-west-1',
resources: ['some_additional'],
detail: {
id: 10876546789,
description: 'My order',
items: [
{
id: 1015938732,
quantity: 1,
description: 'item xpto',
},
],
},
};
await expect(
handler(
testEvent as unknown as ParsedResult<EventBridgeEvent, Order>, // (1)!
{} as Context
)
).resolves.toEqual(10876546789);
});
it('should throw error if event is invalid', async () => {
const testEvent = { foo: 'bar' };
await expect(
handler(
testEvent as unknown as ParsedResult<EventBridgeEvent, Order>,
{} as Context
)
).rejects.toThrow();
});
});
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
import type { LambdaInterface } from '@aws-lambda-powertools/commons/types';
import { Logger } from '@aws-lambda-powertools/logger';
import { parser } from '@aws-lambda-powertools/parser';
import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes/eventbridge';
import type {
EventBridgeEvent,
ParsedResult,
} from '@aws-lambda-powertools/parser/types';
import type { Context } from 'aws-lambda';
import { type Order, orderSchema } from './schema.js';
const logger = new Logger();
class Lambda implements LambdaInterface {
@parser({
schema: orderSchema,
envelope: EventBridgeEnvelope,
safeParse: true,
})
public async handler(
event: ParsedResult<EventBridgeEvent, Order>,
_context: Context
): Promise<number> {
logger.info('Processing event', { event });
if (event.success) {
// ... business logic
return event.data.id;
}
logger.error('Failed to parse event', { event });
throw new Error('Failed to parse event');
}
}
const myFunction = new Lambda();
export const handler = myFunction.handler.bind(myFunction);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import { z } from 'zod';
const orderSchema = z.object({
id: z.number().positive(),
description: z.string(),
items: z.array(
z.object({
id: z.number().positive(),
quantity: z.number(),
description: z.string(),
})
),
optionalField: z.string().optional(),
});
type Order = z.infer<typeof orderSchema>;
export { orderSchema, type Order };
2025-08-14
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4