davidtaing / serverless-webhook-client

Serverless Webhook Client built with Lambda, DynamoDB and SQS
1 stars 0 forks source link

Serverless Webhook Client

Why the topic of webhooks?

I will always find webhooks interesting. It's a meaty problem-space and there's a lot to it.

You build a mailbox (or a few) to receive messages, and you'll have to think about:

Built With

Langauges, Frameworks and Runtimes

Infrastructure

Architecture

Architecture Digram

API

Method Target Notes
POST /api/webhooks/bigcommece Capture webhooks from BigCommerce
POST /api/webhooks/stripe Capture webhooks from Stripe (not yet implemented)
POST /demo/send-webhook Generates a BigCommerce Webhook event and sends it to our API

SQS Queues

Name Notes
Failed Webhooks Queue Rerun failed webhooks. Max two retries before messges get sent to the dead letter queue.
Dead Webhooks Queue Dead Letter Queue. This is where we can notify an operator.

Lambda Functions

Name Trigger Description
capture-webhooks API Gateway Event Capture webhooks and persist to DynamoDB
dynamo-process-webhooks DynamoDB Stream Insert Event Process webhook (first run)
sqs-process-webhooks SQS Message from Failed Webhooks Queue Process webhook (failed runs)

These three lambdas build out the core functionality of the project.

Other Lambda Functions

Name Trigger Description
send-webhooks API Gateway Event Creates and sends a BigCommerce webhook to our API. Built with Golang since different languages may marshall / unmarshall JSON data differently.

These lambdas will are used to demo our features.

DynamoDB

We will be using a Single-Table Design to store our webhooks data. Not too familar with this concept but this is a great opportunity to give this a go.

In a real world situation such as an eCommerce store, I would group webhooks and related data as their own collection of entities. And things like Customers, Orders, maybe Products in another multi-entity 'Single-Table'.

Also, I like the thesis of "Storage is cheap, compute isn't". I think this has some merit.

Entities

Name PK SK Notes
Webhook WH#<WH_ID> WEBHOOK Stores immutable information such as payload & metadata.
Given the immutably, we can avoid additional reads and rely on this information being sent via DynamoDB Streams and SQS.
Webhook Status WH#<WH_ID> STATUS Stores mutable information such status and retries

Example Entities

const webhook = {
  PK: 'WH#57f08d25-a733-453a-be49-8caf04df5169',
  SK: 'WEBHOOK',
  id: '57f08d25-a733-453a-be49-8caf04df5169',
  origin: 'stripe',
  type: 'payment_intent.succeeded',
  created: '2024-05-19T05:27:28+00:00',
  payload: 'json payload',
}

const webhookStatus = {
  PK: 'WH#57f08d25-a733-453a-be49-8caf04df5169',
  SK: 'STATUS',
  id: '57f08d25-a733-453a-be49-8caf04df5169'
  status: 'completed',
  retries: 1,
}

Note: schema is subject to change.

Access Patterns

Access Patterns Target Parameters Notes
Get Webhook Main Table - Webhook ID Get Operation - Given the immutability, we can always use 'eventually' consistent reads
Capture Webhook Main Table - Webhook ID Transaction with Two Operations:
- Put Webhook
- Put Webhook Status
Get Webhook Status Main Table - Webhook ID Get Operation
Set Webhook Status Main Table - Webhook ID Update Operation
Set Webhook to processing Main Table - Webhook ID Update Operation
- set status to processing
- increment retries