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

@felloh-org/lambda-wrapper

v1.11.219

Published

Lambda wrapper for all Felloh Serverless Projects

Readme

Lambda Wrapper

release

A shared library for Felloh serverless projects that provides core functionality including dependency injection, logging, request handling, database entities, and response models for AWS Lambda functions.

Installation

npm install @felloh-org/lambda-wrapper
# or
yarn add @felloh-org/lambda-wrapper

Quick Start

import { LambdaWrapper, DEFINITIONS, ResponseModel } from '@felloh-org/lambda-wrapper';

const configuration = {
  SERVICE_NAME: 'my-service',
};

export const handler = LambdaWrapper(configuration, async (di, request) => {
  const logger = di.get(DEFINITIONS.LOGGER);
  const warehouse = di.get(DEFINITIONS.WAREHOUSE);

  logger.info('Processing request');

  // Your handler logic here

  return new ResponseModel({ message: 'Success' }, 200).generate();
});

Lambda Wrapper

The core wrapper function takes a configuration object and a handler function, returning a Lambda-compatible function with built-in dependency injection, error handling, logging, and metrics.

import { LambdaWrapper } from '@felloh-org/lambda-wrapper';

const handler = LambdaWrapper(configuration, async (di, request, callback) => {
  // di      - DependencyInjection container with all services
  // request - RequestService instance with parsed request data
  // callback - Lambda callback (for non-async handlers)
});

Handler Patterns

Async handler (recommended):

export const handler = LambdaWrapper(config, async (di, request) => {
  const response = new ResponseModel({ result: 'ok' }, 200);
  return response.generate();
});

Callback handler:

export const handler = LambdaWrapper(config, (di, request, callback) => {
  const response = new ResponseModel({ result: 'ok' }, 200);
  callback(null, response.generate());
});

Throw errors as responses:

export const handler = LambdaWrapper(config, async (di, request) => {
  const params = request.getAll();

  if (!params.email) {
    const error = new ResponseModel({}, 422);
    error.addError({
      title: 'Validation Error',
      message: 'Email is required',
    });
    throw error;  // Returns a 422 response, not a 500
  }

  // continue processing...
});

Error Handling

The wrapper automatically catches errors and returns appropriate responses:

  • ResponseModel thrown - returns the response as-is (e.g. 400, 404, 422)
  • LambdaTermination thrown - returns a response using the error's code property
  • Unhandled errors - returns a 500 response and logs the error
  • Database size limit errors - returns a 413 response

5xx errors are logged automatically; 4xx errors are not.

Warm-up Support

The wrapper handles serverless-plugin-warmup events automatically. When a warmup event is detected, the handler returns immediately without executing your code.

Dependency Injection

Every handler receives a DependencyInjection container as its first argument. The container provides access to all built-in services and any custom services you register.

Built-in Services

| Service | Definition | Description | |---------|------------|-------------| | Logger | DEFINITIONS.LOGGER | Structured logging with Winston and Baselime | | Request | DEFINITIONS.REQUEST | Parsed HTTP request data | | Warehouse | DEFINITIONS.WAREHOUSE | TypeORM database connection manager | | Authentication | DEFINITIONS.AUTHENTICATION | User role and organisation checks | | User | DEFINITIONS.USER | Current user from Cognito JWT claims | | EventBridge | DEFINITIONS.EVENT_BRIDGE | AWS EventBridge event publishing | | Secrets | DEFINITIONS.SECRETS | AWS Secrets Manager with caching | | HTTP | DEFINITIONS.HTTP | Axios-based HTTP client | | Webhook | DEFINITIONS.WEBHOOK | HMAC-signed webhook dispatch | | AuditLogger | DEFINITIONS.AUDIT_LOGGER | Audit event creation via EventBridge | | PaymentInitiationObject | DEFINITIONS.PAYMENT_INITIATION_OBJECT | Payment link and ecommerce lookup |

Custom Services

Create custom services by extending DependencyAwareClass:

