npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hattip/adapter-aws-lambda

v0.0.49

Published

AWS Lambda adapter for Hattip

Readme

@hattip/adapter-aws-lambda

Hattip adapter for AWS Lambda.

Usage

Assuming you have your Hattip handler defined in handler.js, create an entry file like the following and use @hattip/bundler-aws-lambda or another tool to bundle it:

import awsLambdaAdapter from "@hattip/adapter-aws-lambda";
// or import from "@hattip/adapter-aws-lambda/streaming" for streaming responses
import hattipHandler from "./handler.js";

export const handler = awsLambdaAdapter(hattipHandler);

If you want to use streaming responses, you should set the InvokeMode of your Lambda function to RESPONSE_STREAM.

Serving static files

For simple use cases, you can use the @hattip/static package along with @hattip/walk to serve static files. Alternatively, you can use @hattip/walk at build time to generate a list of files to be served to reduce cold start times.

import awsLambdaAdapter from "@hattip/adapter-aws-lambda/streaming";
import hattipHandler from "./handler.js";
import { walk } from "@hattip/walk";
import { createStaticMiddleware } from "@hattip/static";
import { createFileReader } from "@hattip/static/fs";

const root = new URL("./public", import.meta.url);
const files = walk(root);
const staticMiddleware = createStaticMiddleware(files, createFileReader(root), {
  gzip: true,
});

export const handler = awsLambdaAdapter((ctx) => {
  return staticMiddleware(ctx) || hattipHandler(ctx);
});

Note that in this setup, static files are served from the lambda function itself. For serious production use cases, you might want to consider using a CDN like CloudFront instead.

Another option is to use an edge runtime like Cloudflare Workers or Deno Deploy in front of your Lambda function. This will give you a multicloud setup similar to Vercel's and Netlify's respectively.

Deploying

You can use the AWS Console, the AWS CLI, infrastructure-as-code tools like AWS CDK, Terraform, or Pulumi to deploy your Lambda function. A very simple example using the AWS SDK v3 is shown below.

Assuming your AWS CLI is configured correctly and you saved this file as aws.js, you can run node aws.js deploy to deploy the function and node deploy.js destroy to destroy it.

It bundles the app starting from entry-aws.js into dist/aws-lambda.zip and deploys it as a Lambda function and prints the function URL that you can use to invoke it. Make sure to change InvokeMode to RESPONSE_STREAM if you want to use streaming responses.

Note that this example is for demonstration purposes only. A full devops solution is beyond the scope of this readme.

// @ts-check
import { IAM, NoSuchEntityException } from "@aws-sdk/client-iam";
import {
  Lambda,
  ResourceNotFoundException,
  ResourceConflictException,
  InvalidParameterValueException,
} from "@aws-sdk/client-lambda";
import {
  CloudWatchLogs,
  ResourceNotFoundException as LogsResourceNotFoundException,
  ResourceAlreadyExistsException as LogsResourceAlreadyExistsException,
} from "@aws-sdk/client-cloudwatch-logs";
import fs from "node:fs";
import { bundle } from "@hattip/bundler-aws-lambda";

const PREFIX = "hattip";
const LAMBDA_EXECUTION_ROLE_NAME = `${PREFIX}-lambda-execution-role`;
const FUNCTION_NAME = `${PREFIX}-function`;

async function deploy() {
  await bundle({
    input: "entry-aws.js",
    output: "dist/aws-lambda.zip",
    copy: ["public"],
    zip: true,
  });

  const iam = new IAM();

  let role = await iam
    .getRole({ RoleName: LAMBDA_EXECUTION_ROLE_NAME })
    .catch((error) => {
      if (error instanceof NoSuchEntityException) {
        return null;
      }
      throw error;
    });

  if (!role) {
    console.log("Creating Lambda Execution Role", LAMBDA_EXECUTION_ROLE_NAME);
    role = await iam.createRole({
      RoleName: LAMBDA_EXECUTION_ROLE_NAME,
      AssumeRolePolicyDocument: JSON.stringify({
        Version: "2012-10-17",
        Statement: [
          {
            Effect: "Allow",
            Principal: {
              Service: "lambda.amazonaws.com",
            },
            Action: "sts:AssumeRole",
          },
        ],
      }),
    });
  }

  await iam.attachRolePolicy({
    RoleName: LAMBDA_EXECUTION_ROLE_NAME,
    PolicyArn:
      "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
  });

  if (!role.Role) {
    throw new Error("Role not found");
  }

  const lambda = new Lambda();

  let fn = await lambda
    .getFunction({ FunctionName: FUNCTION_NAME })
    .catch((error) => {
      if (error instanceof ResourceNotFoundException) {
        return null;
      }
      throw error;
    });

  const zipFile = fs.readFileSync("dist/aws-lambda.zip");

  if (fn) {
    console.log("Updating Lambda Function", FUNCTION_NAME);
    await lambda.updateFunctionCode({
      FunctionName: FUNCTION_NAME,
      ZipFile: zipFile,
    });
  } else {
    console.log("Creating Lambda Function", FUNCTION_NAME);

    while (!fn) {
      fn = await lambda
        .createFunction({
          FunctionName: FUNCTION_NAME,
          Role: role.Role.Arn,
          Runtime: "nodejs20.x",
          Handler: "index.handler",
          Code: { ZipFile: zipFile },
          LoggingConfig: {},
        })
        .catch((error) => {
          if (
            error instanceof InvalidParameterValueException &&
            error.message ===
              "The role defined for the function cannot be assumed by Lambda."
          ) {
            // Ignore this error until the role propagates
            return null;
          }
          throw error;
        });
    }
  }

  /** @type import("@aws-sdk/client-lambda").CreateFunctionUrlConfigCommandOutput | import("@aws-sdk/client-lambda").GetFunctionUrlConfigCommandOutput | null */
  let url = await lambda
    .getFunctionUrlConfig({ FunctionName: FUNCTION_NAME })
    .catch((error) => {
      if (error instanceof ResourceNotFoundException) {
        return null;
      }
      throw error;
    });

  if (!url) {
    console.log("Creating Lambda Function URL", FUNCTION_NAME);
    url = await lambda.createFunctionUrlConfig({
      FunctionName: FUNCTION_NAME,
      InvokeMode: "BUFFERED",
      AuthType: "NONE",
    });
  }

  await lambda
    .addPermission({
      FunctionName: FUNCTION_NAME,
      Action: "lambda:InvokeFunctionUrl",
      Principal: "*",
      StatementId: PREFIX + "-permission-statement",
      FunctionUrlAuthType: "NONE",
    })
    .catch((error) => {
      if (error instanceof ResourceConflictException) {
        return null;
      }

      throw error;
    });

  const cloudwatch = new CloudWatchLogs();

  await cloudwatch
    .createLogGroup({
      logGroupName: `/aws/lambda/${FUNCTION_NAME}`,
    })
    .catch((error) => {
      if (error instanceof LogsResourceAlreadyExistsException) {
        return null;
      }

      throw error;
    });

  await cloudwatch.putRetentionPolicy({
    logGroupName: `/aws/lambda/${FUNCTION_NAME}`,
    retentionInDays: 7,
  });

  console.log(url.FunctionUrl);
}

async function destroy() {
  const lambda = new Lambda();

  const lambdaResult = await lambda
    .deleteFunction({ FunctionName: FUNCTION_NAME })
    .catch((error) => {
      if (error instanceof ResourceNotFoundException) {
        return null;
      }
      throw error;
    });

  if (lambdaResult) {
    console.log("Deleted Lambda Function", FUNCTION_NAME);
  }

  const cloudwatch = new CloudWatchLogs();
  const logResult = await cloudwatch
    .deleteLogGroup({
      logGroupName: `/aws/lambda/${FUNCTION_NAME}`,
    })
    .catch((error) => {
      if (error instanceof LogsResourceNotFoundException) {
        return null;
      }
      throw error;
    });

  if (logResult) {
    console.log("Deleted CloudWatch Log Group", `/aws/lambda/${FUNCTION_NAME}`);
  }

  const iam = new IAM();

  await iam
    .detachRolePolicy({
      RoleName: LAMBDA_EXECUTION_ROLE_NAME,
      PolicyArn:
        "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
    })
    .catch((error) => {
      if (error instanceof NoSuchEntityException) {
        return null;
      }
      throw error;
    });

  const iamResult = await iam
    .deleteRole({ RoleName: LAMBDA_EXECUTION_ROLE_NAME })
    .catch((error) => {
      if (error instanceof NoSuchEntityException) {
        return null;
      }
      throw error;
    });

  if (iamResult) {
    console.log("Deleted Lambda Execution Role", LAMBDA_EXECUTION_ROLE_NAME);
  }
}

if (process.argv[2] === "destroy") {
  destroy().catch((error) => {
    console.error(error);
    process.exit(1);
  });
} else if (process.argv[2] === "deploy") {
  deploy().catch((error) => {
    console.error(error);
    process.exit(1);
  });
} else {
  console.error("Must specify deploy or destroy");
  process.exit(1);
}