Effect friendly wrapper for AWS Lambda functions.
Disclaimer: This library is still in early development stage and the API is likely to change. Feedback is welcome.
Have been using lambda functions as a primary way to build serverless applications for a while now. Since I made the switch from fp-ts
to effect
, I wanted to use effects all the way when writing lambda functions, replacing the previous usage of middy
and fp-ts
. This library is an attempt to provide a functional way to write lambda functions using the effect
library. The library is inspired by the @effect/platform
library and aims to provide a similar experience for writing lambda functions.
Pros: The main approach of this library to use simple Effects as lambda handlers, allowing access at any point to the event and context allowing some really cool patterns.
Take a look at the following example:
import {
APIGatewayProxyEvent,
schemaBodyJson,
toLambdaHandler,
} from "effect-lambda/RestApi";
import { Effect, Console } from "effect";
import { Schema } from "@effect/schema";
const PayloadSchema = Schema.Struct({
message: Schema.String,
});
export const _handler = schemaBodyJson(PayloadSchema).pipe(
Effect.map((payload) => ({
statusCode: 200,
body: JSON.stringify({ message: payload.message }),
})),
Effect.catchTag("ParseError", () =>
Effect.succeed({
statusCode: 400,
body: "Bad Request",
}),
),
);
export const handler = _handler.pipe(toLambdaHandler);
// Or you can add a post processing middleware to the handler just by mapping over the effect
export const handlerWithMiddleware = _handler.pipe(
Effect.map((response) => ({
...response,
headers: { "Content-Type": "application/json" },
})),
toLambdaHandler,
);
// Or you can add a pre-processing middleware
export const handlerWithPreMiddleware = APIGatewayProxyEvent.pipe(
Effect.tap((event) => Console.log(`Received event: ${event}`)),
Effect.flatMap(() => _handler),
toLambdaHandler,
);
Cons:
This library has peer dependencies on @effect/schema
and effect
. You can install them via npm or pnpm or any other package manager you prefer.
# pnpm
pnpm add effect-lambda effect @effect/schema
# npm
npm install effect-lambda effect @effect/schema
Currently the library provides handlers for the following AWS Lambda triggers:
You can find TypeDocs for this package here.
// handler.ts
import { RestApi } from "effect-lambda";
import { Effect } from "effect";
import { Schema } from "@effect/schema";
export const handler = RestApi.toLambdaHandler(
Effect.succeed({
statusCode: 200,
body: JSON.stringify({ message: "Hello, World!" }),
}),
);
// Or access the payload and path parameters from the event
const PayloadSchema = Schema.Struct({
message: Schema.String,
});
const PathParamsSchema = Schema.Struct({
name: Schema.String,
});
export const handler = RestApi.toLambdaHandler(
RestApi.schemaPathParams(PathParamsSchema).pipe(
Effect.map(({ name }) => name),
Effect.bindTo("name"),
Effect.bind("message", () =>
RestApi.schemaBodyJson(PayloadSchema).pipe(Effect.map((x) => x.message)),
),
Effect.map(({ name, message }) => ({
statusCode: 200,
body: `Hello ${name}, ${message}`,
})),
Effect.catchTag("ParseError", () =>
Effect.succeed({
statusCode: 400,
body: "Invalid JSON",
}),
),
),
);
You can use helmet to secure your application using the provided applyMiddleware utility.
import { applyMiddleware, RestApi } from "effect-lambda";
import helmet from "helmet";
import { Effect, pipe } from "effect";
const toHandler = (effect: Parameters<typeof RestApi.toLambdaHandler>[0]) =>
pipe(effect, Effect.map(applyMiddleware(helmet())), RestApi.toLambdaHandler);
export const handler = Effect.succeed({
statusCode: 200,
body: JSON.stringify({ message: "Hello, World!" }),
}).pipe(toHandler);
import { SQSEvent, toLambdaHandler } from "effect-lambda/Sqs";
import { Effect } from "effect";
export const handler = toLambdaHandler(
SQSEvent.pipe(
Effect.map((event) => {
// Do something with the event
}),
),
);
import { SNSEvent, toLambdaHandler } from "effect-lambda/Sns";
import { Effect } from "effect";
export const handler = toLambdaHandler(
SQSEvent.pipe(
Effect.map((event) => {
// Do something with the event
}),
),
);
// handler.ts
import { toLambdaHandler } from "effect-lambda/DynamoDb";
import { Effect } from "effect";
export const handler = toLambdaHandler(
Effect.map((event) => {
event.Records.forEach((record) => {
// Process each record
console.log("DynamoDB Record: %j", record);
});
}),
);
This handler allows you to process DynamoDB stream events in a functional way using the effect-lambda
library. You can access each record in the stream and apply your business logic accordingly.
@effect/schema
- Peer dependency of this library for schemas@effect/platform-node
- Fully effect native library for network requests, file system, etc.Effect friendly wrapper for AWS Lambdas