Compact AWS Client for Modern JS
aws4fetch is a JavaScript library designed for signing AWS requests using the AWS Signature Version 4 process. It provides a compact client (6.4kb minified, 2.5kb gzipped) tailored for modern JavaScript environments that natively support the Web `fetch` API and `SubtleCrypto`. This makes it particularly well-suited for serverless edge computing platforms like Cloudflare Workers, as well as modern web browsers. The library is currently at version `1.0.20` and maintains an active release cadence, frequently adding support for new AWS features and refining existing functionality. A key differentiator is its built-in exponential backoff with full jitter retry strategy, enhancing the reliability of AWS interactions. Unlike traditional Node.js-centric AWS SDKs, `aws4fetch` leverages browser-native APIs, prioritizing minimalism and performance in environments where those APIs are readily available.
Common errors
-
ReferenceError: fetch is not defined
cause `aws4fetch` was executed in a JavaScript environment (e.g., Node.js older than v18) that does not natively provide the `fetch` global API.fixFor Node.js v18+, `fetch` is global. For older Node.js versions, install a `fetch` polyfill (e.g., `npm install node-fetch`) and ensure it's loaded before `aws4fetch` is used: `import fetch from 'node-fetch'; global.fetch = fetch;`. -
ReferenceError: SubtleCrypto is not defined
cause `aws4fetch` was executed in an environment lacking the `SubtleCrypto` global API, which is essential for cryptographic operations like signing.fixIn Node.js, you might need a Web Crypto API polyfill. Consider libraries like `@peculiar/webcrypto` if your Node.js version doesn't provide it globally or if you're in a specific environment that omits it. -
TypeError: (0 , aws4fetch__WEBPACK_IMPORTED_MODULE_0__.AwsClient) is not a constructor
cause This error typically occurs when a bundler or runtime environment fails to correctly resolve the `AwsClient` export, often due to misinterpretation of `package.json#exports` or using `require()` with an ESM-only package.fixEnsure your bundler (e.g., Webpack, Rollup, esbuild) is up-to-date and configured to handle ESM and `package.json#exports` correctly. Always use `import { AwsClient } from 'aws4fetch'` and avoid `require()` where possible. -
TS2307: Cannot find module 'aws4fetch' or its corresponding type declarations.
cause TypeScript cannot locate the type definitions for the `aws4fetch` package, or there's a mismatch between the installed package version and TypeScript compiler settings.fixVerify `aws4fetch` is correctly installed. Ensure your `tsconfig.json` has `"moduleResolution": "bundler"` or `"node16"` (or appropriate for modern ESM) and that your TypeScript version is compatible (v1.0.18 fixed type issues for newer TS). Reinstall `@types/aws4fetch` if it exists, though `aws4fetch` ships its own types.
Warnings
- gotcha The library relies on the global `fetch` API and `SubtleCrypto`. It is primarily designed for environments like modern web browsers and Cloudflare Workers. Running in Node.js without polyfills for these APIs will result in errors.
- breaking Version 1.0.16 introduced an `exports` field to `package.json`. This change can break module resolution for older bundlers (e.g., Webpack 4) or Node.js environments that do not correctly interpret the `exports` map, particularly when using CommonJS `require()`.
- gotcha Version 1.0.17 added a `.mjs` version for Node.js. While intended to improve ESM compatibility, this, coupled with the `exports` field, can introduce module resolution complexities if your project configuration is not aligned with modern Node.js ESM practices.
- breaking Version 1.0.18 included fixes for TypeScript types for newer TypeScript versions. This could potentially introduce type-related compilation errors or incompatibilities for projects using older TypeScript versions that relied on previous type definitions or workarounds.
- gotcha Version 1.0.20 enforced a maximum of 63 characters for hostname labels, aligning with DNS standards. While a fix, if your application generates very long or non-standard hostnames (e.g., for S3 buckets or custom endpoints), these requests might now fail validation.
Install
-
npm install aws4fetch -
yarn add aws4fetch -
pnpm add aws4fetch
Imports
- AwsClient
const AwsClient = require('aws4fetch').AwsClientimport { AwsClient } from 'aws4fetch' - AwsV4Signer
import AwsV4Signer from 'aws4fetch'
import { AwsV4Signer } from 'aws4fetch' - TypeScript Types
import type { AwsClientOptions } from 'aws4fetch'
Quickstart
import { AwsClient } from 'aws4fetch';
// In a real application, retrieve these from secure environment variables or a secrets manager.
// For demonstration, use placeholders or process.env.
const MY_ACCESS_KEY = process.env.AWS_ACCESS_KEY_ID ?? 'YOUR_AWS_ACCESS_KEY_ID';
const MY_SECRET_KEY = process.env.AWS_SECRET_ACCESS_KEY ?? 'YOUR_AWS_SECRET_ACCESS_KEY';
const MY_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN ?? undefined; // Optional for temporary credentials
async function setupAndInvokeLambda() {
if (!MY_ACCESS_KEY || !MY_SECRET_KEY) {
console.error("AWS credentials are not set. Please provide MY_ACCESS_KEY and MY_SECRET_KEY.");
return;
}
const aws = new AwsClient({
accessKeyId: MY_ACCESS_KEY,
secretAccessKey: MY_SECRET_KEY,
sessionToken: MY_SESSION_TOKEN,
region: 'us-east-1' // Explicitly set a region if known, or let aws4fetch parse it
});
// Example: Invoking an AWS Lambda function
// Replace 'my-lambda' with your actual Lambda function name.
// The API endpoint structure is standard for Lambda Invoke API.
const LAMBDA_FN_API_BASE = 'https://lambda.us-east-1.amazonaws.com/2015-03-31/functions';
const functionName = 'my-test-lambda'; // Replace with your Lambda function name
const url = `${LAMBDA_FN_API_BASE}/${functionName}/invocations`;
const eventPayload = {
message: 'Hello from aws4fetch!',
timestamp: new Date().toISOString()
};
try {
console.log(`Invoking Lambda function: ${functionName} with payload:`, eventPayload);
const res = await aws.fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(eventPayload)
});
if (res.ok) {
const jsonResponse = await res.json();
console.log('Lambda invocation successful:', jsonResponse);
return jsonResponse;
} else {
const errorText = await res.text();
console.error(`Lambda invocation failed with status ${res.status}:`, errorText);
throw new Error(`Lambda invocation failed: ${res.status} ${errorText}`);
}
} catch (error) {
console.error('Error during Lambda invocation:', error);
throw error;
}
}
// Call the async function
setupAndInvokeLambda().catch(err => console.error("Unhandled error:", err));