API reference / @backpack/aws-lambda
@backpack/aws-lambda
️ 👩💻
A package containing all sorts of utilities for writing AWS Lambda functions.
Installing
First, ensure you have set up Nexus as private registry needed to use Backpack.
Then, install the package using npm:
npm install @backpack/aws-lambda
defineLambda()
helpers
Helps to define correct AWS Lambda function handlers, by enabling type inference and enforcing the correct type definitions for your specific Lambda integration.
For example:
import { defineRestLambda } from "@backpack/aws-lambda";
export const handler = defineRestLambda(async (event, context) => {
// ...
return { statusCode: 200, body: "OK" };
// ^ should return a `APIGatewayProxyResult`
});
The following helper functions are currently provided by Backpack:
Helper function | Lambda integration |
---|---|
defineLambda() | (none or irrelevant) |
defineRestLambda() | API Gateway (REST) |
defineSnsLambda() | SNS |
defineSqsLambda() | SQS |
defineS3Lambda() | S3 |
defineS3BatchLambda() | S3 Batch |
REST event utilities
If your AWS Lambda integrates with API Gateway for REST APIs, you can use the following utilities for your APIGatewayProxyEvent
event.
parseHeaders()
Parses request headers. Provides type-completion, performs normalization, and includes some validation utilities.
import { APIGatewayProxyResult, APIGatewayProxyEvent } from "aws-lambda";
import { z } from "zod";
import { defineRestLambda, parseHeaders, jsonOk } from "@backpack/aws-lambda/rest";
const handler = defineRestLambda(async (event) => {
const headers = parseHeaders(event);
// returns an optional header by default:
const callerId = headers.get("X-Caller-Id");
// specifying `required: true` will always return a `string`,
// and throws a `RequestValidationError` if not present
const contentType = headers.get("Content-Type", { required: true });
// specifying `multi: true` will return an array instead
const acceptHeaders = headers.get("Accept", { multi: true });
// specifying the `validated` option, it will validate the value against a Zod schema,
// and throws a `RequestValidationError` if invalid
const Locale = z.enum(["nl", "en"]);
const locale = headers.get("Accept-Language", { validated: Locale });
return jsonOk("OK")
})
parsePathParameters()
and parseQueryParameters()
Both helper functions improve type-safety and include some validation utilities for parsing parameters.
import { APIGatewayProxyResult } from "aws-lambda";
import { z } from "zod";
import {
defineRestLambda,
parsePathParameters,
parseQueryParameters,
jsonOk,
} from "@backpack/aws-lambda/rest";
const handler = defineRestLambda(async (event) => {
const pathParameters = parsePathParameters(event);
const queryParameters = parseQueryParameters(event);
// returns an required header by default,
// and throws a `RequestValidationError` if not present
const artistId = pathParameters.get("artistId");
// specifying `required: false` will return a `string | undefined` instead
const departmentId = pathParameters.get("departmentId", { required: false });
// specifying the `validated` option, it will validate the value against a Zod schema,
// and throws a `RequestValidationError` if invalid
const SortDirection = z.enum(["asc", "desc"]);
const sortDirection = queryParameters.get("sort", { validated: SortDirection });
return jsonOk("OK")
});
parseBody()
Parses a request body. Supports both plain text and JSON, and provides built-in validation utilities.
import { APIGatewayProxyResult } from "aws-lambda";
import { z } from "zod";
import { defineRestLambda, parseBody, jsonOk } from "@backpack/aws-lambda/rest";
const handler = defineRestLambda(async (event) => {
const body = parseBody(event);
const text = body.text();
const json = body.json();
// you can also type-cast a JSON object (unsafe)
const unsafeJson = body.json<MySchema>();
// you can also validate your JSON against a Zod schema (safe)
// throws a `RequestValidationError` if invalid
const MySchema = z.object({ foo: z.string(), bar: z.string().optional() })
const safeJson = body.json({ validated: MySchema });
return jsonOk("OK")
});
type MySchema = {
foo: string;
bar?: string;
}
parseRequest()
- all of the above
The function parseRequest()
returns an object that include all utilities mentioned above:
getHeader()
getQueryParameter()
getPathParameter()
getBody()
REST result utilities
If your AWS Lambda integrates with API Gateway for REST APIs, you can use the following utilities for returning an APIGatewayProxyResult
object.
result()
This function can be used to wrap your result object, adding additional type-completion.
For example: it hints to use the HttpStatus
enum from Backpack:
it also adds type-completion for HTTP headers:
jsonResult()
Creates a result with a JSON body. Accepts any object, and converts it to JSON. It also sets the Content-Type
header to application/json
.
import { defineRestLambda, jsonResult, HttpStatus } from "@backpack/aws-lambda/rest";
export const handler = defineRestLambda(async () => {
// ...
return jsonResult({ /* ... */ }, { statusCode: HttpStatus.OK });
});
jsonOk()
Same as jsonResult()
, but with the status code set to HTTP 200 (OK).
import { defineRestLambda, jsonOk } from "@backpack/aws-lambda/rest";
export const handler = defineRestLambda(async () => {
// ...
return jsonOk({ /* ... */ });
});
problemResult()
Creates an RFC-9457 compliant problem result, to return an explicitly documented error.
import {
defineRestLambda,
jsonOk,
problemResult,
RequestValidationError,
} from "@backpack/aws-lambda/rest";
import { AsyncResult } from "@backpack/error-handling/async-result";
export const handler = defineRestLambda(() =>
AsyncResult
.try(() => {
/* ... */
})
.map((it) => jsonOk(it))
.recoverIfInstanceOf(RequestValidationError, () =>
problemResult({
type: "/problems/validation-error",
title: "Validation error",
status: 400,
}),
)
);
Error handling
Combining the error handling utilities from @backpack/error-handling
with the default error handler from @backpack/aws-lambda/rest
allows you to easily set up proper error handling for REST endpoints.
The example below demonstrates three ways to set it up:
- Using the
@catchErrors()
decorator - Using
AsyncResult<T>
- Using
Promise<T>
import type { APIGatewayProxyResult } from "aws-lambda";
import { catchErrors, onFailure, recoverIfInstanceOf, orElseGet } from "@backpack/error-handling/promises";
import { jsonOk, jsonResult, defaultErrorHandler, ProblemError } from "@backpack/aws-lambda/rest";
export class MyLambda {
@catchErrors(
onFailure(myErrorLogger()),
recoverIfInstanceOf(MyCustomError, myErrorConverter()),
orElseGet(defaultErrorHandler()),
)
public async handle(): Promise<APIGatewayProxyResult> {
await this.someOperation();
return jsonOk("Done!");
}
private async someOperation(): Promise<void> {
// ...
}
}
class MyCustomError extends Error { /* ... */ }
function myErrorLogger() {
return async (error: unknown) => console.error(error);
}
function myErrorConverter() {
return (error: MyCustomError) =>
jsonResult(
{ message: `My custom error ${error.message}!` },
{ statusCode: 500 },
);
}
import type { APIGatewayProxyResult } from "aws-lambda";
import { AsyncResult } from "@backpack/error-handling/async-result";
import { jsonOk, jsonResult, defaultErrorHandler, ProblemError } from "@backpack/aws-lambda/rest";
export class MyLambda {
public handle(): Promise<APIGatewayProxyResult> {
return AsyncResult
.try(() => this.someOperation())
.map(() => jsonOk("Done!"))
.onFailure(myErrorLogger())
.recoverIfInstanceOf(MyCustomError, myErrorConverter())
.orElseGet(defaultErrorHandler());
}
private async someOperation(): Promise<void> {
// ...
throw new ProblemError({
type: "/problems/foo-not-found",
status: 404,
title: "Foo not found!",
detail: "Foo 123 could not be found!",
});
// ...
}
}
class MyCustomError extends Error { /* ... */ }
function myErrorLogger() {
return (error: unknown) => console.error(error);
}
function myErrorConverter() {
return (error: MyCustomError) =>
jsonResult(
{ message: `My custom error ${error.message}!` },
{ statusCode: 500 },
);
}
import type { APIGatewayProxyResult } from "aws-lambda";
import { promiseTry, map, onFailure, recoverIfInstanceOf } from "@backpack/error-handling/promises";
import { jsonOk, jsonResult, defaultErrorHandler, ProblemError } from "@backpack/aws-lambda/rest";
export class MyLambda {
public handle(): Promise<APIGatewayProxyResult> {
return promiseTry(() => this.someOperation())
.then(map(() => jsonOk("Done!")))
.catch(onFailure(myErrorLogger()))
.catch(recoverIfInstanceOf(MyCustomError, myErrorConverter()))
.catch(defaultErrorHandler());
}
private async someOperation(): Promise<void> {
// ...
throw new ProblemError({
type: "/problems/foo-not-found",
status: 404,
title: "Foo not found!",
detail: "Foo 123 could not be found!",
});
// ...
}
}
class MyCustomError extends Error { /* ... */ }
function myErrorLogger() {
return (error: unknown) => console.error(error);
}
function myErrorConverter() {
return (error: MyCustomError) =>
jsonResult(
{ message: `My custom error ${error.message}!` },
{ statusCode: 500 },
);
}
Check the full API reference for more information.