2.0.0-beta • Published 8 years ago

lambda6 v2.0.0-beta

Weekly downloads
1
License
MIT
Repository
github
Last release
8 years ago

lambda6

Build Status Code Climate Test Coverage npm version

A simple ES6+ handler architecture for AWS Lambda with opinionated defaults. The base class, Handler can be subclassed to provide a structured framework for handling AWS Lambda events. Handler matches an event operationKey to a method name by default, and extracts an argument for that method from the event using the payloadKey. Both of these keys can be changed by passing in an options hash to the constructor.

see .babelrc for precisely what ES6/7 features are used.

Table of Contents

Installing

npm install --save lambda6

Design Goals

  • ES6+ transpiled by Babel
  • Limited dependencies
  • Easy dispatching of events based on event structure, with sane defaults
  • Minimalist and intuitive request lifecycle
  • Promise-oriented using the venerable Bluebird library

Documentation

JavaScript API docs are generated using ESDoc during the build process. If any documentation is inaccurate or missing, please create an issue here on GitHub.

Getting Started

The fastest way to get started is to use the lambda6 Yeoman generator to generate a new lambda6 project.

npm install -g yo generator-lambda6
mkdir lambda-6-sample && cd $_
yo lambda6
gulp lambda

The last line above will deploy your function to AWS Lambda using the settings configured by the Yeoman generator. Check out the README for more detailed instructions. After you've generated that project, you can refer to the section below for more details on how to structure your handler methods.

Usage

1. Create a subclass of Handler

Within your subclass, create methods for each operation. Register these methods using the @operation decorator.

import { Handler, operation } from 'lambda6'

class HelloHandler extends Handler {

  @operation
  greet({ greeting, title, name }) {
    const fullName = `${title} ${name}`;
    return `${this.operation}: ${greeting}, ${fullName}`;
    // returns "greet: hello Mr. Bond"
  }

}

2. Organize your Lambda request operations

By default, lambda6 expects to receive an event with the following own properties set using the keys below:

Options KeyRequiredDefaultDescription
operationKeyYes"operation"the operation/method to invoke
payloadKeyYes"payload"method argument/payload

The Handler class uses operation to find an appropriate method to handle the request and then passes the payload to that method. The dispatched method can optionally retrieve the same data from this.event.payload, so the method argument is there for convenience. Deep gets (like lodash's _.get()) for key names are not yet supported.

Here's an example of calling the HelloHandler class with a Lambda event:

{
  "operation": "greet",
  "payload": {
    "name": "Bond",
    "title": "Mr.",
    "greeting": "hello"
  }
}

This allows you to create a thin dispatch layer between the event received and the method that will process the request. In the case above, greet would be invoked within a newly-created this context with the following characteristics:

  • prototype set to the Handler subclass of which greet is member
  • metadata, operation, event and context immutable properties of this

3. Export and expose the handler function to AWS Lambda

export function handler(event, context) {
  // passing in the default operationKey as an example
  return new MyHandler({ operationKey: 'operation' }).handle(event, context);
}

Dependencies

lambda6 has a few runtime dependencies, most of which provide an ES6+ environment in AWS Lambda. They are:

This appears to be the minimal set of dependencies to allow ES6 code to run well in AWS Lambda.

Building

This project uses gulp to perform the necessary build steps. The default build step (which transpiles the ES6 code to lib) can be invoked like so:

gulp

Tests can be run without generating the transpiled code by running:

gulp test

Pay close attention to the ESLint settings in .eslintrc, which are quite strict, so that you don't get tripped up if you start changing code.

Contributing

Pull requests are welcome and encouraged. Please take a look at .eslintrc to see what the coding standards are that are enforced as part of our build system. That way, when you create a pull request, the build will pass. Also, please provide a rationale for your change and make sure it fits within the Design Goals.

Future Improvements

The eventual goal is to leverage the declarative features of ES6 (decorators, getters, etc.) to more concisely describe a service. To that effect, here are some ideas in the pipeline.

  • Handler introspection and enumeration of operations
  • Use deep gets for operation and payload keys, see _.get()
  • Dispatch endpoint methods with multiple arguments from the event
  • Make @operation more sophisticated and attach pre/post events (i.e. auth)
  • Allow other endpoint types (like Handler), for example:
class UserHandler extends Handler {

  // Handle with a literal value
  @operation
  get version() { '1.0.1' }

  // Handle with a literal error value (calls context.fail())
  @operation
  get errorOp() { Error('not yet implemented') }

  // Handle with a function
  @operation
  me({ id }) {
    return db.getProfile(id);
  }

  // Handle with a downstream chained Handler
  @operation
  get ['groups/{groupId}']: { GroupHandler }

}