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

aws-sigv4-fetch

v5.0.1

Published

SignatureV4 fetch function implemented with the official AWS SDK

Readme

This library wraps the fetch API so every request is signed with AWS Signature Version 4. Signing is done by @smithy/signature-v4, the same signer the AWS SDK uses, so signatures are computed exactly the way AWS expects.

Why?

Most AWS services (API Gateway, Lambda Function URLs, AppSync, IAM, OpenSearch) can be locked behind IAM authentication. Once they are, a plain fetch is rejected with 403 Forbidden, because every request must carry an Authorization header derived from your credentials, the request itself, and the current time. However, you may not want to:

  • Adopt a service-specific SDK client: pulling in @aws-sdk/client-* just to call your own HTTP endpoint is a lot of dependency for one request
  • Hand-roll the signature: SigV4 covers the method, URL, query string, headers and body, and getting the canonical form wrong fails with an opaque 403
  • Rewrite your HTTP layer: your code already calls fetch, and it should keep doing so

This library gives you a fetch function with the identical signature, so signing becomes a drop-in replacement.

[!TIP] Using Axios, Ky, Got or another HTTP library instead? Use aws-sigv4-sign, which returns a signed Request whose headers you can hand to any client.

Installation

npm install aws-sigv4-fetch

Requires Node.js >= 20. Ships both ES Module and CommonJS builds with bundled TypeScript declarations, so no @types/* package is needed.

// ESM
import { createSignedFetcher } from 'aws-sigv4-fetch';

// CommonJS
const { createSignedFetcher } = require('aws-sigv4-fetch');

Usage

createSignedFetcher takes the signing configuration once and returns a fetch function. The returned function has the same signature as the native fetch, so it accepts a string, a URL or a Request, plus an optional RequestInit.

import { createSignedFetcher } from 'aws-sigv4-fetch';

const signedFetch = createSignedFetcher({ service: 'lambda', region: 'eu-west-1' });

const response = await signedFetch('https://mylambda.lambda-url.eu-west-1.on.aws/', {
  method: 'POST',
  body: JSON.stringify({ a: 1 }),
  headers: { 'Content-Type': 'application/json' },
});

Service and region

service is required and must match the AWS service you are calling. A mismatch fails with Credential should be scoped to correct service: 'service'. region is optional and defaults to us-east-1.

const signedFetch = createSignedFetcher({
  // service: must match the target, this is the most common source of 403s
  service: 'execute-api',
  // region: defaults to 'us-east-1'; global services like IAM are always signed for us-east-1
  region: 'eu-west-1',
});

Common values:

| Target | service | | -------------------------------- | ------------- | | API Gateway (REST and HTTP APIs) | execute-api | | Lambda Function URL | lambda | | AppSync | appsync | | IAM | iam | | OpenSearch / Elasticsearch | es | | S3 | s3 |

Credentials

Credentials are optional in Node.js and required in the browser. When omitted in Node.js they are resolved with @aws-sdk/credential-provider-node, which checks, in order: environment variables, SSO token cache, web identity tokens, shared credentials and config files, and finally the EC2/ECS instance metadata service.

// Credentials are picked up from the environment
const signedFetch = createSignedFetcher({ service: 'lambda', region: 'eu-west-1' });

[!IMPORTANT] The default provider is constructed once and reused for the lifetime of the process. The AWS SDK caches the credentials it resolves and refreshes them before they expire, so only the first signed request pays for the lookup. Because the provider is pinned, changes to AWS_PROFILE or the other credential environment variables after the first signed request are not picked up; pass credentials explicitly if you need to switch identities at runtime.

You can always pass credentials explicitly, which skips the lookup. The option accepts either a static AwsCredentialIdentity or an AwsCredentialIdentityProvider function:

const signedFetch = createSignedFetcher({
  service: 'lambda',
  region: 'eu-west-1',
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
    // sessionToken: only for temporary credentials, adds the x-amz-security-token header
    sessionToken: process.env.AWS_SESSION_TOKEN,
  },
});

In the browser there is no environment to resolve from, so omitting credentials throws. Use temporary, scoped credentials from Amazon Cognito or a web federated identity provider via @aws-sdk/credential-providers:

import { fromCognitoIdentityPool } from '@aws-sdk/credential-providers';

const signedFetch = createSignedFetcher({
  service: 'execute-api',
  region: 'eu-west-1',
  credentials: fromCognitoIdentityPool({
    identityPoolId: 'eu-west-1:...',
    clientConfig: { region: 'eu-west-1' },
  }),
});

[!WARNING] Never hardcode AWS credentials in a browser application. Doing so exposes your access key ID and secret access key to anyone who loads the page.

Custom fetch

fetch is optional and defaults to the global fetch, which is available natively in Node.js >= 20. Pass your own implementation when the global is missing or when you want the same instrumented fetch your application already uses.

import ponyfill from 'cross-fetch';

const signedFetch = createSignedFetcher({
  service: 'lambda',
  region: 'eu-west-1',
  // fetch: defaults to the global fetch
  fetch: ponyfill,
});

A global polyfill works too, in which case the option can be omitted entirely:

import 'cross-fetch/polyfill';

// fetch is now global, so it does not need to be passed
const signedFetch = createSignedFetcher({ service: 'lambda', region: 'eu-west-1' });

Any client that accepts a fetch

Because the returned function is signature-compatible with fetch, any library that lets you swap in a custom fetch is signed without further glue. For example graphql-request:

import { createSignedFetcher } from 'aws-sigv4-fetch';
import { GraphQLClient } from 'graphql-request';

const client = new GraphQLClient('https://mygraphqlapi.appsync-api.eu-west-1.amazonaws.com/graphql', {
  fetch: createSignedFetcher({ service: 'appsync', region: 'eu-west-1' }),
});

const result = await client.request(query, { input: { name: 'Item' } });

Advanced

Everything must be set before signing

[!IMPORTANT] The signature covers the method, URL, query string, headers and body. Anything you change after signing invalidates it and the request fails with 403 Forbidden. Pass the full RequestInit to the signed fetcher rather than mutating the request afterwards.

Custom headers are therefore part of the signature, while an AbortSignal is passed straight through to the underlying fetch:

const response = await signedFetch('https://mylambda.lambda-url.eu-west-1.on.aws/', {
  headers: { 'X-Custom-Header': 'value' },
  signal: AbortSignal.timeout(5_000),
});

Browser bundles

The Node-only credential provider is loaded through a dynamic import, and aws-sigv4-sign maps it to false in its browser field, so bundlers leave it out of browser builds entirely. This is why credentials must be explicit in the browser.

API

createSignedFetcher(options)

function createSignedFetcher(options: SignedFetcherOptions): typeof fetch;

Returns a fetch function that signs every request before sending it. Configuration is captured once, when the fetcher is created; the returned function takes only fetch's own arguments.

const signedFetch = createSignedFetcher({ service: 'lambda', region: 'eu-west-1' });

// Same call signatures as the native fetch
await signedFetch('https://mylambda.lambda-url.eu-west-1.on.aws/');
await signedFetch(new URL('https://mylambda.lambda-url.eu-west-1.on.aws/'));
await signedFetch(new Request('https://mylambda.lambda-url.eu-west-1.on.aws/'));
await signedFetch('https://mylambda.lambda-url.eu-west-1.on.aws/', { method: 'POST', body: '{}' });

Types

SignedFetcherOptions

The options bag accepted by createSignedFetcher.

import type { SignedFetcherOptions } from 'aws-sigv4-fetch';

type SignedFetcherOptions = {
  service: string; // required, e.g. 'lambda' or 'execute-api'
  region?: string; // default: 'us-east-1'
  credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider; // default: resolved from the environment in Node.js
  fetch?: typeof fetch; // default: the global fetch
};

CreateSignedFetcher

The type of createSignedFetcher itself. Useful when wrapping or injecting the factory.

import type { CreateSignedFetcher } from 'aws-sigv4-fetch';

type CreateSignedFetcher = (init: SignedFetcherOptions) => typeof fetch;

License

MIT