Jaypie
TypeScript framework for AWS Lambda and Express applications with CDK constructs and Datadog observability.
Stack: AWS Lambda, CDK, Express.js, Datadog, TypeScript, Node.js 22-25
Documentation
Section titled “Documentation”| Section | Description |
|---|---|
| Core Concepts | Handler lifecycle, errors, logging, environment |
| How-To Guides | Step-by-step guides for common tasks |
| Packages | API reference for each package |
| Experimental | Unstable packages in development |
| Architecture | Project structure, patterns |
| Contributing | Development workflow |
Getting Started
Section titled “Getting Started”What is Jaypie?
Section titled “What is Jaypie?”Jaypie is a TypeScript framework for AWS Lambda and Express applications with integrated CDK constructs and Datadog observability.
| Layer | Package | Purpose |
|---|---|---|
| Infrastructure | @jaypie/constructs |
CDK constructs for Lambda, API Gateway, CloudFront |
| Handlers | @jaypie/express, @jaypie/lambda |
Handler wrappers with lifecycle and error formatting |
| Observability | @jaypie/logger, @jaypie/datadog |
Structured logging, metrics |
| Testing | @jaypie/testkit |
Mocks and matchers |
| Utilities | @jaypie/kit, @jaypie/errors |
Type coercion, error classes |
Installation
Section titled “Installation”Core (Most Projects)
Section titled “Core (Most Projects)”npm install jaypieWith CDK
Section titled “With CDK”npm install jaypie @jaypie/constructs aws-cdk-lib constructsWith LLM
Section titled “With LLM”npm install jaypie @jaypie/llmDevelopment
Section titled “Development”npm install -D @jaypie/testkit @jaypie/eslint @jaypie/repokit vitestQuick Reference
Section titled “Quick Reference”Handler Pattern
Section titled “Handler Pattern”import { expressHandler, log, NotFoundError } from "jaypie";
export default expressHandler( async (req, res) => { log.trace("[getUser] fetching"); const user = await db.users.findById(req.params.id); if (!user) throw NotFoundError(); return { data: user }; }, { name: "getUser", secrets: ["MONGODB_URI"], validate: [(req) => req.params.id], setup: [async () => await connectDb()], teardown: [async () => await disconnectDb()], });Handler Options
Section titled “Handler Options”| Option | Type | Purpose |
|---|---|---|
name |
string |
Handler name for logging |
secrets |
string[] |
Load from AWS Secrets Manager |
validate |
Function[] |
Validation (throw or return false) |
setup |
Function[] |
Run before handler |
teardown |
Function[] |
Run after handler (always) |
Response Mapping
Section titled “Response Mapping”| Return | Status |
|---|---|
object |
200 |
null/undefined |
204 |
true |
201 |
| Thrown error | Error status |
Error Classes
Section titled “Error Classes”| Class | Status | Use When |
|---|---|---|
BadRequestError |
400 | Invalid input |
UnauthorizedError |
401 | No/invalid auth |
ForbiddenError |
403 | No permission |
NotFoundError |
404 | Resource missing |
InternalError |
500 | Server error |
ConfigurationError |
500 | Missing config |
BadGatewayError |
502 | External service error |
UnavailableError |
503 | Service down |
import { BadRequestError, NotFoundError } from "jaypie";
throw BadRequestError("Email required");throw NotFoundError();Logging
Section titled “Logging”| Level | Use Case |
|---|---|
trace |
Happy path |
debug |
Unexpected but handled |
info |
Significant events (rare) |
warn |
Concerning situations |
error |
Failures |
import { log } from "jaypie";
log.trace("[functionName] starting");log.var({ userId: "123" }); // Single keylog.debug("Cache miss");log.error("Service failed");Utilities
Section titled “Utilities”Type Coercion
Section titled “Type Coercion”import { force } from "jaypie";
force.boolean("true"); // trueforce.number("42"); // 42force.array("item"); // ["item"]force.string(null, "default"); // "default"Environment Checks
Section titled “Environment Checks”import { isProductionEnv, isLocalEnv, isNodeTestEnv } from "jaypie";
if (isProductionEnv()) { /* production */ }if (isLocalEnv()) { /* local dev */ }if (isNodeTestEnv()) { /* testing */ }Other Utilities
Section titled “Other Utilities”import { uuid, sleep, cloneDeep } from "jaypie";
const id = uuid();await sleep(1000);const copy = cloneDeep(original);Environment Variables
Section titled “Environment Variables”| Variable | Purpose |
|---|---|
PROJECT_ENV |
Environment: local, sandbox, production |
PROJECT_KEY |
Project identifier |
LOG_LEVEL |
trace, debug, info, warn, error |
SECRET_* |
Secret references (fetched from Secrets Manager) |
CDK_ENV_QUEUE_URL |
Default SQS queue |
CDK_ENV_BUCKET |
Default S3 bucket |
Testing Setup
Section titled “Testing Setup”vitest.config.ts:
import { defineConfig } from "vitest/config";
export default defineConfig({ test: { setupFiles: ["@jaypie/testkit/testSetup"], },});Mock Jaypie:
import { vi } from "vitest";
vi.mock("jaypie", async () => { const testkit = await import("@jaypie/testkit/mock"); return testkit;});Custom Matchers:
expect(() => fn()).toThrowBadRequestError();expect(() => fn()).toThrowNotFoundError();expect(value).toMatchUuid();CDK Constructs
Section titled “CDK Constructs”import { JaypieLambda, JaypieApiGateway } from "@jaypie/constructs";
const api = new JaypieLambda(this, "Api", { code: "../api/dist", handler: "handler.handler", secrets: [mongoSecret],});
new JaypieApiGateway(this, "Gateway", { handler: api, host: "api.example.com", zone: "example.com",});LLM Integration
Section titled “LLM Integration”import Llm from "@jaypie/llm";
const response = await Llm.operate("What is 2+2?", { model: "claude-sonnet-4",});
// Streamingfor await (const chunk of Llm.stream("Tell me a story")) { process.stdout.write(chunk.content || "");}Packages
Section titled “Packages”| Package | Purpose |
|---|---|
jaypie |
Main package: re-exports express, lambda, errors, kit, logger |
@jaypie/express |
Express handler wrapper |
@jaypie/lambda |
Lambda handler wrapper |
@jaypie/errors |
JSON:API error classes |
@jaypie/logger |
Structured logging |
@jaypie/kit |
Utilities: force, uuid, sleep |
Infrastructure
Section titled “Infrastructure”| Package | Purpose |
|---|---|
@jaypie/constructs |
CDK constructs with Datadog |
@jaypie/llm |
LLM provider abstraction |
Development
Section titled “Development”| Package | Purpose |
|---|---|
@jaypie/testkit |
Mocks and matchers |
@jaypie/eslint |
ESLint configuration |
@jaypie/repokit |
Repository tooling |
Experimental
Section titled “Experimental”| Package | Purpose |
|---|---|
@jaypie/dynamodb |
DynamoDB single-table patterns |
@jaypie/fabric |
Service handler adapters |
@jaypie/fabricator |
Test data generation |
@jaypie/mcp |
Model Context Protocol server |
@jaypie/textract |
AWS Textract utilities |
Next Steps
Section titled “Next Steps”| Goal | Page |
|---|---|
| Understand handlers | Handler Lifecycle |
| Build Express API | Express on Lambda |
| Set up CDK | CDK Infrastructure |
| Write tests | Testing |
| Add LLM | LLM Integration |
| CI/CD setup | CI/CD |