@jet-cdk/afterburner
v0.3.4
Published
A set of functions that provide elegant, yet powerful apis for building a serverless app with AWS CDK, with a focus on streamlining lambda function integration.
Downloads
12
Readme
afterburner
Afterburner provides an elegant, powerful set of functional apis for building serverless apps with the AWS CDK, with a focus on streamlining lambda function usage. The apis are based around function composition, allowing you to mix and match the pieces in the way that you need, without losing any power or access to the properties you need.
It currently provides apis for: - Setting up routing for API Gateway V2 - Setting up resolvers for Appsync.
- Setting up lambda triggers on Cognito
Here's a sample of the the API Gateway API:
//Use provided tags to get going quickly, or construct your own
'/my': {
GET: node`lib/my-node-function.ts`,
//All languages with CDK constructs, have provided helpers: node (typescript included), python and go
POST: go`lib/go-function.ts`,
},
'/yours': {
//You can build your functions as you go
GET: lambda(go('lib/your-node-function.go')),
//Specify any of the construct's supported properties when you need more control
PUT: lambda(go({handler: 'lib/go-function.go', entry: 'doPut'}))
},
'/my/proxy': {
// All integrations are supported
GET: httpProxy({url: 'https://myproxy.com'}),
},
'/secure': {
//Authorizers supported. They just compose over integrations.
GET: userPool({userPoolClient: myClient, userPool: myPool}, myScopes)(
node`lib/my-secure-api.ts`
),
// You can provide your own handler. It's functions all the way own
POST: (scope, id)=>({integration: new MyIntegration(scope, id)})
}
It is part of the Jet-CDK project, a toolkit to simplify serverless app development. Although it pairs well with the Jet live environment, Afterburner can be used standalone.
API Gateway
The routing api allows you to reate mappings from paths, to methods, to a handler.
If you find you are using a certain configuration of lambda handler commonly, you can turn it into a tag with tagOf
, allowing you to keep your routing syntax clean.
Quick start
Be sure to install:
- @aws-cdk/aws-apigatewayv2
- @aws-cdk/aws-apigatewayv2-integrations
- @aws-cdk/aws-apigatewayv2-authorizers
- Any lambda handler packages, eg @aws-cdk/aws-lambda-nodejs
A minimal rest API :
import { HttpApi } from '@aws-cdk/aws-apigatewayv2';
import { route } from '@jet-cdk/afterburner/apigatewayv2';
import nodejs from '@jet-cdk/afterburner/apigatewayv2/prefab-tags/nodejs';
const api = new HttpApi(this, 'api');
const routes = route(api, {
'/my': {
GET: nodejs`lib/get-function.ts`,
POST: nodejs`lib/post-function.ts`
},
'/another-place': {
ANY: nodejs`lib/get-node-function.ts`,
}
}
The CDK nodejs handler defaults are intact, that means an export called handler
is used from each function file
More options
To specify a handler with your own settings add a couple of imports:
import lambda from '@jet-cdk/afterburner/apigatewayv2/integrations/lambda-proxy';
import nodeFn from '@jet-cdk/afterburner/functions/nodejs';
Now you can provide handlers with any configuration:
'/my': {
POST: lambda(nodeFn({
entry: 'lib/post-function.ts',
handler: 'handlePost',
runtime: Runtime.NODEJS_12_X
})
}
If you find that use a certain configuration repeatedly, you can turn it into a template tag:
import { route } from '@jet-cdk/afterburner/apigatewayv2';
import lambda from '@jet-cdk/afterburner/apigatewayv2/integrations/lambda-proxy';
import nodeFn from '@jet-cdk/afterburner/functions/nodejs';
import { tagOf } from '@jet-cdk/afterburner/lib';
const node12 = tagOf((filename) => lambda(nodeFn({
entry: filename,
runtime: Runtime.NODEJS_12_X
})
route(api, {
'/my': {
POST: node12`/lib/my-node12-handler.ts`
}
})
Accessing the functions
After creation, handlers are accesible by their mapping.
const routes = route(api, {
'/my': {
GET: nodejs`lib/get-function.ts`,
POST: nodejs`lib/post-function.ts`
},
'/another-place': {
ANY: nodejs`lib/get-node-function.ts`,
}
}
table.grantReadWriteData(
routes['/my'].POST.integration.handler.grantPrincipal
)
Other integrations
You will find all integrations and authorizers are supported:
import httpProxy from '@jet-cdk/afterburner/apigatewayv2/integrations/http-proxy';
GET: httpProxy({url: 'https://myproxy.com'}),
import userPool from '@jet-cdk/afterburner/apigatewayv2/authorizers/user-pool';
GET: userPool({userPoolClient: myClient, userPool: myPool})(
node`lib/my-secure-api.ts`
)
Full example
Here's an example of other ways to use the api
import { HttpApi } from '@aws-cdk/aws-apigatewayv2';
import { route } from '@jet-cdk/afterburner/apigatewayv2';
import lambda from '@jet-cdk/afterburner/apigatewayv2/integrations/lambda-proxy';
import httpProxy from '@jet-cdk/afterburner/apigatewayv2/integrations/http-proxy';
import userPool from '@jet-cdk/afterburner/apigatewayv2/authorizers/user-pool';
import nodejs from '@jet-cdk/afterburner/apigatewayv2/prefab-tags/nodejs';
import go from '@jet-cdk/afterburner/functions/go';
import nodeFn from '@jet-cdk/afterburner/functions/nodejs';
import compose from 'compose-function';
import * as cdk from '@aws-cdk/core';
import { CfnOutput } from '@aws-cdk/core';
export class TestAppStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
//Set up tags for common lambda handlers
const node = tagOf(filename=>lambda(nodejs(filename)))
//Use compose for a touch of elegance
const goFn = tagOf(compose(lambda, go))
//All properties are available
const dynanode = tagOf(filename=>lambda(nodejs({
entry: s,
runtime: Runtime.NODEJS_12_X,
environment: {
TABLE_NAME: 'my_table'}
}
)))
const api = new HttpApi(this, 'api');
const routes = route(api, {
//Use a tag
'/my': {
GET: node`lib/my-node-function.ts`,
POST: dynanode`lib/my-node-function.ts`
},
'/yours': {
//All built in languages are supported
POST: goFn`lib/go-function.ts`,
//Build your functions as you go
GET: lambda(go('lib/your-node-function.go')),
//Specify properties inline
PUT: lambda(go({handler: 'lib/go-function.go', entry: 'doPut'}))
},
'/my/proxy': {
// All integrations are supported
GET: httpProxy({url: 'https://myproxy.com'}),
// You can provide your own handler. Its functions all the way own
POST: (scope, id)=>({integration: new MyIntegration(scope, id)})
},
'/secure': {
GET: userPool({userPoolClient: myClient, userPool: myPool})(
node`lib/my-secure-api.ts`
)
}
});
//After creation, handlers are accesible by their mapping
table.grantReadWriteData(
routes['/my'].POST.integration.handler.grantPrincipal
)
new CfnOutput(this, 'apiUrl', { value: api.url ?? '' });
}
}
Appsync
Quick start
Make sure to install: Be sure to install:
- @aws-cdk/aws-appsyc
- Any lambda handler packages, eg @aws-cdk/aws-lambda-nodejs
The appsync api is very similar to the api gateway one:
import { GraphqlApi } from '@aws-cdk/aws-appsync';
import { setupResolvers } from '@jet-cdk/afterburner/appsync';
import nodejs from '@jet-cdk/afterburner/appsync/prefab-tags/nodejs';
const api = new GraphQLApi(this, 'api', {name: 'My Api'});
const dataSources = setupResolvers(api, {
Query: {
myQuery: nodejs`lib/my-query.ts`,
},
Mutation: {
doSomething: nodejs`lib/do-something.ts`,
}
});
import * as cdk from '@aws-cdk/core';
import * as appsync from '@aws-cdk/aws-appsync';
import { CfnOutput } from '@aws-cdk/core';
import { setupResolvers } from '@jet-cdk/afterburner/appsync';
import * as dynamodb from '@aws-cdk/aws-dynamodb';
import node from '@jet-cdk/afterburner/appsync/prefab-tags/nodejs';
import lambda from '@jet-cdk/afterburner/appsync/datasources/lambda';
import dynamoDS from '@jet-cdk/afterburner/appsync/datasources/dynamo';
import nodejs from '@jet-cdk/afterburner/functions/nodejs';
export class TestAppStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const table = new dynamodb.Table(this, 'Table', {
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }
});
const api = new appsync.GraphqlApi(this, 'api', { name: 'app' });
const dataSources = setupResolvers(api, {
resolvers: {
Query: {
myQuery: node`lib/myQuery.ts`,
putItem: resolver(lambda(nodejs('lib/putItem.ts'))),
editTable: resolver(dynamoDS(table), {
requestMappingTemplate: appsync.MappingTemplate.dynamoDbPutItem(
appsync.PrimaryKey.partition('id').auto(),
appsync.Values.projecting('input')
),
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultItem()
}),
},
Mutation: {
doTheThing: lambda(nodejs({entry: 'lib/myQuery.ts', handler: 'doTheThing'})),
}
},
});
table.grantReadWriteData(dataSources.Query.putItem.lambdaFunction.grantPrincipal);
new CfnOutput(this, 'apiUrl', { value: api.graphqlUrl ?? '' });
}
}
Cognito
Cognito accepts functions directly, therefore no prefabbed template tags are provided. Simply use the language functions around a string for a simple handler.
import { addTriggers } from "@jet-cdk/afterburner/cognito";
import nodejs from "@jet-cdk/afterburner/functions/nodejs";
const triggerFns = addTriggers(userPool, {
triggers: {
customMessage: nodejs("handlers/user/customMessage.ts")
postConfirmation: nodejs({
entry: "handlers/user/postConfirmation.ts",
environment: {
TABLE_NAME: userTable.tableName,
},
}),
},
});
userTable.grantFullAccess(triggerFns.postConfirmation);