import { DependencyAwareClass, DEFINITIONS } from '@felloh-org/lambda-wrapper';

class PaymentProcessor extends DependencyAwareClass {
  async processPayment(transactionId) {
    const logger = this.getContainer().get(DEFINITIONS.LOGGER);
    const warehouse = this.getContainer().get(DEFINITIONS.WAREHOUSE);

    const connection = await warehouse.connect();
    const repo = connection.getRepository(TransactionEntity);
    const transaction = await repo.findOne({ where: { id: transactionId } });

    logger.info(`Processing payment ${transactionId}`);

    return transaction;
  }
}

Register custom services in the configuration:

const configuration = {
  DEPENDENCIES: {
    PAYMENT_PROCESSOR: PaymentProcessor,
  },
};

export const handler = LambdaWrapper(configuration, async (di, request) => {
  const processor = di.get('PAYMENT_PROCESSOR');
  await processor.processPayment('txn_123');
});

Container API

// Get a service
const logger = di.get(DEFINITIONS.LOGGER);

// Get the raw Lambda event
const event = di.getEvent();

// Get the Lambda context
const context = di.getContext();

// Get configuration values
const config = di.getConfiguration();           // full config
const dbHost = di.getConfiguration('DB_HOST');   // specific key, returns null if missing

// Check if running in serverless-offline
if (di.isOffline) {
  // local development mode
}

Services

RequestService

Parses and provides access to all HTTP request data.

const request = di.get(DEFINITIONS.REQUEST);

// Get a single parameter (checks query params for GET, body for POST)
const email = request.get('email');
const page = request.get('page', '1');              // with default value

// Get all parameters
const allParams = request.getAll();

// Force reading from query string regardless of HTTP method
const queryParams = request.getAll('GET');

// Path parameters
const id = request.getPathParameter('id');
const allPath = request.getPathParameter();          // all path params

// Headers (case-insensitive)
const contentType = request.getHeader('Content-Type');
const allHeaders = request.getAllHeaders();

// Authorization
const token = request.getAuthorizationToken();       // extracts Bearer token

// Client info
const ip = request.getIp();
const browser = request.getUserBrowserAndDevice();   // parsed user-agent

// AWS event records (SQS, SNS, etc.)
const record = request.getAWSRecords();

Content type support:

The request service automatically parses the body based on the Content-Type header:

  • application/json - JSON parsing
  • application/x-www-form-urlencoded - form data parsing
  • text/xml - XML parsing via xml2js
  • multipart/form-data - file upload parsing with Buffer support

Request validation:

const constraints = {
  email: { presence: true, email: true },
  amount: { presence: true, numericality: { greaterThan: 0 } },
  start_date: { datetime: { dateOnly: true } },
};

await request.validateAgainstConstraints(constraints);
// Resolves on success, rejects with a 422 ResponseModel on failure

Validation uses validate.js with datetime support via moment.

LoggerService

Structured logging with Winston and Baselime integration. Pretty-prints output in serverless-offline mode.

const logger = di.get(DEFINITIONS.LOGGER);

// Standard logging
logger.info('Payment processed successfully');
logger.error(new Error('Connection failed'));
logger.warning(error);                            // routes to error() or info() based on LOGGER_SOFT_WARNING env

// Labels (lightweight markers for tracing)
logger.label('payment-initiated');
logger.label('silent-label', true);               // silent = not logged to console

// Metrics
logger.metric('response_time', 250);
logger.metric('ip_address', '10.0.0.1', true);   // silent = not logged to console

// Object inspection
logger.object('Processing payload', { id: 1, amount: 500 });
logger.object('Failed response', errorObj, 'error');  // supports 'info', 'warning', 'error'

Axios errors are automatically trimmed to config, message, response.status, and response.data - stripping verbose request/header data from logs.

WarehouseService

Manages TypeORM database connections with connection caching.

const warehouse = di.get(DEFINITIONS.WAREHOUSE);

// Get a connection (cached by default)
const connection = await warehouse.connect();

