cfn-forge
Version:
CloudFormation deployment automation tool with git-based workflows
376 lines (327 loc) • 10.3 kB
YAML
name: serverless-api
description: A serverless API template with Lambda functions and API Gateway
version: 1.0.0
author: CFN-Forge
cfnForgeVersion: 1.0.0
variables:
projectName:
prompt: "Project name"
default: "my-serverless-api"
description:
prompt: "Project description"
default: "A serverless API built with AWS Lambda and API Gateway"
lambdaRuntime:
prompt: "Lambda runtime"
default: "nodejs18.x"
options: ["nodejs18.x", "nodejs20.x", "python3.9", "python3.10", "python3.11"]
memorySize:
prompt: "Lambda memory size (MB)"
default: "128"
type: "number"
files:
include:
- "**/*"
exclude:
- "template.yaml"
- ".git/**"
- "node_modules/**"
structure:
cloudformation.yaml: |
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: {{description}}
Parameters:
Environment:
Type: String
Default: dev
AllowedValues: [dev, staging, prod]
Description: Environment name
Globals:
Function:
Timeout: 30
Runtime: {{lambdaRuntime}}
MemorySize: {{memorySize}}
Environment:
Variables:
NODE_ENV: !Ref Environment
LOG_LEVEL: INFO
Resources:
# API Gateway
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Environment
Cors:
AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
# Lambda Functions
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-hello-world"
CodeUri: src/handlers/hello-world/
Handler: index.handler
Events:
HelloWorldApi:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /hello
Method: GET
CreateItemFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-create-item"
CodeUri: src/handlers/create-item/
Handler: index.handler
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref ItemsTable
Events:
CreateItemApi:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /items
Method: POST
GetItemsFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-get-items"
CodeUri: src/handlers/get-items/
Handler: index.handler
Policies:
- DynamoDBReadPolicy:
TableName: !Ref ItemsTable
Events:
GetItemsApi:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /items
Method: GET
# DynamoDB Table
ItemsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-items"
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
Outputs:
ApiUrl:
Description: API Gateway URL
Value: !Sub "https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/${Environment}"
Export:
Name: !Sub "${AWS::StackName}-ApiUrl"
ItemsTableName:
Description: DynamoDB Items Table Name
Value: !Ref ItemsTable
Export:
Name: !Sub "${AWS::StackName}-ItemsTable"
.cfn-forge.yaml: |
project:
name: {{projectName}}
description: {{description}}
type: serverless-api
environments:
dev:
stackName: "{{projectName}}-dev"
template: cloudformation.yaml
region: us-east-1
parameters:
Environment: dev
staging:
stackName: "{{projectName}}-staging"
template: cloudformation.yaml
region: us-east-1
parameters:
Environment: staging
prod:
stackName: "{{projectName}}-prod"
template: cloudformation.yaml
region: us-east-1
parameters:
Environment: prod
deployment:
packagingBucket: "{{projectName}}-deployment-artifacts"
createBucket: true
git:
watchBranches: ["main", "develop"]
deployOnPush: true
package.json: |
{
"name": "{{projectName}}",
"version": "1.0.0",
"description": "{{description}}",
"main": "src/handlers/hello-world/index.js",
"scripts": {
"start": "sam local start-api",
"build": "sam build",
"deploy": "cfn-forge deploy",
"watch": "cfn-forge watch",
"validate": "cfn-forge validate",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/"
},
"dependencies": {
"aws-sdk": "^2.1490.0"
},
"devDependencies": {
"jest": "^29.0.0",
"eslint": "^8.0.0"
},
"keywords": ["serverless", "aws", "lambda", "api-gateway"],
"author": "{{author}}",
"license": "MIT"
}
src/handlers/hello-world/index.js: |
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event, null, 2));
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
message: 'Hello from {{projectName}}!',
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV
})
};
};
src/handlers/create-item/index.js: |
const AWS = require('aws-sdk');
const { v4: uuidv4 } = require('uuid');
const dynamodb = new AWS.DynamoDB.DocumentClient();
const tableName = process.env.ITEMS_TABLE || '{{projectName}}-dev-items';
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event, null, 2));
try {
const body = JSON.parse(event.body || '{}');
const item = {
id: uuidv4(),
...body,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
await dynamodb.put({
TableName: tableName,
Item: item
}).promise();
return {
statusCode: 201,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
message: 'Item created successfully',
item
})
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
message: 'Internal server error',
error: error.message
})
};
}
};
src/handlers/get-items/index.js: |
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();
const tableName = process.env.ITEMS_TABLE || '{{projectName}}-dev-items';
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event, null, 2));
try {
const result = await dynamodb.scan({
TableName: tableName
}).promise();
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
items: result.Items,
count: result.Count
})
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
message: 'Internal server error',
error: error.message
})
};
}
};
README.md: |
# {{projectName}}
{{description}}
## Quick Start
1. Install dependencies:
```bash
npm install
```
2. Deploy to AWS:
```bash
cfn-forge deploy
```
3. Start local development:
```bash
cfn-forge watch
```
## API Endpoints
- `GET /hello` - Hello world endpoint
- `GET /items` - Get all items
- `POST /items` - Create a new item
## Project Structure
```
{{projectName}}/
src/
handlers/
hello-world/
create-item/
get-items/
cloudformation.yaml
.cfn-forge.yaml
package.json
```
## Deployment
This project uses CFN-Forge for deployment. The configuration is in `.cfn-forge.yaml`.
### Environments
- **dev**: Development environment
- **staging**: Staging environment
- **prod**: Production environment
### Commands
- `cfn-forge deploy` - Deploy to the default environment
- `cfn-forge deploy --env prod` - Deploy to production
- `cfn-forge watch` - Watch for changes and auto-deploy
- `cfn-forge validate` - Validate CloudFormation template
hooks:
postInstall:
- type: npm
command: install