npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tayori/lambda

v0.0.1

Published

AWS Lambda adapter for Tayori Webhook Router

Readme

@tayori/lambda

AWS Lambda adapter for Tayori webhook router.

Overview

@tayori/lambda provides a seamless integration between Tayori's type-safe webhook routing and AWS Lambda. Perfect for serverless webhook handlers with API Gateway or Lambda Function URLs.

Installation

npm install @tayori/lambda @tayori/core
# or
pnpm add @tayori/lambda @tayori/core
# or
yarn add @tayori/lambda @tayori/core

Note: @tayori/core is a peer dependency and must be installed separately.

Features

  • Serverless-Ready: Optimized for AWS Lambda cold starts
  • API Gateway Support: Works with both REST API and HTTP API
  • Function URLs: Compatible with Lambda Function URLs
  • Type-Safe: Full TypeScript support with AWS Lambda types
  • Error Handling: Built-in error handling with proper HTTP responses
  • Base64 Decoding: Automatic handling of base64-encoded payloads

Quick Start

With Stripe Webhooks

import Stripe from 'stripe';
import { StripeWebhookRouter, createStripeVerifier } from '@tayori/stripe';
import { lambdaAdapter } from '@tayori/lambda';

const stripe = new Stripe(process.env.STRIPE_API_KEY!);
const router = new StripeWebhookRouter();

router.on('payment_intent.succeeded', async (event) => {
  console.log('Payment succeeded:', event.data.object.id);
  // Update database, send email, etc.
});

router.on('customer.subscription.created', async (event) => {
  console.log('New subscription:', event.data.object.id);
  // Provision access, send welcome email, etc.
});

export const handler = lambdaAdapter(router, {
  verifier: createStripeVerifier(stripe, process.env.STRIPE_WEBHOOK_SECRET!),
});

With Custom Webhooks

import { WebhookRouter, type Verifier, type WebhookEvent } from '@tayori/core';
import { lambdaAdapter } from '@tayori/lambda';

// Define your event types
interface MyEvent extends WebhookEvent {
  type: 'my.event';
  data: { object: { id: string; message: string } };
}

type MyEventMap = {
  'my.event': MyEvent;
};

// Create a custom verifier
const myVerifier: Verifier = (payload, headers) => {
  // Verify signature and parse payload
  const body = JSON.parse(payload.toString());
  return {
    event: {
      id: body.id,
      type: body.type,
      data: { object: body.data },
    },
  };
};

const router = new WebhookRouter<MyEventMap>();

router.on('my.event', async (event) => {
  console.log('Custom event:', event.data.object.message);
});

export const handler = lambdaAdapter(router, {
  verifier: myVerifier,
});

API Reference

lambdaAdapter

Creates an AWS Lambda handler from a Tayori webhook router.

function lambdaAdapter<TEventMap>(
  router: WebhookRouter<TEventMap>,
  options: LambdaAdapterOptions
): Handler<APIGatewayProxyEvent, APIGatewayProxyResult>

Parameters:

  • router - A WebhookRouter instance from @tayori/core
  • options - Configuration options

Returns: An AWS Lambda Handler function

LambdaAdapterOptions

interface LambdaAdapterOptions {
  /**
   * Verifier function for webhook signature validation
   * @required
   */
  verifier: Verifier;

  /**
   * Custom error handler
   * @optional
   */
  onError?: (error: Error, event?: WebhookEvent) => Promise<void> | void;

  /**
   * Custom success response body
   * @optional
   * @default { success: true }
   */
  successResponse?: unknown;
}

AWS Deployment

With AWS SAM

# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  WebhookFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs18.x
      Environment:
        Variables:
          STRIPE_API_KEY: !Ref StripeApiKey
          STRIPE_WEBHOOK_SECRET: !Ref StripeWebhookSecret
      Events:
        Webhook:
          Type: Api
          Properties:
            Path: /webhook
            Method: post

Parameters:
  StripeApiKey:
    Type: String
    NoEcho: true
  StripeWebhookSecret:
    Type: String
    NoEcho: true

Deploy:

sam build
sam deploy --guided

With Serverless Framework

# serverless.yml
service: stripe-webhooks

provider:
  name: aws
  runtime: nodejs18.x
  environment:
    STRIPE_API_KEY: ${env:STRIPE_API_KEY}
    STRIPE_WEBHOOK_SECRET: ${env:STRIPE_WEBHOOK_SECRET}

functions:
  webhook:
    handler: index.handler
    events:
      - httpApi:
          path: /webhook
          method: post

Deploy:

serverless deploy

With CDK

import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';

const webhookFn = new lambda.Function(this, 'WebhookHandler', {
  runtime: lambda.Runtime.NODEJS_18_X,
  handler: 'index.handler',
  code: lambda.Code.fromAsset('dist'),
  environment: {
    STRIPE_API_KEY: process.env.STRIPE_API_KEY!,
    STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET!,
  },
});

const api = new apigateway.RestApi(this, 'WebhookApi');
const webhookResource = api.root.addResource('webhook');
webhookResource.addMethod('POST', new apigateway.LambdaIntegration(webhookFn));

With Lambda Function URLs

// Lambda function code (no changes needed)
import { lambdaAdapter } from '@tayori/lambda';
// ... rest of your code