// Force a new connection (bypass cache)
const fresh = await warehouse.connect(null, false);

// Connect to a specific database
const analytics = await warehouse.connect('analytics_db');

// Health check
await warehouse.status();

Connection types:

Set via the WAREHOUSE_TYPE environment variable:

  • postgres - direct PostgreSQL connection using WAREHOUSE_HOST, WAREHOUSE_USERNAME, WAREHOUSE_PASSWORD, WAREHOUSE_DATABASE
  • postgres-ssm - credentials fetched from AWS Secrets Manager using DB_CREDS_ARN

AuthenticationService

Checks user roles and organisation access. Lazy-initialises a database connection on first use.

const auth = di.get(DEFINITIONS.AUTHENTICATION);
await auth.init();

// Check organisation access and roles in one call
// Throws a 401 ResponseModel if checks fail
await auth.checkUserRoles('org-123', ['admin', 'finance']);

// Check organisation access only
await auth.checkUserRoles('org-123');

// Check roles only
await auth.checkUserRoles(null, ['admin']);

// Individual checks
const roles = await auth.fetchUserRoles();                // ['admin', 'viewer']
const hasAdmin = await auth.hasRole('admin');              // true/false
const orgs = await auth.fetchUserOrganisations(user);     // includes descendants

HTTPService

Axios-based HTTP client with configurable timeout.

const http = di.get(DEFINITIONS.HTTP);

// Default timeout is 10 seconds
http.setDefaultTimeout(30000);

const response = await http.request({
  method: 'POST',
  url: 'https://api.example.com/payments',
  headers: { Authorization: 'Bearer token' },
  data: { amount: 1000 },
});

SecretsService

Fetches and caches secrets from AWS Secrets Manager.

const secrets = di.get(DEFINITIONS.SECRETS);

// Fetched once, then cached for the Lambda execution
const creds = await secrets.get('arn:aws:secretsmanager:eu-west-1:123:secret:db-creds');
// { username: 'admin', password: '...', host: '...', ... }

WebhookService

Dispatches HMAC-SHA256 signed webhooks.

const webhook = di.get(DEFINITIONS.WEBHOOK);

await webhook.send(
  'https://example.com/webhook',
  { event: 'payment.completed', transaction_id: 'txn_123' },
  'webhook-signing-secret'
);
// POST with X-Signature header containing HMAC-SHA256 hex digest

AuditLoggerService

Creates audit trail events via EventBridge.

const auditLogger = di.get(DEFINITIONS.AUDIT_LOGGER);

await auditLogger.createEvent('payment.refunded', 'entity-123', 'org-456');

Response Model

All Lambda responses use a standardised format with CORS headers.

import { ResponseModel } from '@felloh-org/lambda-wrapper';

// Basic response
const response = new ResponseModel({ users: [] }, 200);
return response.generate();

// Building a response
const response = new ResponseModel();
response.setData({ id: 1, name: 'Test' });
response.setCode(201);
response.setMetaVariable('pagination', { page: 1, total: 10 });
response.setBodyVariable('warnings', ['Deprecated field used']);
return response.generate();

// Adding errors
const error = new ResponseModel({}, 422);
error.addError({
  title: 'Validation Error',
  message: 'Email is not valid',
  documentation_url: 'https://developers.felloh.com/errors#error-responses',
  type: 'validation',
  code: 'validation.email',
});
throw error;  // or return error.generate()

// Static shorthand
return ResponseModel.generate({ result: 'ok' }, 200);

Generated response format:

{
  "statusCode": 200,
  "headers": {
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Credentials": true
  },
  "body": "{\"data\":{},\"errors\":[],\"meta\":{\"code\":200,\"reason\":\"OK\",\"message\":\"The request was successful\",\"request_id\":\"uuid-v4\"}}"
}

Supported status codes:

