micro-aws-lambda
v5.3.3
Published
<img src='https://github.com/Albert-Gao/micro-aws-lambda/blob/master/logo.png?raw=true' maxWidth="100%" height='auto' />
Readme
Micro AWS Lambda
Intro
- For Lambda Proxy / Http API mode
- Written in Typescript
- Zero runtime dependencies
- Tiny: 4.7KB after minified
- Rapid middlewares
- simple reasoning, just running one by one
- early exit with
throworreturnanything - pass values among middlewares
- Return response
- an object, it will be converted to a Lambda compatible response
- a customizable
httpResponse()/success()(200) - a customizable
httpError()/badRequest()(400) /internalError()(500) - or string, number, boolean
- Easy debug:
- Adding debug info to response object
- console.log event / context
Why do you build this lib
AWS Lambda is making it a flash to creating an API endpoint. But that's just the infrastructure part. It doesn't mean your business logic can be simplified.
- I need a middleware setup to decouple my business logic without installing a lib that has many dependencies and result in a bigger bundle size as well.
- I want to deal with a simple interface, where the order is just one by one. I don't want to deal with a mental model where a middleware will be invoked twice for both stages, and handle both the
beforeandafterstage in one function.
What problems does it solve
Middleware is for decoupling logic. I learned the value of beforeHooks and afterHooks after adopting Feathers.JS. Which has a beautiful concept of 3 layers for every endpoint, and I found myself start the declarative programming for the backend. No more code for the repeating work. In micro-aws-lambda's context, it is just an array of Middleware.
Let's say a simple return-a-user endpoint, what does it look like when you are using micro-aws-lambda
const handler = lambdas([
validateRequestBody(GetUserSchema),
isStillEmployed,
verifyPaymentStatus,
justReturnUserObjectDirectlyFromDB,
removeFieldsFromResponse('password', 'address'),
combineUserNames,
transformResponseToClientSideStructure,
]);Ideally, you can just compose your future lambda without writing any code except for an integration test. The logic will be declarative. Every middleware here can be fully tested and ready to reuse.
Usage
1. Install
npm install micro-aws-lambda
2. Quick start
import { lambdas } from 'micro-aws-lambda';
const handler = lambdas([() => ({ message: 'it works' })]);
// call the API, you will get json response: { message: "it works" }3. The usage of Typescript
import { lambdas, Middleware, HttpResponse } from 'micro-aws-lambda';
interface Shared {
user: { id: string; group: string };
}
interface Response {
isPassing: boolean;
}
const extractUserFromEvent: Middleware<Shared, Response> = async ({
event,
shared,
}) => {
const user = JSON.parse(event.body);
if (!user) {
throw HttpResponse.badRequest({ isPassing: false });
}
shared.user = user;
};
const parseUserData: Middleware<Shared, Response> = ({ shared }) => {
if (shared.user.id === 'bad-user-id') {
throw HttpResponse.badRequest({ isPassing: false });
}
return HttpResponse.success({ isPassing: true });
};
export const handler = lambdas([extractUserFromEvent, parseUserData]);And later on, if there are any lambda handler needs that extractUserFromEvent, you just reuse that piece anywhere you want!
the default
Middlewareis forAPIGatewayProxyHandlerV2from@types/aws-lambda. If you are using the Lambda Proxy mode, please importMiddlewareLegacy, otherwise,
4. Two minutes master
How to control the flow?
returnanything that is notnullorundefinedwill STOP the executionthrowwill STOP the execution- just
return/return null/return undefinedwill NOT stop the execution, unless it's the last middleware - if nothing is returning after invoking the last middleware, an empty object will be returned
- otherwise, the array of
Middlewarewill just be executed one by one - who returns the 1st wins, for example,
lambdas([m1, m2]), ifm1is returning something, it will be used as the http response and m2 will not be executed.
What can you
return- a
HttpResponse.response() - or a
HttpResponse.success()(just aHttpResponse.response()with status code set to 200, you can still change it) - or an plain object / string / number (which will be auto-wrapped with
HttpResponse.success()in the end)
- a
What can you
throwHttpResponse.error()HttpResponse.badRequest()HttpResponse.unauthorized()HttpResponse.forbidden()HttpResponse.notFound()HttpResponse.methodNotAllowed()HttpResponse.notAcceptable()HttpResponse.conflict()HttpResponse.internalError()HttpResponse.notImplemented()HttpResponse.badGateway()HttpResponse.serviceUnavailable()HttpResponse.gatewayTimeout()HttpResponse.networkAuthenticationRequire()- or anything else
Hover over the function will get a tooltip of the status code for this helper, also, you can pass a 2nd parameter to change the statusCode or headers as well
How to pass something down the chain,
- use
sharedfrom the parameter - attach your value to it:
shared.myValue = 123,myValuecould be any name
- use
Do I have to return something in the middleware
- No. For example, a validation middleware can only react to the wrong data without returning anything like
if (wrong) {throw badRequest()}
- No. For example, a validation middleware can only react to the wrong data without returning anything like
5. Actually, you can throw or return anything
returna plainobject|string|number=== (200) responsethrowa plainobject|string|number=== (400) response- custom status code by adding
statusCodeproperty - Or use our built-in shortcut,
import {HttpResponse} from 'micro-aws-lambda', thenHttpResponse.success({body:{message:'wow'}})
6. Config
6.1 addTraceInfoToResponse
It will add debug info into the response object
{
debug: {
endpoint: "",
requestBody: "",
requestMethod: "",
country: "",
lambdaRequestId: "",
logStreamName: "",
logGroupName: "",
apiGatewayId: ""
}
}6.2 logRequestInfo
It will console.log:
eventcontextAws-Api-Gateway-Request-IdIdentity-Source-Ip
7. Migrating from v4
passDownObjhas renamed tosharedreturnSTOPS the execution now, likethrow, makes the flow easier to reason about!- all
httphelpers can be used underHttpResponse, just import this one alone
Credits
- The initial version is heavily inspired by my favourite REST framework: Feathers.JS
- This project was bootstrapped with TSDX.