export const handler = lambdaAdapter(router, { verifier });
# Create function
aws lambda create-function \
  --function-name stripe-webhook-handler \
  --runtime nodejs18.x \
  --handler index.handler \
  --zip-file fileb://function.zip \
  --role arn:aws:iam::ACCOUNT:role/lambda-role

# Create Function URL
aws lambda create-function-url-config \
  --function-name stripe-webhook-handler \
  --auth-type NONE

# Get the URL
aws lambda get-function-url-config \
  --function-name stripe-webhook-handler

Advanced Usage

Custom Error Handling

export const handler = lambdaAdapter(router, {
  verifier: createStripeVerifier(stripe, secret),
  onError: async (error, event) => {
    // Log to CloudWatch
    console.error(`Webhook error for ${event?.type}:`, error);

    // Send to external monitoring
    await sendToDatadog({
      event: 'webhook.error',
      eventType: event?.type,
      error: error.message,
    });
  },
});

Custom Success Response

export const handler = lambdaAdapter(router, {
  verifier: createStripeVerifier(stripe, secret),
  successResponse: {
    status: 'processed',
    timestamp: Date.now(),
  },
});

Multiple Event Types

const router = new StripeWebhookRouter();

// Payment events
router.group('payment_intent', (r) => {
  r.on('succeeded', async (event) => {
    await processPayment(event.data.object);
  });

  r.on('payment_failed', async (event) => {
    await notifyPaymentFailure(event.data.object);
  });
});

// Subscription events
router.group('customer.subscription', (r) => {
  r.on('created', async (event) => {
    await provisionAccess(event.data.object);
  });

  r.on('deleted', async (event) => {
    await revokeAccess(event.data.object);
  });
});

export const handler = lambdaAdapter(router, {
  verifier: createStripeVerifier(stripe, secret),
});

Response Format

Success Response (200)

{
  "statusCode": 200,
  "headers": {
    "Content-Type": "application/json"
  },
  "body": "{\"success\":true}"
}

Verification Failed (401)

{
  "statusCode": 401,
  "headers": {
    "Content-Type": "application/json"
  },
  "body": "{\"error\":\"Webhook verification failed\"}"
}

Processing Error (500)

{
  "statusCode": 500,
  "headers": {
    "Content-Type": "application/json"
  },
  "body": "{\"error\":\"Webhook processing failed\"}"
}

Testing

Local Testing with SAM CLI

# Start local API
sam local start-api

# Send test webhook
curl -X POST http://localhost:3000/webhook \
  -H "Content-Type: application/json" \
  -d @test-event.json

Unit Testing

import { describe, it, expect } from 'vitest';
import type { APIGatewayProxyEvent, Context } from 'aws-lambda';
import { StripeWebhookRouter } from '@tayori/stripe';
import { lambdaAdapter } from '@tayori/lambda';

describe('Lambda webhook handler', () => {
  it('processes webhook events', async () => {
    const router = new StripeWebhookRouter();
    let processed = false;

    router.on('payment_intent.succeeded', async () => {
      processed = true;
    });

    const handler = lambdaAdapter(router, {
      verifier: mockVerifier,
    });

    const event: APIGatewayProxyEvent = {
      body: JSON.stringify(mockPayload),
      headers: { 'stripe-signature': 'mock-sig' },
      // ... other required fields
    };

    const result = await handler(event, {} as Context, () => {});

    expect(result.statusCode).toBe(200);
    expect(processed).toBe(true);
  });
});

Integration Testing with Stripe CLI

# Forward webhooks to local Lambda
stripe listen --forward-to http://localhost:3000/webhook

# Trigger test events
stripe trigger payment_intent.succeeded

Performance Optimization

Minimize Cold Starts

// Initialize Stripe client outside handler
const stripe = new Stripe(process.env.STRIPE_API_KEY!);
const router = new StripeWebhookRouter();

// Configure handlers outside
router.on('payment_intent.succeeded', async (event) => {
  // Handler logic
});

// Export handler (called on each invocation)
export const handler = lambdaAdapter(router, {
  verifier: createStripeVerifier(stripe, process.env.STRIPE_WEBHOOK_SECRET!),
});

Use Lambda Layers

Package common dependencies in Lambda Layers:

# Create layer
mkdir -p layer/nodejs
cd layer/nodejs
npm install stripe @tayori/stripe @tayori/lambda @tayori/core

# Package layer
cd ..
zip -r layer.zip nodejs/

Common Patterns

DynamoDB Integration

import { DynamoDB } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';

const ddb = DynamoDBDocument.from(new DynamoDB({}));

router.on('customer.subscription.created', async (event) => {
  await ddb.put({
    TableName: 'Subscriptions',
    Item: {
      id: event.data.object.id,
      customerId: event.data.object.customer,
      status: event.data.object.status,
      createdAt: Date.now(),
    },
  });
});

SQS Integration

import { SQS } from '@aws-sdk/client-sqs';

const sqs = new SQS({});

router.on('payment_intent.succeeded', async (event) => {
  await sqs.sendMessage({
    QueueUrl: process.env.QUEUE_URL!,
    MessageBody: JSON.stringify({
      type: 'payment.process',
      paymentIntentId: event.data.object.id,
    }),
  });
});

Requirements

  • Node.js >= 18
  • TypeScript >= 5.3
  • AWS Lambda runtime compatible with Node.js 18+

Related Packages

Documentation

For more examples and guides, see the main documentation.

License

MIT