lambdamiddleware

package module
v0.0.0-...-3a6a385 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 20, 2021 License: MIT Imports: 2 Imported by: 0

README

aws-lambda-middleware

This Golang package provides helpers to construct AWS Lambda middlewares. It works very similarly to HTTP Middlewares you would find on your favourite HTTP server library.

Features

  • Only use the AWS Lambda SDK interfaces, no reflection, no type casting
  • Composable, you can compose Middlewares together into a Middleware Chain

Usage

go get -u github.com/easyCZ/aws-lambda-middleware

Full example available in ./middleware_example_test.go.

import (
    "context"
    "log"
    
    "github.com/easyCZ/aws-lambda-middleware"
    
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-lambda-go/lambdacontext"
)

type HandlerPayload struct {
    Value string `json:"value"`
}


func ExampleMiddleware() {
	logger := log.Default()
	myLambdaHandler := func(ctx context.Context, payload HandlerPayload) error {
		logger.Printf("Handling lambda invocation for payload: %v\n", payload)

		return nil
	}

	handler := lambda.NewHandler(myLambdaHandler)
	handler = lambdamiddleware.Chain(handler, []lambdamiddleware.Middleware{
		LoggingMiddleware(logger),
		ContextExtendMiddleware,
	}...)

	// start your lambda
	lambda.StartHandler(handler)
}

// LoggingMiddleware is a constructor, it allows us to inject dependencies into this middleware.
func LoggingMiddleware(logger *log.Logger) lambdamiddleware.Middleware {
	// We return a Middleware which has access to our logger in a closure
	return func(next lambda.Handler) lambda.Handler {

		// Middleware must return a handler. We use the HandlerFunc helper to write a handler as a single function.
		return lambdamiddleware.HandlerFunc(func(ctx context.Context, bytes []byte) ([]byte, error) {
			// Extract invocation information from the context. This is populated from the lambda runtime and the internal lambda.Handler implementation.
			lctx, _ := lambdacontext.FromContext(ctx)

			// Print the request body, and the request ID.
			// You may want to attach this as structured fields to your *smart* logger.
			logger.Printf("Handing lambda invocation. Request ID: %s. Body: %s\n", lctx.AwsRequestID, string(bytes))

			// Trigger the next middleware in the chain. This may be the actual handler, or another Middleware.
			response, err := next.Invoke(ctx, bytes)

			// We have access to the response, we can choose to log it.
			if err != nil {
				logger.Printf("Finished lambda invocation with error: %s. Request ID: %s. Response: %s.", err.Error(), lctx.AwsRequestID, string(response))
			} else {
				logger.Printf("Finished lambda invocation with OK. Request ID: %s. Response: %s.", lctx.AwsRequestID, string(response))
			}

			// We return the response and the error back to the Middleware before us, or to the AWS Runtime.
			return response, err
		})
	}
}

// ContextExtendMiddleware is a middleware which extends the context with a value
// ContextExtendMiddleware is an example where we do not use a constructor to contruct the middleware, instead we implement the Middleware type directly.
func ContextExtendMiddleware(next lambda.Handler) lambda.Handler {
	// We return a HandlerFunc to get access to the request context.
	return lambdamiddleware.HandlerFunc(func(ctx context.Context, bytes []byte) ([]byte, error) {
		// Attach a custom value to the context.
		ctx = context.WithValue(ctx, "my_key", "my_value")

		// Directly invoke the next handler, without capturing the response and error - we do not need them in this case.
		return next.Invoke(ctx, bytes)
	})
}

Contributions

Contributions welcome. Please raise a Pull Request.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Chain

func Chain(handler lambda.Handler, middlewares ...Middleware) lambda.Handler

Chain creates a middleware chain. Middlewares are executed in the order specified

Types

type HandlerFunc

type HandlerFunc func(context.Context, []byte) ([]byte, error)

HandlerFunc is a helper to define a lambda.Handler as a function

func (HandlerFunc) Invoke

func (f HandlerFunc) Invoke(ctx context.Context, payload []byte) ([]byte, error)

type Middleware

type Middleware func(next lambda.Handler) lambda.Handler

Middleware represents a Middleware for a lambda.Handler

Example
package main

import (
	"context"
	"log"

	"github.com/easyCZ/aws-lambda-middleware"

	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-lambda-go/lambdacontext"
)

type HandlerPayload struct {
	Value string `json:"value"`
}

func main() {
	logger := log.Default()
	myLambdaHandler := func(ctx context.Context, payload HandlerPayload) error {
		logger.Printf("Handling lambda invocation for payload: %v\n", payload)

		return nil
	}

	handler := lambda.NewHandler(myLambdaHandler)
	handler = lambdamiddleware.Chain(handler, []lambdamiddleware.Middleware{
		LoggingMiddleware(logger),
		ContextExtendMiddleware,
	}...)

	// start your lambda
	lambda.StartHandler(handler)
}

// LoggingMiddleware is a constructor, it allows us to inject dependencies into this middleware.
func LoggingMiddleware(logger *log.Logger) lambdamiddleware.Middleware {
	// We return a Middleware which has access to our logger in a closure
	return func(next lambda.Handler) lambda.Handler {

		// Middleware must return a handler. We use the HandlerFunc helper to write a handler as a single function.
		return lambdamiddleware.HandlerFunc(func(ctx context.Context, bytes []byte) ([]byte, error) {
			// Extract invocation information from the context. This is populated from the lambda runtime and the internal lambda.Handler implementation.
			lctx, _ := lambdacontext.FromContext(ctx)

			// Print the request body, and the request ID.
			// You may want to attach this as structured fields to your *smart* logger.
			logger.Printf("Handing lambda invocation. Request ID: %s. Body: %s\n", lctx.AwsRequestID, string(bytes))

			// Trigger the next middleware in the chain. This may be the actual handler, or another Middleware.
			response, err := next.Invoke(ctx, bytes)

			// We have access to the response, we can choose to log it.
			if err != nil {
				logger.Printf("Finished lambda invocation with error: %s. Request ID: %s. Response: %s.", err.Error(), lctx.AwsRequestID, string(response))
			} else {
				logger.Printf("Finished lambda invocation with OK. Request ID: %s. Response: %s.", lctx.AwsRequestID, string(response))
			}

			// We return the response and the error back to the Middleware before us, or to the AWS Runtime.
			return response, err
		})
	}
}

// ContextExtendMiddleware is a middleware which extends the context with a value
// ContextExtendMiddleware is an example where we do not use a constructor to contruct the middleware, instead we implement the Middleware type directly.
func ContextExtendMiddleware(next lambda.Handler) lambda.Handler {
	// We return a HandlerFunc to get access to the request context.
	return lambdamiddleware.HandlerFunc(func(ctx context.Context, bytes []byte) ([]byte, error) {
		// Attach a custom value to the context.
		ctx = context.WithValue(ctx, "my_key", "my_value")

		// Directly invoke the next handler, without capturing the response and error - we do not need them in this case.
		return next.Invoke(ctx, bytes)
	})
}
Output:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL