Skip to content

Simplify working with AWS API Gateway Lambda handlers

License

Notifications You must be signed in to change notification settings

JSainsburyPLC/g8

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

g8

Provides the following utilities to simplify working with AWS lambda and API Gateway.

  • Simple handler interface
  • HTTP request parsing with JSON support and request body validation
  • HTTP response writer with JSON support
  • Custom error type with JSON support
  • Logging unhandled errors with a stack trace
  • Correlation ID
  • New Relic integration

Request body parsing

Use the bind method to unmarshal the response body to a struct

type requestBody struct {
	Name   string `json:"name"`
}

handler := func(c *g8.APIGatewayProxyContext) error {
	var b requestBody
	err := c.Bind(&b)
	if err != nil {
		return err
	}

	...

	c.JSON(http.StatusOK, responseBody)
}

Request body validation

Implement the Validate method on the struct

type requestBody struct {
	Name   string `json:"name"`
	Status string `json:"status"`
}

func (b body) Validate() error {
	if b.Status == "" {
		return errors.New("status empty")
	}
	return nil
}

handler := func(c *g8.APIGatewayProxyContext) error {
	var b requestBody
	err := c.Bind(&b)
	if err != nil {
		// validation error would be returned here
		return err
	}
	
	...
	
	c.JSON(http.StatusOK, responseBody)
}

API Gateway Lambda Authorizer Handlers

You are able to define handlers for Lambda Authorizer (previously known as custom authorizers) using g8. Here is an example:

handler := g8.APIGatewayCustomAuthorizerHandlerWithNewRelic(
    func(c *APIGatewayCustomAuthorizerContext) error{
        c.Response.SetPrincipalID("some-principal-ID")

        c.Response.AllowAllMethods()
        // other examples:
        // c.Response.DenyAllMethods()
        // c.Response.AllowMethod(Post, "/pets/*")
        return nil
    },
    g8.HandlerConfig{
        ...
    },
)

lambda.StartHandler(handler)

Response writing

There are several methods provided to simplify writing HTTP responses.

handler := func(c *g8.APIGatewayProxyContext) error {
    ...
    c.JSON(http.StatusOK, responseBody)
}

Errors

Go Errors

Returning Go errors to the error response writer will log the error and respond with an internal server error

handler := func(c *g8.APIGatewayProxyContext) error {
	...
	return errors.New("something went wrong")
}

Custom Errors

You can return custom g8 errors and also map them to HTTP status codes

handler := func(c *g8.APIGatewayProxyContext) error {
    ...
    return g8.Err{
        Status: http.StatusBadRequest,
        Code:   "SOME_CLIENT_ERROR",
        Detail: "Invalid param",
    }
}

Writes the following response, with status code 400

{
    "code": "SOME_CLIENT_ERROR",
    "detail": "Invalid param"
}

Logging stack traces

Unhandled errors are logged automatically with a stack trace if the error is wrapped by eris.

The new version of eris handles errors differently and produce a different JSON response compared to the previous version, additionally all errors now produce a JSON response, please fully test these changes.

eris.Wrapf(err, "failed to send offers to user id: %v", userID)

HTTP Adaptor

In order to facilitate the serving of HTTP and the development of g8.APIGatewayProxyHandler lambdas, engineers can utilise the NewHTTPHandler function. This function is particularly beneficial for local development and pact provider testing, as it assists in verifying that an API provider is adhering to the pacts established by its clients. By using this feature, engineers can ensure that their API is functioning correctly and meeting the necessary expectations of its users.

Example

g8.NewHTTPHandler(LambdaHandlerEndpoints{
    g8.LambdaHandler{
        Handler:     func(c *g8.APIGatewayProxyContext) error {
            c.JSON(http.StatusOK, "success")
            return nil
        },
        Method:      http.MethodGet,
        PathPattern: "/full/url/path/{var1}/{var2}",
    },
    g8.LambdaHandler{
        Handler:     func(c *g8.APIGatewayProxyContext) error {
            return errors.New("some error")
        },
        Method:      http.MethodPost,
        PathPattern: "/another/full/url/path/{var1}",
    },
}, 8080)

Requirements

  • Go 1.19+