laconia-core
🛡️ Laconia Core — Micro dependency injection framework.
An AWS Lambda handler function is a single entry point for both injecting dependencies
and function execution. In non-serverless development, you can and will normally
only focus on the latter. This brings a unique challenge to AWS Lambda development
as it is very difficult to test a handler function when it is responsible for doing
both the object creations and the application run. laconia-core is a simple dependency
injection framework for your Lambda code, hence solving this problem for you.
Laconia explicitly splits the responsibility of the object creations and Lambda function execution.
Laconia also provides a simple way for you to execute your Lambda function
so that your unit tests will not execute the code that instantiates your Lambda dependencies.
FAQ
Check out FAQ
Usage
Install laconia-core using yarn:
yarn add laconia-core
Or via npm:
npm install --save laconia-core
To fully understand how Laconia's Dependency Injection works, let's have a look into an
example below. This is not a running code as there are a lot of code that have been trimmed down,
full example can be found in the acceptance test: src
and unit test.
Lambda handler code:
const instances = ({ env }) => ({
orderRepository: new DynamoDbOrderRepository(env.ORDER_TABLE_NAME),
idGenerator: new UuidIdGenerator()
});
module.exports.handler = laconia(
async ({ event, orderRepository, idGenerator }) => {
await orderRepository.save(order);
}
).register(instances);
Unit test code:
const handler = require("../src/place-order").handler;
beforeEach(() => {
lc = {
orderRepository: {
save: jest.fn().mockReturnValue(Promise.resolve())
}
};
});
it("should store order to order table", async () => {
await handler.run(lc);
expect(lc.orderRepository.save).toBeCalledWith(
expect.objectContaining(order)
);
});
Note that as you have seen so far, Laconia is not aiming to become a comprehensive
DI framework hence the need of you handle the instantiation of all of the objects by yourself.
It should theoretically be possible to integrate Laconia to other more comprehensive
NodeJS DI framework but it has not been tested.
LaconiaContext
Laconia provides a one stop location to get all of the information you need for your Lambda
function execution via LaconiaContext
. In a nutshell, LaconiaContext is just an object that
contains all of those information by using object property keys, hence you can destructure it
to get just the information you need.
AWS Event and Context
When Laconia is adopted, the handler function signature will change. Without Laconia,
your handler signature would be event, context, callback
. With Laconia, your handler
signature would be laconiaContext
. The event
and context
are always available in LaconiaContext.
callback
is not made available as this should not be necessary anymore when you are
using Node 8, just return
the value that you want to return to the caller inside the handler function.
Example:
laconia(({ event, context }) => true);
Environment Variables
It is very common to set environment variables for your Lambda functions.
This is normally accessed via process.env
. Unit testing a Lambda function that
uses process.env
is awkward, as you have to modify the process
global variable and remember
to remove your change so that it doesn't affect other test.
For better unit testability, LaconiaContext contains the environment variables
with key env
.
Example:
laconia(({ env }) => true);
API
laconia(fn)
fn(laconiaContext)
- This
Function
is called when your Lambda is invoked - Will be called with
laconiaContext
object, which can be destructured to {event, context}
Example:
laconia(() => "value");
laconia(() => Promise.resolve("value"));
register(instanceFn)
Registers objects into LaconiaContext. Objects registered here will be made
available in the Lambda function execution.
instanceFn(laconiaContext)
- This
Function
is called when your Lambda is invoked - An object which contains the instances to be registered must be returned
Example:
laconia(({ service }) => service.call()).register(() => ({
service: new SomeService()
}));
run(laconiaContext)
Runs Lambda handler function with the specified laconiaContext
and bypasses
the LaconiaContext registration step. This function should only be used in a
unit test.
laconiaContext
- A plain object that should represent the LaconiaContext to be used in the function execution
laconia(({ service }) => service.call())
.register(() => ({
service: new SomeService()
}))
.run({
service: jest.mock()
});