create-lambda-graphql-app: A CLI That Scaffolds Fully Wired AWS AppSync + Lambda Services
Every engineering team has that one repository everyone secretly clones just to copy-paste a folder.
For us, it was a microservice built around AWS AppSync and Lambda. Whenever someone needed to write a new API handler, the ritual was always the same: find the last service a teammate built, copy the folder, purge whatever logic wasn't needed, rename half the files, fix the broken relative imports, manually update template.yaml, forget to update the GraphQL schema, and eventually deploy something that mostly worked.
It was slow, error-prone, and propagated technical debt across projects. Every copy carried forward the hidden flaws of the template before it.
To solve this, I packaged our entire boilerplate into an open-source CLI tool: create-lambda-graphql-app.
npx create-lambda-graphql-app
That's all it takes. Answer a few interactive prompts and you get a production-ready, fully wired project structure instantly.
The Root Problem with Manual Scaffolding
Copy-pasting directories fails in subtle, compounding ways over time:
-
Directory drift. Service A keeps input validation in
validators/, Service B moves it tovalidation/, and Service C inlines it inside the handler. Six months later, jumping between repositories requires a re-learning curve. -
Invisible glue code. An AppSync service requires multiple pieces to stay tightly synced: the GraphQL schema, VTL mapping templates, AppSync data sources, resolvers, IAM execution roles, and Lambda code. Missing a single link leads to successful deployments that silently fail at runtime with
400 Bad Requesterrors. - Tribal knowledge onboarding. Relying on "just ask whoever built the last one" is not a scalable documentation strategy.
-
Boilerplate fatigue. Setting up
.gitignore, ESLint, Prettier, debug configurations, and local invocation runners gets reinvented slightly differently every single time.
The CLI makes the ideal architecture the path of least resistance: running a single command is simply faster than manually copying and editing folders.
What's Included Out of the Box
The package unifies the whole stack — interactive CLI, directory structure, GraphQL typing, infrastructure as code, and local developer tooling.
1. The CLI tool
A lightweight, zero-bloat Node.js CLI written in modern ES Modules (Node 18+):
| Layer | Dependency | Purpose |
|---|---|---|
| Parsing | commander |
Command-line option management |
| Prompts | inquirer |
Interactive user input |
| Styling | chalk |
Colorized terminal output |
| File system | fs-extra |
Directory creation and templating |
| Formatting | dedent |
Clean code generation formatting |
It provides two core workflows:
-
npx create-lambda-graphql-app— scaffolds a new service from scratch. -
npx create-lambda-graphql-app add <handler-name>— safely injects a new handler into an existing project.
2. Standard project structure
Here is the folder structure generated out of the box:
my-service/
├── handlers/
│ └── create-order/
│ ├── index.mjs # Orchestration pipeline
│ ├── validators/ # Request payload validation (Joi)
│ ├── transformers/ # Input data transformation
│ ├── helpers/ # Core business logic
│ ├── dal/ # Data Access Layer (DynamoDB, S3, etc.)
│ ├── exceptions/ # Custom error definitions
│ └── constants/
├── layers/
│ └── common-dependency/ # Shared node_modules layer
├── local-test/
│ └── create-order/ # Local invocation events & harness
├── mapping/
│ ├── request.vtl # AppSync request template + guards
│ └── response.vtl # AppSync response template + security headers
├── schema/
│ └── schema.graphql # GraphQL SDL definition
├── scripts/
│ └── deploy-assets.mjs # S3 sync script for schema & VTL
├── .vscode/
│ └── launch.json # Auto-generated debug configs per handler
├── template.yaml # AWS SAM infrastructure definition
├── samconfig.toml # Default SAM deployment configuration
├── eslint.config.mjs
├── .prettierrc.json
└── README.md
Standardized Handler Architecture
Every generated handler is structured as a clean, predictable pipeline. The entry file (index.mjs) contains zero business logic — it delegates processing across distinct layers and normalizes errors into standard HTTP status codes:
Request Event → Validator → Transformer → Helper Logic → Data Access (DAL)
Each layer raises explicit domain exceptions (ValidationError, TransformError, BusinessLayerError, DataLayerError, NotFoundError). The handler catches these and maps them into a uniform envelope:
{
"data": { ... },
"responseDetail": {
"status": "SUCCESS",
"statusCode": 200,
"message": "Operation completed successfully"
}
}
This guarantees that every API across your architecture reports errors in the exact same format, simplifying error parsing for frontends and monitoring tools.
Pre-Wired AWS SAM Infrastructure
The auto-generated template.yaml isn't a dummy file — it provides a complete AWS SAM stack:
-
Runtime defaults. Node.js 20 on
arm64, active AWS X-Ray tracing, shared Lambda dependency layers, and environment-aware timeouts. -
Environment mapping (
Mappings.StagesMap). Clean separation of deployment variables acrossdev,qa,ppe, andprodstages (VPC subnets, log retention, bucket names, and authentication provider ARNs). - AppSync integration. Provisioned GraphQL API with Lambda & API Key authorization, CloudWatch logs enabled, and automated S3 schema loading.
- Granular IAM roles. Pre-configured permissions scoped for DynamoDB, S3, SSM, Secrets Manager, EventBridge, X-Ray, and VPC access.
-
Automated resolver wiring. Naming a handler
get-ordersconfigures it as a GraphQL Query; naming it anything else (e.g.,create-order) automatically registers it as a Mutation, complete with generatedInputandResponsetypes in your schema.
S3 schema & VTL deployment workflow
To bypass template size limits and simplify deployment, GraphQL schemas and VTL files live inside the repository but are read directly from S3 during deployment.
The included build tool simplifies asset synchronization:
npm run deploy:assets -- --org my-org --env dev
This command automatically publishes schema.graphql, request.vtl, and response.vtl directly to s3://<org>-app-schema-bucket/<env>/<service>/.
Daily Developer Workflow
-
Bootstrapping a new service: run
npx create-lambda-graphql-app→npm install→sam build→ deploy. -
Adding a handler safely: run
npx create-lambda-graphql-app add cancel-order. It creates the folder hierarchy, appends SAM infrastructure blocks, and updatesschema.graphqlwithout disturbing your current codebase. -
Seamless onboarding: every generated service includes an explicit
README.mdwalking developers through local invocation, parameter customization, and deployment commands. -
Out-of-the-box debugging: pre-configured VS Code
launch.jsonfiles and mock event payloads let you debug handlers with breakpoints locally in seconds.
Getting Started
You don't need to install anything globally. Run it directly with npx:
# 1. Generate the service
npx create-lambda-graphql-app
# Prompt responses:
# ? Project name: my-service
# ? Organization name: my-org
# ? Primary handler name: get-orders (get-* maps to a Query)
# ? Add another handler: create-order (maps to a Mutation)
cd my-service
npm install
npm install --prefix layers/common-dependency
# 2. Build local SAM artifacts
npm run build
# 3. Deploy GraphQL schema and VTL templates to S3
npm run deploy:assets -- --org my-org --env dev
# 4. First-time guided SAM deployment
sam deploy --guided
# 5. Start local API Gateway / AppSync emulator
npm run local
Adding another handler down the line:
npx create-lambda-graphql-app add cancel-order
npm run build
npm run deploy:assets -- --org my-org --env dev
sam deploy
Roadmap & Next Steps
While the template covers most production needs out of the box, future releases will introduce:
- A non-interactive
--yesflag / configuration file for CI/CD automation pipelines. - Automated unit test scaffolding (
jest/vitest) per handler. - Pluggable database layer presets (DynamoDB Single-Table vs. Amazon RDS / Prisma).
If your team is still manually copying service directories to launch AppSync and Lambda APIs, give it a try:
npx create-lambda-graphql-app
GitHub repository & issues: d3vjamal/lambda-node-graphql-starter
Appendix: Generated Project README.md
Below is the standard README.md included inside generated repositories.
create-lambda-graphql-app
Scaffold a fully wired AWS AppSync + Lambda + SAM microservice in seconds with a single command.
Overview
create-lambda-graphql-app turns complex AWS AppSync + Lambda + SAM setup into a zero-friction CLI workflow. Stop copy-pasting old folders, broken relative imports, and out-of-sync template.yaml files.
Why use create-lambda-graphql-app?
- Zero configuration setup: get a production-ready, multi-stage AWS SAM project out of the box.
- Opinionated handler architecture: enforces a clean pipeline separation (Validator → Transformer → Helper → DAL) across all Lambda functions.
- Fully wired GraphQL + AppSync: schema definitions, VTL request/response mapping templates, AppSync data sources, and resolvers auto-generated in sync.
-
Local debugging out of the box: includes VS Code
launch.jsonconfigurations and event payload harnesses for instant local debugging. - Incremental scaffolding: easily add new handlers to an existing project at any time without breaking existing code or manual infrastructure configurations.
Quick start
No global installation required. Execute directly using npx:
# 1. Generate a new service
npx create-lambda-graphql-app
Interactive prompts:
? Project name: my-service
? Organization name: my-org
? Initial handler name: get-orders
? Add another handler? Yes
? Handler name: create-order
? Add another handler? No
Project setup & local build:
cd my-service
# Install dependencies for project & common layer
npm install
npm install --prefix layers/common-dependency
# Build local SAM artifacts
npm run build
# Deploy assets (GraphQL schema & VTL templates) to S3
npm run deploy:assets -- --org my-org --env dev
# Deploy to AWS via SAM CLI (first time setup)
sam deploy --guided
Directory structure
my-service/
├── handlers/
│ ├── get-orders/ # Auto-generated Query handler
│ │ ├── index.mjs # Entrypoint & exception mapping pipeline
│ │ ├── validators/ # Request validation logic (Joi)
│ │ ├── transformers/ # Input/output mapping logic
│ │ ├── helpers/ # Business logic
│ │ ├── dal/ # Data Access Layer (DynamoDB, S3, RDS)
│ │ ├── exceptions/ # Custom domain exceptions
│ │ └── constants/
│ └── create-order/ # Auto-generated Mutation handler
├── layers/
│ └── common-dependency/ # Shared dependencies Lambda Layer
├── local-test/
│ └── get-orders/ # Local test event & execution harness
├── mapping/
│ ├── request.vtl # AppSync request mapping template
│ └── response.vtl # AppSync response template + security headers
├── schema/
│ └── schema.graphql # Auto-generated GraphQL SDL
├── scripts/
│ └── deploy-assets.mjs # Syncs schema & VTL templates to S3
├── .vscode/
│ └── launch.json # Auto-generated VS Code debug configs
├── template.yaml # Complete AWS SAM infrastructure template
├── samconfig.toml # Default SAM deployment configuration
├── eslint.config.mjs # Flat ESLint config
├── .prettierrc.json # Prettier code formatting rules
└── README.md
CLI usage
1. Scaffold a new project
npx create-lambda-graphql-app [project-name]
2. Add a handler to an existing project
Run this command inside any project generated by create-lambda-graphql-app:
npx create-lambda-graphql-app add <handler-name>
What happens when you add a handler:
- Creates a new directory structure under
handlers/<handler-name>/. - Creates a local test payload runner under
local-test/<handler-name>/. - Injects the
Function,LogGroup,DataSource, andResolverintotemplate.yaml. - Updates
schema/schema.graphqlwith matching Query/Mutation types and Input/Response shapes. - Adds a debugging target to
.vscode/launch.json.
Handler conventions & architecture
Handlers follow a strict pipeline architecture:
GraphQL Event → Validator → Transformer → Helper → DAL
Naming conventions & schema inferences
The CLI automatically infers GraphQL operation types based on handler naming:
| Handler prefix / name | Inferred operation type | Generated GraphQL SDL |
|---|---|---|
get-*, list-*, fetch-*
|
Query | getOrders(input: GetOrdersInput!): GetOrdersResponse! |
Any other name (e.g. create-*, cancel-*) |
Mutation | createOrder(input: CreateOrderInput!): CreateOrderResponse! |
Standard response envelope
All handlers format their response into a standardized JSON payload structure:
{
"data": {
"orderId": "ord_12345",
"status": "PROCESSING"
},
"responseDetail": {
"status": "SUCCESS",
"statusCode": 200,
"message": "Order created successfully"
}
}
Available scripts
| Script | Command | Description |
|---|---|---|
build |
sam build |
Compiles SAM template and builds Lambda dependencies. |
deploy:assets |
node scripts/deploy-assets.mjs |
Uploads schema.graphql and VTL templates to S3. |
local |
sam local start-api |
Emulates API locally for testing. |
lint |
eslint . |
Runs static code analysis. |
format |
prettier --write . |
Auto-formats code across the codebase. |
Requirements
- Node.js: 18.x or higher
-
AWS SAM CLI: installed and configured (
aws configure) -
Docker: required if building dependencies natively with
sam build --use-container
Contributing
Contributions are welcome! Please feel free to open issues or submit pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a pull request
License
Distributed under the MIT License. See LICENSE for details.
Top comments (0)