| Code | Reason | |------|--------| | 200 | OK | | 201 | Created | | 204 | No Content | | 400 | Bad Request | | 401 | Unauthorized | | 403 | Forbidden | | 404 | Not Found | | 406 | Not Acceptable | | 410 | Gone | | 413 | Request Entity Too Large | | 422 | Unprocessable Entity | | 429 | Too Many Requests | | 500 | Internal Server Error | | 502 | Bad Gateway | | 503 | Service Unavailable | | 504 | Gateway Timeout |

Status Model

Used for health checks and service status reporting.

import { StatusModel, STATUS_TYPES } from '@felloh-org/lambda-wrapper';

const status = new StatusModel('database', STATUS_TYPES.OK);
// STATUS_TYPES: OK, ACCEPTABLE_FAILURE, APPLICATION_FAILURE

Lambda Termination

A custom error class for terminating Lambda execution with a specific status code and consumer-facing message.

import { LambdaTermination } from '@felloh-org/lambda-wrapper';

// Terminates with a 503 response
throw new LambdaTermination(
  'Payment provider timeout after 30s',  // internal (logged)
  503,                                    // status code
  'Service temporarily unavailable',      // body (returned to consumer)
  'Provider X timed out'                  // details
);

EventBridge Events

Publish typed events to AWS EventBridge.

import {
  LambdaWrapper,
  DEFINITIONS,
  TransactionCompleteEvent,
  TransactionCompleteEmailEvent,
  UserRegistrationEvent,
  CSVEmailEvent,
  RefundRequestEmailEvent,
  BaseEvent,
} from '@felloh-org/lambda-wrapper';

export const handler = LambdaWrapper(config, async (di) => {
  const eventBridge = di.get(DEFINITIONS.EVENT_BRIDGE);

  // Use a pre-built event
  const event = new TransactionCompleteEvent();
  event.setDetailParam('transaction_id', 'txn_123');
  await eventBridge.put(event);

  // Or build a custom event
  const custom = new BaseEvent('order.shipped');
  custom.setDetailParam('order_id', 'ord_456');
  custom.setDetail({ order_id: 'ord_456', carrier: 'DHL' });
  await eventBridge.put(custom);
});

Available event types:

| Event | Description | |-------|-------------| | BaseEvent | Generic event, set your own detail type | | TransactionCompleteEvent | Transaction completed | | TransactionCompleteEmailEvent | Transaction receipt email | | UserRegistrationEvent | New user registration | | UserPasswordChangedEvent | Password changed | | UserPasswordResetEvent | Password reset requested | | SandboxRegistrationEvent | Sandbox account registration | | CSVEmailEvent | CSV export email | | POSCRequestEmailEvent | POSC request email | | RefundRequestEmailEvent | Refund request notification |

Webhook Models

Format transaction and refund data for webhook payloads.

import { TransactionWebhookModel, RefundWebhookModel } from '@felloh-org/lambda-wrapper';

// Transaction webhook
const payload = new TransactionWebhookModel(transaction, 'Payment received');
const data = payload.get();
// { transaction: { id, narrative }, amount, booking, status, currency, surcharge, ... }

Database (TypeORM)

Entities

The library includes TypeORM entities organised by domain:

| Domain | Description | |--------|-------------| | payment | Transactions, refunds, chargebacks, payment links, ecommerce, providers, sessions | | user | Users, organisations, roles, features, webhooks, billing, partners, portals | | bank | Accounts, ledgers, settlements, disbursals, adjustments, beneficiaries | | agent-data | Bookings, components, suppliers, package types | | acquirer | BIN data, batches, adjustments, band rates, transaction settlements | | aisp | Open banking transactions, GoCardless accounts/agreements/requisitions | | nuapay | Nuapay accounts, balances, beneficiaries, transactions | | nuvei | Nuvei batches, merchants, transactions, metadata | | trust-payments | Trust Payments batches, merchants, transactions, chargebacks, fees | | total-processing | Total Processing transactions and metadata | | planet | Planet transactions, metadata, batches | | saltedge | Saltedge accounts, connections, customers, transactions, leads | | basis-theory | Tokenisation tokens | | go-cardless | GoCardless organisation configuration | | marketing | Marketing contacts and ESUs | | reference | Countries | | playbook | Playbooks, requests, secrets, versions |

