Skip to content

Commit 0b890fb

Browse files
authored
docs(examples): add example for AWS SAM (#674)
1 parent 95cccab commit 0b890fb

13 files changed

+876
-0
lines changed

Diff for: .gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,7 @@ site
3939

4040
# Generated API documentation (from TypeDoc)
4141
/api
42+
43+
# SAM Example copies files
44+
/examples/sam/src/handlers/*
45+
!/examples/sam/src/handlers/COPY_LAMBDA_FUNCTIONS_HERE

Diff for: examples/lambda-functions/get-all-items.ts

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
2+
import { Metrics } from '@aws-lambda-powertools/metrics';
3+
import { Logger } from '@aws-lambda-powertools/logger';
4+
import { Tracer } from '@aws-lambda-powertools/tracer';
5+
import { DocumentClient } from 'aws-sdk/clients/dynamodb';
6+
7+
// Create the PowerTools clients
8+
const metrics = new Metrics();
9+
const logger = new Logger();
10+
const tracer = new Tracer();
11+
12+
// Create DynamoDB DocumentClient and patch it for tracing
13+
const docClient = tracer.captureAWSClient(new DocumentClient());
14+
15+
// Get the DynamoDB table name from environment variables
16+
const tableName = process.env.SAMPLE_TABLE;
17+
18+
/**
19+
*
20+
* Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
21+
* @param {Object} event - API Gateway Lambda Proxy Input Format
22+
*
23+
* Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
24+
* @returns {Object} object - API Gateway Lambda Proxy Output Format
25+
*
26+
*/
27+
export const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> => {
28+
if (event.httpMethod !== 'GET') {
29+
throw new Error(`getAllItems only accepts GET method, you tried: ${event.httpMethod}`);
30+
}
31+
32+
// Tracer: Get facade segment created by AWS Lambda
33+
const segment = tracer.getSegment();
34+
35+
// Tracer: Create subsegment for the function & set it as active
36+
const handlerSegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
37+
tracer.setSegment(handlerSegment);
38+
39+
// Tracer: Annotate the subsegment with the cold start & serviceName
40+
tracer.annotateColdStart();
41+
tracer.addServiceNameAnnotation();
42+
43+
// Tracer: Add annotation for the awsRequestId
44+
tracer.putAnnotation('awsRequestId', context.awsRequestId);
45+
46+
// Metrics: Capture cold start metrics
47+
metrics.captureColdStartMetric();
48+
49+
// Logger: Add persistent attributes to each log statement
50+
logger.addPersistentLogAttributes({
51+
awsRequestId: context.awsRequestId,
52+
});
53+
54+
// get all items from the table (only first 1MB data, you can use `LastEvaluatedKey` to get the rest of data)
55+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property
56+
// https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html
57+
let response;
58+
try {
59+
if (!tableName) {
60+
throw new Error('SAMPLE_TABLE environment variable is not set');
61+
}
62+
63+
const data = await docClient.scan({
64+
TableName: tableName
65+
}).promise();
66+
const items = data.Items;
67+
68+
// Logger: All log statements are written to CloudWatch
69+
logger.debug(`retrieved items: ${items?.length || 0}`);
70+
71+
response = {
72+
statusCode: 200,
73+
body: JSON.stringify(items)
74+
};
75+
} catch (err) {
76+
tracer.addErrorAsMetadata(err as Error);
77+
logger.error('Error reading from table. ' + err);
78+
response = {
79+
statusCode: 500,
80+
body: JSON.stringify({ 'error': 'Error reading from table.' })
81+
};
82+
}
83+
84+
// Tracer: Close subsegment (the AWS Lambda one is closed automatically)
85+
handlerSegment.close(); // (## index.handler)
86+
87+
// Tracer: Set the facade segment as active again (the one created by AWS Lambda)
88+
tracer.setSegment(segment);
89+
90+
// All log statements are written to CloudWatch
91+
logger.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`);
92+
93+
return response;
94+
};

Diff for: examples/lambda-functions/get-by-id.ts

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
2+
import { Metrics } from '@aws-lambda-powertools/metrics';
3+
import { Logger } from '@aws-lambda-powertools/logger';
4+
import { Tracer } from '@aws-lambda-powertools/tracer';
5+
import { DocumentClient } from 'aws-sdk/clients/dynamodb';
6+
7+
// Create the PowerTools clients
8+
const metrics = new Metrics();
9+
const logger = new Logger();
10+
const tracer = new Tracer();
11+
12+
// Create DynamoDB DocumentClient and patch it for tracing
13+
const docClient = tracer.captureAWSClient(new DocumentClient());
14+
15+
// Get the DynamoDB table name from environment variables
16+
const tableName = process.env.SAMPLE_TABLE;
17+
18+
/**
19+
*
20+
* Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
21+
* @param {Object} event - API Gateway Lambda Proxy Input Format
22+
*
23+
* Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
24+
* @returns {Object} object - API Gateway Lambda Proxy Output Format
25+
*
26+
*/
27+
28+
export const getByIdHandler = async (event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> => {
29+
if (event.httpMethod !== 'GET') {
30+
throw new Error(`getById only accepts GET method, you tried: ${event.httpMethod}`);
31+
}
32+
// Tracer: Get facade segment created by AWS Lambda
33+
const segment = tracer.getSegment();
34+
35+
// Tracer: Create subsegment for the function & set it as active
36+
const handlerSegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
37+
tracer.setSegment(handlerSegment);
38+
39+
// Tracer: Annotate the subsegment with the cold start & serviceName
40+
tracer.annotateColdStart();
41+
tracer.addServiceNameAnnotation();
42+
43+
// Tracer: Add annotation for the awsRequestId
44+
tracer.putAnnotation('awsRequestId', context.awsRequestId);
45+
46+
// Metrics: Capture cold start metrics
47+
metrics.captureColdStartMetric();
48+
49+
// Logger: Add persistent attributes to each log statement
50+
logger.addPersistentLogAttributes({
51+
awsRequestId: context.awsRequestId,
52+
});
53+
54+
// Get the item from the table
55+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#get-property
56+
let response;
57+
try {
58+
if (!tableName) {
59+
throw new Error('SAMPLE_TABLE environment variable is not set');
60+
}
61+
if (!event.pathParameters) {
62+
throw new Error('event does not contain pathParameters')
63+
}
64+
if (!event.pathParameters.id) {
65+
throw new Error('PathParameter id is missing')
66+
}
67+
68+
const data = await docClient.get({
69+
TableName: tableName,
70+
Key: { id: event.pathParameters.id },
71+
}).promise();
72+
const item = data.Item;
73+
response = {
74+
statusCode: 200,
75+
body: JSON.stringify(item)
76+
};
77+
} catch (err) {
78+
tracer.addErrorAsMetadata(err as Error);
79+
logger.error('Error reading from table. ' + err);
80+
response = {
81+
statusCode: 500,
82+
body: JSON.stringify({ 'error': 'Error reading from table.' })
83+
};
84+
}
85+
86+
// Tracer: Close subsegment (the AWS Lambda one is closed automatically)
87+
handlerSegment.close(); // (## index.handler)
88+
89+
// Tracer: Set the facade segment as active again (the one created by AWS Lambda)
90+
tracer.setSegment(segment);
91+
92+
// All log statements are written to CloudWatch
93+
logger.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`);
94+
95+
return response;
96+
};

