Guidance for AI coding agents (and humans) working in this repository.
This is an AWS Lambda serverless starter project written in Node.js and TypeScript, organized as an npm workspaces monorepo. The application code and the AWS CDK infrastructure code are separate workspaces. All code is ESM, and all unit tests use Vitest.
- Language: TypeScript 6
- Module System: ESM (
"type": "module",module: nodenext) - Platform: AWS Lambda
- Runtime: Node.js 24+
- AWS SDK: v3 (modular packages)
- Testing: Vitest (with
@vitest/coverage-v8) - Linting/Formatting: ESLint + Prettier
- Validation: Zod
- Logging: Pino + Pino-Lambda (
@leanstacks/lambda-utils) - Package Manager: npm (workspaces)
- Infrastructure: AWS CDK v2 (run with
tsx) - DevOps: GitHub Actions
/packages
/api # Workspace: Lambda functions and application source
/src
/handlers # Lambda handlers (parse input, call services, return responses)
get-task.ts
get-task.test.ts # Unit tests live next to the source file
/services # Business logic
/models # Data models, DTOs, and Zod schemas
/utils # AWS clients, config, logging, constants
package.json # API dependencies and scripts
tsconfig.json # Extends /tsconfig.base.json; defines the `@/` alias
vitest.config.ts # Extends /vitest.config.ts with mergeConfig
/infra # Workspace: AWS CDK infrastructure as code
/stacks # One stack per major grouping of resources
/utils
config.ts # CDK config helper (validated with Zod)
app.ts # CDK app entry point
cdk.json # CDK config
.env.example # Documents required CDK environment variables
package.json # CDK dependencies and scripts
tsconfig.json # Extends /tsconfig.base.json; defines the `@/` alias
vitest.config.ts # Extends /vitest.config.ts with mergeConfig
/docs # Project documentation (see docs/README.md)
/.github # Workflows, issue and PR templates, Copilot pointer to this file
.editorconfig # Editor config
.nvmrc # Node version
.prettierrc # Prettier config
eslint.config.mjs # ESLint config (covers all workspaces)
package.json # Root: workspaces, shared devDependencies and scripts
tsconfig.base.json # Base TypeScript config extended by the workspaces
vitest.config.ts # Base Vitest config extended by the workspaces
- Shared/common packages (TypeScript, ESLint, Prettier, Vitest,
@types/*, etc.) are installed in the rootpackage.json. - Workspace-specific dependencies live in the workspace
package.json. - There is a single
package-lock.jsonat the root. Runnpm install/npm cifrom the root only. - Common scripts (
build,clean,test,test:coverage) are defined at the root and run in each workspace with--workspaces --if-present.
Run from the repository root unless noted.
npm run build- Type-check all workspaces (tsc --noEmit). Lambdas are bundled by CDK/esbuild at synth/deploy time.npm run clean- Remove build outputs and coverage.npm run test- Run all unit tests.npm run test:coverage- Run all unit tests with coverage.npm run test:watch- Run Vitest in watch mode.npm run lint/npm run lint:fix- Run ESLint.npm run format/npm run format:check- Run/check Prettier.npm run <script> -w packages/api- Run a script in a single workspace (usepackages/infrafor CDK).npm run synth -w packages/infra- Synthesize CDK stacks.npm run cdk -w packages/infra -- <command>- Run any CDK command (for examplediff,deploy).npm run local:start/local:stop/local:logs- Manage LocalStack. Seedocs/LocalStackGuide.md.
Before finishing a change, make sure npm run lint, npm run format:check, npm run build, and npm run test pass.
- Use TypeScript for all source and infrastructure code.
- Use ESM. Relative and alias imports must include the
.jsextension (for exampleimport { logger } from '@/utils/logger.js'), even though the source file is.ts. - Use
node:prefixes for Node built-ins (for examplenode:path). There is no__dirnamein ESM; useimport.meta.url. - The
@/alias refers to thesrcdirectory of the API workspace (packages/api/src) and to the root of the infra workspace (packages/infra). It is configured in each workspace'stsconfig.json(paths) andvitest.config.ts(resolve.alias), and esbuild uses the API tsconfig when bundling the Lambdas. - Use arrow functions for defining functions.
- Handlers parse input, call services, and return responses; they reside in
/handlers. - Core business logic resides in
/services. - Create types, interfaces, and DTOs in
/models. - Create reusable utilities in
/utils(AWS clients, config helpers, logging). - Validate configuration and input data with Zod.
- Organize import statements: external packages first, then internal modules.
- Use async/await for asynchronous operations.
- Handle errors gracefully and return meaningful error responses.
- Document functions and modules with JSDoc comments.
- Format with Prettier (120 columns, single quotes, semicolons, trailing commas). Do not hand-format.
- Use Vitest. Import test APIs explicitly:
import { describe, it, expect, vi, beforeEach } from 'vitest'(globals are disabled). - Place test files next to the source file with a
.test.tssuffix. - Use
describeanditblocks for organization;beforeEachfor setup andafterEachfor cleanup. - Mock dependencies to isolate the component under test, and mock external calls (AWS SDK, databases).
vi.mockcalls are hoisted above imports. Variables used inside avi.mockfactory must be created withvi.hoisted(() => ({ ... })).- To test module-level initialization, call
vi.resetModules()and thenawait import('./module.js')inside the test. Usevi.doMockfor per-test, non-hoisted mocks. - A mock used as a constructor (
new) needs afunctionorclassimplementation, not an arrow function. - Structure tests using Arrange-Act-Assert, and add comments to separate the sections.
- Shared Vitest settings live in the root
vitest.config.ts. Workspaces extend it usingmergeConfig.
- The design philosophy prioritizes cost efficiency, scalability, and security.
- Use AWS CDK v2 and TypeScript in
packages/infra. The CDK app runs withtsx(seecdk.json). - Define one CDK stack per major grouping of resources (for example lambda stack, data stack).
- Never commit secrets or hardcoded credentials.
- Use
packages/infra/.envfor local configuration (never commit it) and.env.exampleto document required variables. Prefix variables withCDK_. - Use AWS SSM Parameter Store for secure configuration.
- Tag all CDK resources for cost allocation and management:
App,Env(dev, qat, prd),OU, andOwner. - Use NodejsFunction from
aws-cdk-lib/aws-lambda-nodejsfor the Lambdas. Handlers are bundled as ESM (OutputFormat.ESM,index.mjs) using the API workspacetsconfig.json.
- Prefer single-table design where feasible.
- Use composite primary keys (partition key + sort key) for efficient querying.
- Keep
README.mdand/docsup to date when changing structure, scripts, or configuration. - This
AGENTS.mdis the single source of truth for agent instructions..github/copilot-instructions.mdonly points here.