Every application needs to interact with an ecosystem of 3rd party SaaS providers. Implementing a webhook HTTP endpoint in your application allows this ecosystem of external applications to notify you. Your application can then react to those notifications and perform tasks accordingly.
serverless plugin install -n serverless-lift
service: my-app
provider:
name: aws
constructs:
stripe:
type: webhook
authorizer:
handler: myAuthorizer.main
path: /my-webhook-endpoint
method: POST
plugins:
- serverless-lift
Each webhook construct deploys the following resources:
- an API Gateway V2 HTTP API and its $default stage
- an EventBridge EventBus
- an IAM Role allowing API Gateway to use
PutEvents
API of Eventbridge - an API Gateway V2 route
- an API Gateway V2 integration defining mappings of parameters between the HTTP request body and the Eventbridge Event's body
- a custom Lambda authorizer to handle signature verification at API Gateway level
Each webhook construct exposes the following variable:
busName
: the name of the deployed EventBridge bus
This can be used to reference the bus on which notification are published, for example:
constructs:
stripe:
# ...
functions:
myConsumer:
handler: src/stripeConsumer.handler
events:
- eventBridge:
eventBus: ${construct:stripe.busName}
pattern:
source:
# filter all events received on stripe webhook
- stripe
detail-type:
- invoice.paid
How it works: the ${construct:stripe.busName}
variable will automatically be replaced with a CloudFormation reference to the EventBridge bus.
Required
constructs:
stripe:
type: webhook
path: /my-path
The endpoint your webhook should be exposed on. Always starts with a /
.
The final URL for the webhook endpoint will be displayed in the information section when running a serverless deploy
command and will be https://{id}.execute-api.{region}.amazonaws.com{path}
Conditional - depends on insecure
value
constructs:
stripe:
# ...
authorizer:
handler: stripe/authorizer.main
The authorizer
is a Lambda function that checks that webhooks are valid.
Note: the "authorizer" Lambda function is configured inside the webhook construct, instead of being defined in the functions
section.
The only required value is the handler
: this should point to the code that authenticate 3rd party notification. The handler will receive an event from API Gateway using payload format v2. The handler should be written to return the expected simple payload format.
// authorizer.js
export const main = (event, context, callback) => {
callback(null, {
"isAuthorized": true,
});
}
All settings allowed for functions can be used under the authorizer
key. For example:
constructs:
stripe:
# ...
authorizer:
handler: stripe/authorizer.main
environment:
STRIPE_SECRET: my-secret
Lift will automatically configure the function to be triggered by API Gateway. It is not necessary to define events
on the function.
Optional
Defaults to false
.
It is possible to skip writing an authorizer
function by setting insecure: true
.
HTTP requests on the wehbook endpoint will not be validated. This setting is not recommended and SHOULD NOT BE USED IN PRODUCTION to prevent webhook injection as well as Denial of Wallet attacks.
constructs:
stripe:
# ...
insecure: true
Optional
Defaults to Webhook
.
Can either be a dynamic path selector:
constructs:
stripe:
# ...
eventType: $request.body.type
Or a static string:
constructs:
stripe:
# ...
eventType: stripe
Always favor dynamic path selector to ensure the minimum amount of compute is executed downstream. The list of available dynamic selector is available in API Gateway documentation.
Optional
Defaults to POST
This is the HTTP method the webhook will accept. It can be any of the following:
POST
PUT
PATCH
constructs:
stripe:
# ...
method: POST
You can specify an extensions
property on the webhook construct to extend the underlying CloudFormation resources. In the exemple below, the EventBridge Bus CloudFormation resource generated by the stripe
webhook construct will be extended with the new Name: StripeBus
CloudFormation property.
constructs:
stripe:
type: webhook
insecure: true
path: /stripe
extensions:
bus:
Properties:
Name: StripeBus
Extension key | CloudFormation resource | CloudFormation documentation |
---|---|---|
api | AWS::ApiGatewayV2::Api | Link |
bus | AWS::Events::EventBus | Link |
Feel like a common extension pattern should be implemented as part of the construct configuration? Open a GitHub issue.