DEV Community

Cover image for I Built an npm Package That Scaffolds AWS AppSync, Lambda & GraphQL Services — Files, Infra, and All
Jamal
Jamal

Posted on

I Built an npm Package That Scaffolds AWS AppSync, Lambda & GraphQL Services — Files, Infra, and All

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
Enter fullscreen mode Exit fullscreen mode

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 to validation/, 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 Request errors.
  • 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
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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 across dev, qa, ppe, and prod stages (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-orders configures it as a GraphQL Query; naming it anything else (e.g., create-order) automatically registers it as a Mutation, complete with generated Input and Response types 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
Enter fullscreen mode Exit fullscreen mode

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-appnpm installsam 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 updates schema.graphql without disturbing your current codebase.
  • Seamless onboarding: every generated service includes an explicit README.md walking developers through local invocation, parameter customization, and deployment commands.
  • Out-of-the-box debugging: pre-configured VS Code launch.json files 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Roadmap & Next Steps

While the template covers most production needs out of the box, future releases will introduce:

  • A non-interactive --yes flag / 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
Enter fullscreen mode Exit fullscreen mode

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.

npm version
license
node version

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.json configurations 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

CLI usage

1. Scaffold a new project

npx create-lambda-graphql-app [project-name]
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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, and Resolver into template.yaml.
  • Updates schema/schema.graphql with 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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to branch (git push origin feature/amazing-feature)
  5. Open a pull request

License

Distributed under the MIT License. See LICENSE for details.

Top comments (0)