Serverless boilerplate based on serverless-webpack + typescript. Define ready to deploy project with predefined Β scripts, linter-prettier rules, basic lib and helpers. Based on pseudo Onion Architecture: lambda[controller] -> (services + helpers)[[domain layer]] -> repositories.
In order to dig deeper in onion architecture check this boilerplate: https://github.com/Melzar/onion-architecture-boilerplate
- Usage π¨βπ»/π©βπ»
- Plugins π
- File structure π
- Scripts π
- How to deploy π
- Folders purpose π
$: npm i -g serverless # install serverless framework
$: sls create --template https://github.com/collierrgbsitisfise/serverless-bonk-template --path <dir name>
- serverless-webpack
- serverless-offline
- serverless-dotenv-plugin
- serverless-prune-plugin
- serverless-iam-roles-per-function
.
βββ resources # resources such as VPC, DynamoDB Tables etc.
βββ scripts # will be used in CI/CD, development process - should not be part of production bundle.
βββ schemas # schemas to validate API request on API gateway level.
βββ @mocks # mocks which will be used in tests/development.
βββ @types # types.
βββ env # env files.
βββ lib # helpers to operate with lambdas itself, should not be used inside lambda, should not operate somehow with business logic.
βββ apiGatewayLambdaWrapper.ts # wrap lambdas which are linked to api gateway.
βββ cloudWatchLambdaWrapper.ts # wrap lambdas which are subscribed to cloud watch event.
βββ snsLambdaWrapper.ts # wrap lambdas which are subscribed to sns message.
βββ sqsLambdaWrapper.ts # wrap lambdas which are subscribed to sqs message.
βββ dynamoDBStreamLambdaWrapper.ts # wrap lambdas which are subscribed to dynamoDB stream.
βββ src
β βββ functions # lambda fucntions.
β β βββ example
β β β βββ example.ts # `Example` lambda source code.
β β β βββ example.yaml # `Example` function template part.
β β β
β β βββ index.ts # import/export all lambdas.
β β
β βββ helpers # helpers which are used inside src folder.
β βββ services # services logic which will operate with external API/repositories, will contain domain logic.
β βββ repositories # operate with database.
β
βββ package.json
βββ serverless.ts # serverless service file.
βββ tsconfig.json # typescript compiler configuration
βββ tsconfig.paths.json # typescript paths
βββ webpack.config.js # webpack configuration
βββ .eslintrc.js # ESlint config
βββ .prettierrc.js # prettier config
Command | Script |
---|---|
Lint | npm run lint |
Prettier | npm run prettier |
Typescript check | npm run ts-check |
Test | npm run test |
Setup env | ENV=<envValue> npm run setup # will create .env on root level |
-
Run next commands
$: ENV=<envValue> npm run setup # setup env file
$: npm run deploy # deploy
On the libs
folder are defined utils which will operate with lambda itself. The libs utils should't be used inside handler. In boilerplate are predefined lambda wrappers for base case scenario lambda use:
- lambda tied to dynamodb stream
- lambda tied to sns
- lambda tied to sqs
- lambda tied to cloudwatch event
- lambda tied to ApiGateway
Wrapper for lambda tied to dynamoDB stream
import { DynamoDBStreamEvent, Context, Callback } from 'aws-lambda';
export const dynamoDblambdaWrapper = (
lambda: (event: DynamoDBStreamEvent, context: Context, callback: Callback) => Promise<any>,
onSucces: (event: DynamoDBStreamEvent, result: any) => any | PromiseLike<any>,
onError: (event: DynamoDBStreamEvent, error: Error) => any | PromiseLike<any>,
) => {
return function wrapp(event: DynamoDBStreamEvent, context?: Context, callback?: Callback): Promise<any> {
return Promise.resolve()
.then(() => lambda(event, context, callback))
.then((res: any) => onSucces(event, res))
.catch((err: Error) => onError(event, err));
};
};
Wrapper for lambda tied to ApiGateway
export type Headers = { [key: string]: string };
export type LambdaFunction = (
event: APIGatewayEvent,
context?: Context,
callback?: Callback,
) => [any, number, Headers] | Promise<[any, number, Headers]>;
export type OnSuccesHandler = (
value: any,
statusCode: number,
headers?: Headers,
) => APIGatewayProxyResult | PromiseLike<APIGatewayProxyResult>;
export type OnErrorHandle = (error: Error) => Promise<APIGatewayProxyResult>;
const defaultHeaders = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
};
const onSuccesHandler = (
data: any,
statusCode: number,
headers?: Headers,
): APIGatewayProxyResult | PromiseLike<APIGatewayProxyResult> => ({
statusCode,
headers: {
...defaultHeaders,
...headers,
},
body: JSON.stringify(data),
});
const onErrorHandler = async (error: Error): Promise<APIGatewayProxyResult> => {
return {
statusCode: 500,
headers: defaultHeaders,
body: JSON.stringify(error),
};
};
export const apiGatewayLambdaWrapper = (
lambda: LambdaFunction,
onSucces: OnSuccesHandler = onSuccesHandler,
onError: OnErrorHandle = onErrorHandler,
) => {
return function wrapp(event: APIGatewayEvent, context: Context, callback: Callback): Promise<APIGatewayProxyResult> {
return Promise.resolve()
.then(() => lambda(event, context, callback))
.then(([res, code, headers]: [any, number, Headers]) => onSucces(res, code, headers))
.catch(onError);
};
};
Some raw data(example of sns message, api request, sqs message, etc) which could be used during local development or for test.
general types, which will be used across project..
For each environment should be predefined <ENV>.env
file, which will be used by setup-script
before deploy.
Should't contain sensitive info such as secrets , db passwords, etc. Such kind of info must be retrived from secret-manager in runtime
Define resources which will be created/updated on deploy, such as dynamodb table, SqlRDSInstance, etc.
Define request schemas by which ApiGateway will validate request. Also could be defined response schemas. All of them could be used in test or/and for documentation
.js
files which usually are used in CI/CD. Also it could be used in development purpose.
Scripts examples example:
- setup .env variables
- setup development webhooks using ngrok
- adding additional
.env
variables on CI/CD - purge cloudfront cache
Internal logic of application services, repository, helpers.
There is an article which describes one of the predefined helpers