@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 },
);
}
defaultErrorHandler()
β
The default error handler from Backpack. Handles ProblemError
, RequestValidationError
and defines a default "catch-all" result.
Problem
β
Represents the body of an RFC-9457 Problem compliant error response.
ProblemError
β
An error with a Problem
associated to it, intended to be used for error responses.
BadRequestProblem
β
A generic ProblemError
that represents a Bad Request (400) HTTP error.
UnauthorizedProblem
β
A generic ProblemError
that represents an Unauthorized (401) HTTP error.
ForbiddenProblem
β
A generic ProblemError
that represents a Forbidden (403) HTTP error.
NotFoundProblem
β
A generic ProblemError
that represents a Not Found (404) HTTP error.
InternalServerErrorProblem
β
A generic ProblemError
that represents an Internal Server Error (500) HTTP error.
NotImplementedProblem
β
A generic ProblemError
that represents a Not Implemented (501) HTTP error.
ServiceUnavailableProblem
β
A generic ProblemError
that represents a Service Unavailable (503) HTTP error.
problemErrorConverter()
β
Converts ProblemError
into a result. Included in defaultErrorHandler()
.
requestValidationErrorConverter()
β
Converts RequestValidationError
into a result. Included in defaultErrorHandler()
.