A compact (6.4kb minified, 2.5kb gzipped) AWS client for environments that support fetch
and SubtleCrypto
– that is, modern web browsers and JS platforms like Cloudflare Workers. Also retries requests with an exponential backoff with full jitter strategy by default.
import { AwsClient } from 'aws4fetch' const aws = new AwsClient({ accessKeyId: MY_ACCESS_KEY, secretAccessKey: MY_SECRET_KEY }) // https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html const LAMBDA_FN_API = 'https://lambda.us-east-1.amazonaws.com/2015-03-31/functions' async function invokeMyLambda(event) { const res = await aws.fetch(`${LAMBDA_FN_API}/my-lambda/invocations`, { body: JSON.stringify(event) }) // `res` is a standard Response object: https://developer.mozilla.org/en-US/docs/Web/API/Response return res.json() } invokeMyLambda({my: 'event'}).then(json => console.log(json))
You can see a more detailed example, a Cloudflare Worker script you can use as a replacement for API Gateway, in the example
directory.
aws4fetch
exports two classes: AwsClient
and AwsV4Signer
You can use the same instance of AwsClient
for all your service calls as the service and region will be determined at fetch time – or you can create separate instances if you have different needs, eg no retrying for some service.
import { AwsClient } from 'aws4fetch' const aws = new AwsClient({ accessKeyId, // required, akin to AWS_ACCESS_KEY_ID secretAccessKey, // required, akin to AWS_SECRET_ACCESS_KEY sessionToken, // akin to AWS_SESSION_TOKEN if using temp credentials service, // AWS service, by default parsed at fetch time region, // AWS region, by default parsed at fetch time cache, // credential cache, defaults to `new Map()` retries, // number of retries before giving up, defaults to 10, set to 0 for no retrying initRetryMs, // defaults to 50 – timeout doubles each retry })
Has the same signature as the global fetch function
import { AwsClient } from 'aws4fetch' const aws = new AwsClient(opts) async function doFetch() { const response = await aws.fetch(url, { method, // if not supplied, will default to 'POST' if there's a body, otherwise 'GET' headers, // standard JS object literal, or Headers instance body, // optional, String or ArrayBuffer/ArrayBufferView – ie, remember to stringify your JSON // and any other standard fetch options, eg keepalive, etc // optional, largely if you want to override options in the AwsClient instance aws: { signQuery, // set to true to sign the query string instead of the Authorization header accessKeyId, // same as in AwsClient constructor above secretAccessKey, // same as in AwsClient constructor above sessionToken, // same as in AwsClient constructor above service, // same as in AwsClient constructor above region, // same as in AwsClient constructor above cache, // same as in AwsClient constructor above datetime, // defaults to now, to override use the form '20150830T123600Z' appendSessionToken, // set to true to add X-Amz-Security-Token after signing, defaults to true for iot allHeaders, // set to true to force all headers to be signed instead of the defaults singleEncode, // set to true to only encode %2F once (usually only needed for testing) }, }) console.log(await response.json()) }
NB: Due to the way bodies are handled in Request
instances, it's faster to invoke the function as above – using a URL as the input
argument and passing the body
in the init
argument – instead of the form of invocation that uses a Request
object directly as input
.
If you don't know which URL to call for the AWS service you want, the full list of AWS endpoints can be found here: https://docs.aws.amazon.com/general/latest/gr/rande.html
And the APIs are documented here: https://docs.aws.amazon.com/ (the REST APIs are usually documented under "API Reference" for each service)
Returns a Promise that resolves to an AWS4 signed Request
– has the same signature as fetch
. Use this to create a Request
you can send using fetch()
yourself.
import { AwsClient } from 'aws4fetch' const aws = new AwsClient(opts) async function doFetch() { const request = await aws.sign(url, { method, // if not supplied, will default to 'POST' if there's a body, otherwise 'GET' headers, // standard JS object literal, or Headers instance body, // optional, String or ArrayBuffer/ArrayBufferView – ie, remember to stringify your JSON // and any other standard fetch options, eg keepalive, etc // optional, largely if you want to override options in the AwsClient instance aws: { signQuery, // set to true to sign the query string instead of the Authorization header accessKeyId, // same as in AwsClient constructor above secretAccessKey, // same as in AwsClient constructor above sessionToken, // same as in AwsClient constructor above service, // same as in AwsClient constructor above region, // same as in AwsClient constructor above cache, // same as in AwsClient constructor above datetime, // defaults to now, to override use the form '20150830T123600Z' appendSessionToken, // set to true to add X-Amz-Security-Token after signing, defaults to true for iot allHeaders, // set to true to force all headers to be signed instead of the defaults singleEncode, // set to true to only encode %2F once (usually only needed for testing) }, }) const response = await fetch(request) console.log(await response.json()) }
The underlying signing class for a request – use this if you just want to deal with the raw AWS4 signed method/url/headers/body.
import { AwsV4Signer } from 'aws4fetch' const signer = new AwsV4Signer({ url, // required, the AWS endpoint to sign accessKeyId, // required, akin to AWS_ACCESS_KEY_ID secretAccessKey, // required, akin to AWS_SECRET_ACCESS_KEY sessionToken, // akin to AWS_SESSION_TOKEN if using temp credentials method, // if not supplied, will default to 'POST' if there's a body, otherwise 'GET' headers, // standard JS object literal, or Headers instance body, // optional, String or ArrayBuffer/ArrayBufferView – ie, remember to stringify your JSON signQuery, // set to true to sign the query string instead of the Authorization header service, // AWS service, by default parsed at fetch time region, // AWS region, by default parsed at fetch time cache, // credential cache, defaults to `new Map()` datetime, // defaults to now, to override use the form '20150830T123600Z' appendSessionToken, // set to true to add X-Amz-Security-Token after signing, defaults to true for iot allHeaders, // set to true to force all headers to be signed instead of the defaults singleEncode, // set to true to only encode %2F once (usually only needed for testing) })
Actually perform the signing of the request and return a Promise that resolves to an object containing the signed method, url, headers and body.
method
will be a String
, url
will be an instance of URL
, headers
will be an instance of Headers
and body
will unchanged from the argument you supply to the constructor.
import { AwsV4Signer } from 'aws4fetch' const signer = new AwsV4Signer(opts) async function sign() { const { method, url, headers, body } = await signer.sign() console.log(method, url, [...headers], body) }
Returns a Promise that resolves to the signed string to use in the Authorization
header
Used by the sign()
method – you shouldn't need to access this directly unless you're constructing your own requests.
Returns a Promise that resolves to the hex signature
Used by the sign()
method – you shouldn't need to access this directly unless you're constructing your own requests.
With npm do:
npm install aws4fetch