Migrations

Run migrations against the database:

# Create database schemas
yarn orm:schema:create

# Run pending migrations
yarn orm:migration:run

# Generate a new migration
yarn orm:migration:generate <name>

# Revert last migration
yarn orm:revert

Migrations require the following environment variables:

| Variable | Description | |----------|-------------| | WAREHOUSE_TYPE | postgres or postgres-ssm | | WAREHOUSE_HOST | Database host (for postgres type) | | WAREHOUSE_USERNAME | Database username (for postgres type) | | WAREHOUSE_PASSWORD | Database password (for postgres type) | | WAREHOUSE_DATABASE | Database name | | WAREHOUSE_SCHEMA | Database schema | | DB_CREDS_ARN | Secrets Manager ARN (for postgres-ssm type) |

Database Schemas

The database is organised into the following schemas: user, payment, bank, agent_data, nuapay, trust_payments, total_processing, token, nuvei, reference, saltedge, acquirer, operations, basis_theory, aisp, go_cardless, planet, marketing, amex, crm, shield.

Utilities

PromisifiedDelay

Provides randomised delays for retry logic, with configurable latency profiles.

import { PromisifiedDelay } from '@felloh-org/lambda-wrapper';

// High latency profile (2-20s delays, weighted towards shorter)
const delay = new PromisifiedDelay(true);

// Standard latency profile (2-5s delays)
const delay = new PromisifiedDelay(false);

// Use in retry loop
for (let attempt = 0; attempt < 3; attempt++) {
  try {
    return await makeRequest();
  } catch (error) {
    await delay.get();
  }
}

Development

Commands

# Build for production (webpack)
yarn build

# Build for TypeORM migrations (babel)
yarn build:orm

# Run linter
yarn lint

# Run tests with coverage
yarn test

# Run dependency vulnerability audit
yarn audit:check

# Run database migrations
yarn orm:migration:run

# Generate a new migration
yarn orm:migration:generate <name>

# Revert last migration
yarn orm:revert

# Create database schemas
yarn orm:schema:create

Environment Variables

| Variable | Description | |----------|-------------| | SERVICE_NAME | Service identifier for logging and events | | EVENT_BRIDGE_ARN | AWS EventBridge bus ARN | | REGION | AWS region | | WAREHOUSE_TYPE | Database connection type (postgres or postgres-ssm) | | WAREHOUSE_HOST | Database host | | WAREHOUSE_USERNAME | Database username | | WAREHOUSE_PASSWORD | Database password | | WAREHOUSE_DATABASE | Database name | | WAREHOUSE_SCHEMA | Database schema | | DB_CREDS_ARN | Secrets Manager ARN for database credentials | | DB_LOGGING | Set to true to enable TypeORM query logging | | LOGGER_SOFT_WARNING | Set to true to downgrade warnings to info level | | IS_OFFLINE | Set by serverless-offline for local development |

Project Structure

src/
├── action/              # Action handlers (e.g. unmatched route)
├── config/              # Dependency definitions
├── dependency-injection/  # DI container and base class
├── entity/              # TypeORM entities by domain
├── enums/               # Shared enumerations
├── event/               # EventBridge event classes
├── migration/           # Database migrations by schema
├── model/               # Response, status, and webhook models
├── service/             # Core services (logger, request, warehouse, auth, etc.)
├── util/                # Utilities (LambdaTermination, PromisifiedDelay)
└── wrapper/             # Lambda wrapper implementation

CI/CD

The project uses Concourse CI with the following pipeline:

  1. lint-and-test - runs lint, tests, and dependency audit in parallel
  2. migrations.staging - creates schemas and runs migrations on staging
  3. migrations.production - promotes to production after staging succeeds
  4. migrations.sandbox - promotes to sandbox after staging succeeds

Testing

Tests are colocated with source files (e.g. src/service/logger.test.js alongside src/service/logger.js).

yarn test

License

MIT