Diff for: examples/lambda-functions/put-item.ts

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
2+
import { Metrics } from '@aws-lambda-powertools/metrics';
3+
import { Logger } from '@aws-lambda-powertools/logger';
4+
import { Tracer } from '@aws-lambda-powertools/tracer';
5+
import { DocumentClient } from 'aws-sdk/clients/dynamodb';
6+
7+
// Create the PowerTools clients
8+
const metrics = new Metrics();
9+
const logger = new Logger();
10+
const tracer = new Tracer();
11+
12+
// Create DynamoDB DocumentClient and patch it for tracing
13+
const docClient = tracer.captureAWSClient(new DocumentClient());
14+
15+
// Get the DynamoDB table name from environment variables
16+
const tableName = process.env.SAMPLE_TABLE;
17+
18+
/**
19+
*
20+
* Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
21+
* @param {Object} event - API Gateway Lambda Proxy Input Format
22+
*
23+
* Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
24+
* @returns {Object} object - API Gateway Lambda Proxy Output Format
25+
*
26+
*/
27+
28+
export const putItemHandler = async (event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> => {
29+
if (event.httpMethod !== 'POST') {
30+
throw new Error(`putItem only accepts POST method, you tried: ${event.httpMethod}`);
31+
}
32+
// Tracer: Get facade segment created by AWS Lambda
33+
const segment = tracer.getSegment();
34+
35+
// Tracer: Create subsegment for the function & set it as active
36+
const handlerSegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
37+
tracer.setSegment(handlerSegment);
38+
39+
// Tracer: Annotate the subsegment with the cold start & serviceName
40+
tracer.annotateColdStart();
41+
tracer.addServiceNameAnnotation();
42+
43+
// Tracer: Add annotation for the awsRequestId
44+
tracer.putAnnotation('awsRequestId', context.awsRequestId);
45+
46+
// Metrics: Capture cold start metrics
47+
metrics.captureColdStartMetric();
48+
49+
// Logger: Add persistent attributes to each log statement
50+
logger.addPersistentLogAttributes({
51+
awsRequestId: context.awsRequestId,
52+
});
53+
54+
// Creates a new item, or replaces an old item with a new item
55+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#put-property
56+
let response;
57+
try {
58+
if (!tableName) {
59+
throw new Error('SAMPLE_TABLE environment variable is not set');
60+
}
61+
if (!event.body) {
62+
throw new Error('Event does not contain body')
63+
}
64+
65+
// Get id and name from the body of the request
66+
const body = JSON.parse(event.body);
67+
const id = body.id;
68+
const name = body.name;
69+
70+
await docClient.put({
71+
TableName: tableName,
72+
Item: { id: id, name: name }
73+
}).promise();
74+
response = {
75+
statusCode: 200,
76+
body: JSON.stringify(body)
77+
};
78+
} catch (err) {
79+
tracer.addErrorAsMetadata(err as Error);
80+
logger.error('Error writing data to table. ' + err);
81+
response = {
82+
statusCode: 500,
83+
body: JSON.stringify({ 'error': 'Error writing data to table.' })
84+
};
85+
}
86+
87+
// Tracer: Close subsegment (the AWS Lambda one is closed automatically)
88+
handlerSegment.close(); // (## index.handler)
89+
90+
// Tracer: Set the facade segment as active again (the one created by AWS Lambda)
91+
tracer.setSegment(segment);
92+
93+
// All log statements are written to CloudWatch
94+
logger.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`);
95+
96+
return response;
97+
};

Diff for: examples/lambda-functions/tsconfig.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"compilerOptions": {
3+
"experimentalDecorators": true,
4+
"noImplicitAny": true,
5+
"target": "ES2020",
6+
"module": "commonjs",
7+
"declaration": true,
8+
"declarationMap": true,
9+
"outDir": "lib",
10+
"removeComments": false,
11+
"strict": true,
12+
"inlineSourceMap": true,
13+
"moduleResolution": "node",
14+
"resolveJsonModule": true,
15+
"pretty": true,
16+
"esModuleInterop": true
17+
},
18+
"exclude": [ "./node_modules"],
19+
"watchOptions": {
20+
"watchFile": "useFsEvents",
21+
"watchDirectory": "useFsEvents",
22+
"fallbackPolling": "dynamicPriority"
23+
},
24+
"lib": [ "es2020" ],
25+
"types": [
26+
"node"
27+
]
28+
}

0 commit comments

Comments
 (0)