Security News
PyPI Introduces Digital Attestations to Strengthen Python Package Security
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
@aws-lite/client
Advanced tools
aws-lite
aws-lite
is a simple, extremely fast, extensible AWS client for Node.js.(It's got good error reporting, too.)
aws-lite
is developed and maintained by the folks at OpenJS Foundation Architect. We <3 AWS!
aws-sdk
/ @aws-sdk/*
?Amazon has historically done a great job of maintaining its SDKs. However, its JavaScript SDKs are huge, with lots of generated code. This results in things like very slow instantiation (example: >400ms to load a single AWS client in SDK v3, and >500ms in v2), and reporting errors without usable stack traces.
We built aws-lite
to provide a simpler, faster, more stable position from which to work with AWS services in Node.js.
Install the client:
npm i @aws-lite/client
You can use the client as-is to quickly interact with AWS service APIs, or extend it with specific service plugins like so:
npm i @aws-lite/dynamodb
/**
* Instantiate a client
* This is a synchronous operation that will attempt to load your AWS credentials, local configuration, region settings, etc.
*/
import awsLite from '@aws-lite/client'
const config = { region: 'us-west-1' } // Optional
const aws = await awsLite(config)
/**
* Reads
* Fire a GET request by specifying an AWS service name and endpoint
*/
await aws({
service: 'lambda',
endpoint: '/2015-03-31/functions/my-lambda-name/configuration',
})
// {
// FunctionName: 'my-lambda-name',
// Runtime: 'nodejs18.x',
// ...
// }
/**
* Writes
* POST JSON to an endpoint by adding a payload property
*/
await aws({
service: 'lambda',
endpoint: '/2015-03-31/functions/my-lambda-name/invocations',
payload: { ok: true },
})
// ... whatever your Lambda returned
The following options may be passed when instantiating the aws-lite
client:
accessKeyId
(string)
AWS_ACCESS_KEY_ID
or AWS_ACCESS_KEY
env vars, and then to a ~/.aws/credentials
file, if presentAWS_SHARED_CREDENTIALS_FILE
env varaws-lite
will throwsecretAccessKey
(string)
AWS_SECRET_ACCESS_KEY
or AWS_SECRET_KEY
env vars, and then to a ~/.aws/credentials
file, if presentAWS_SHARED_CREDENTIALS_FILE
env varaws-lite
will throwsessionToken
(string)
AWS_SESSION_TOKEN
env var, and then to a ~/.aws/credentials
file, if presentAWS_SHARED_CREDENTIALS_FILE
env varregion
(string)
us-west-1
); if not provided, defaults to AWS_REGION
, AWS_DEFAULT_REGION
, or AMAZON_REGION
env vars~/.aws/config
(or custom) file will only be loaded by making the AWS_SDK_LOAD_CONFIG
env var trueAWS_CONFIG_FILE
(and AWS_SDK_LOAD_CONFIG
) env varaws-lite
will throwprofile
(string)
AWS_PROFILE
env var, and then to the default
profile, if presentautoloadPlugins
(boolean) [default = true]
@aws-lite/*
+ aws-lite-plugin-*
pluginsdebug
(boolean) [default = false]
endpointPrefix
(string)
host
(string)
keepAlive
(boolean) [default = true]
plugins
(array)
aws-lite
plugins to load; can be module names (e.g. @aws-lite/dynamodb
) or file paths on the local machine (e.g. /path/to/my/plugin.mjs
)@aws-lite/
) and unofficial plugins (prefixed with aws-lite-plugin-
) will be loadedport
(number)
protocol
(string) [default = https
]
http
, helpful for local testingresponseContentType
(string)
An example:
import awsLite from '@aws-lite/client'
// Minimal: load everything from env vars
aws = await awsLite()
// Maximal: specify everything yourself
aws = await awsLite({
accessKeyId: '$accessKey',
secretAccessKey: '$secretKey',
sessionToken: '$sessionToken',
region: 'us-west-1',
profile: 'work',
autoloadPlugins: false, // Not necessary if manually specifying plugins
debug: true,
endpointPrefix: '/test/path/',
host: 'localhost',
keepAlive: false,
plugins: [ '@aws-lite/dynamodb', '/a/custom/local/plugin/path' ],
port: 12345,
protocol: 'http',
responseContentType: 'application/json',
})
The following parameters may be passed with individual client requests; only service
is required:
awsjson
(boolean or array)
payload
endpoint
(string) [default = /
]
headers
(object)
payload
that cannot be automatically JSON-encoded and you do not specify a content-type
header, the default application/octet-stream
will be usedpayload
(object, buffer, readable stream, string)
body
, data
, json
content-type
header set, if not already present); buffers, streams, and strings simply pass through as-ispaginate
(boolean) [experimental]
paginator.default
), pass false
to disable automatic paginationpaginator
(object) [experimental]
type
(string) [default = payload
] - payload
(default) passes the cursor via body payload, query
passes the cursor via query string parametercursor
(string) [required] - pagination token from the prior response payload to be passed with the current request; example: in S3 this is the query string parameter continuation token
token
(string) [required] - pagination token returned in each response payload to be passed with the next request as cursor
; example: in S3, this would be NextContinuationToken
accumulator
(string) [required] - Response payload array property name to aggregate into final result set; example: in S3, this would be Contents
default
(string) - set value to enabled
to enable pagination for all applicable requests by default; individual requests can opt out of pagination by setting paginate
to false
query
(object)
endpoint
as a query string in your requestservice
(string) [required]
DynamoDB
= dynamodb
); full list can be found hereAdditionally, the following configuration options can be specified in each request, overriding those specified by the instantiated client:
region
,protocol
,host
, andport
An example:
import awsLite from '@aws-lite/client'
const aws = await awsLite()
// Make a plain JSON request
await awsLite({
service: 'lambda',
endpoint: '/2015-03-31/functions/$function-name/invocations',
query: { Qualifier: '1' }, // Invoke's version / alias '1'
payload: { ok: true }, // Object will be automatically JSON-encoded
})
// Make an AWS-flavored JSON request
await awsLite({
service: 'dynamodb',
headers: { 'X-Amz-Target': `DynamoDB_20120810.GetItem` },
awsjson: [ 'Key' ], // Ensures only payload.Key will become AWS-flavored JSON
payload: {
TableName: '$table-name',
Key: { myHashKey: 'Gaal', mySortKey: 'Dornick' }
},
})
// Paginate results
await awsLite({
service: 'dynamodb',
headers: { 'X-Amz-Target': `DynamoDB_20120810.Scan` },
paginator: {
cursor: 'ExclusiveStartKey',
token: 'LastEvaluatedKey',
accumulator: 'Items',
enabled: 'default',
},
payload: {
TableName: '$table-name',
},
})
The following properties are returned with each non-error client response:
statusCode
(number)
headers
(object)
payload
(object, string, null)
null
payloadAn example:
import awsLite from '@aws-lite/client'
const aws = await awsLite()
await awsLite({
service: 'lambda',
endpoint: '/2015-03-31/functions/$function-name/configuration',
})
// {
// statusCode: 200,
// headers: {
// 'content-type': 'application/json',
// 'x-amzn-requestid': 'ba3a55d2-16c2-4c2b-afe1-cf0c5523040b',
// ...
// },
// payload: {
// FunctionName: '$function-name',
// FunctionArn: 'arn:aws:lambda:us-west-1:1234567890:function:$function-name',
// Role: 'arn:aws:iam::1234567890:role/$function-name-role',
// Runtime: 'nodejs18.x',
// ...
// }
// }
Out of the box, @aws-lite/client
is a full-featured AWS API client that you can use to interact with any AWS service that makes use of authentication via AWS signature v4 (which should be just about all of them).
@aws-lite/client
can be extended with plugins to more easily interact with AWS services, or provide custom behavior or semantics. As such, plugins enable you to have significantly more control over the entire API request/response lifecycle.
A bit more about how plugins work:
aws-lite
publishes service plugins under the @aws-lite/$service
namespace that conform to aws-lite
standards@aws-lite/*
plugins, and packages published to npm with the aws-lite-plugin-*
prefix, are automatically loaded by the @aws-lite/client
upon instantiation
autoloadPlugins
parameterThus, to make use of the @aws-lite/dynamodb
plugin, this is what your code would look like:
npm i @aws-lite/client @aws-lite/dynamodb
import awsLite from '@aws-lite/client'
const aws = await awsLite() // @aws-lite/dynamodb is now loaded
aws.dynamodb.PutItem({ TableName: 'my-table', Key: { id: 'hello' } })
The aws-lite
plugin API is lightweight and simple to learn. It makes use of four optional lifecycle hooks:
validate
[optional] - an object of property names and types used to validate inputs pre-requestrequest()
[optional] - an async function that enables mutation of inputs to the final service API requestresponse()
[optional] - an async function that enables mutation of service API responses before they are returnederror()
[optional] - an async function that enables mutation of service API errors before they are returnedThe above four lifecycle hooks must be exported as an object named methods
, along with a valid AWS service code property named service
, like so:
// A simple plugin for validating `TableName` input on dynamodb.PutItem() calls
export default {
service: 'dynamodb',
awsDoc: 'https://docs.aws.../API_PutItem.html',
readme: 'https://github...#PutItem',
methods: {
PutItem: {
validate: {
TableName: { type: 'string', required: true }
}
}
}
}
// Using the above plugin
aws.dynamodb.PutItem({ TableName: 12345 }) // Throws validation error
Additionally, two optional (but highly recommended) metadata properties that will be included in any method errors:
awsDoc
(string) [optional] - intended to be a link to the AWS API doc pertaining to this method; should usually start with https://docs.aws.amazon.com/...
readme
(string) [optional] - a link to a relevant section in your plugin's readme or docsExample plugins can be found below, in plugins/
dir (containing @aws-lite/*
plugins), and in tests.
validate
The validate
lifecycle hook is an optional object containing (case-sensitive) input property names, with a corresponding object that denotes their type
and whether required
.
Types are as follows: array
boolean
number
object
string
. An example validate
plugin:
// Validate inputs for a single DynamoDB method (`CreateTable`)
export default {
service: 'dynamodb',
methods: {
CreateTable: {
validate: {
TableName: { type: 'string', required: true },
AttributeDefinitions: { type: 'array', required: true },
KeySchema: { type: 'array', required: true },
BillingMode: { type: 'string' },
DeletionProtectionEnabled: { type: 'boolean' },
GlobalSecondaryIndexes: { type: 'array' },
LocalSecondaryIndexes: { type: 'array' },
ProvisionedThroughput: { type: 'object' },
SSESpecification: { type: 'object' },
StreamSpecification: { type: 'object' },
TableClass: { type: 'string' },
Tags: { type: 'array' },
}
}
}
}
request()
The request()
lifecycle hook is an optional async function that enables that enables mutation of inputs to the final service API request.
request()
is executed with two positional arguments:
params
(object)
utils
(object)
The request()
method may return nothing, or a valid client request. An example:
// Automatically serialize input to AWS-flavored JSON
export default {
service: 'dynamodb',
methods: {
PutItem: {
validate: { Item: { type: 'object', required: true } },
request: async (params, utils) => {
params.Item = utils.awsjsonMarshall(params.Item)
return {
headers: { 'X-Amz-Target': `DynamoDB_20120810.PutItem` }
payload: params
}
}
}
}
}
response()
The response()
lifecycle hook is an async function that enables mutation of service API responses before they are returned.
response()
is executed with two positional arguments:
response
(object)
statusCode
(number)
headers
(object)
payload
(object or string)
aws-lite
will attempt to parse it prior to executing response()
. Responses that are primarily JSON, but with nested AWS-flavored JSON, will be parsed only as JSON and may require additional deserialization with the awsjsonUnmarshall
utility or awsjson
propertyutils
(object)
The response()
method may return: nothing (which will pass through the response
object as-is) or any data (most commonly an object or string, or mutated version of the response
object).
Should you return an object, you may also include an awsjson
property (that behaves the same as in client requests). The awsjson
property is considered reserved, and will be stripped from any returned data.
An example:
// Automatically deserialize AWS-flavored JSON
export default {
service: 'dynamodb',
methods: {
GetItem: {
// Assume successful responses always have an AWS-flavored JSON `Item` property
response: async (response, utils) => {
response.awsjson = [ 'Item' ]
return response // Returns the response (`statusCode`, `headers`, `payload`), with `payload.Item` unformatted from AWS-flavored JSON, and the `awsjson` property removed
}
}
}
}
error()
The error()
lifecycle hook is an async function that enables mutation of service API errors before they are returned.
error()
is executed with two positional arguments:
error
(object)
error
(object or string): the raw error from the service API; if the entire error payload is JSON, aws-lite
will attempt to parse it into the error
propertymetadata
(object) - aws-lite
error metadata; to improve the quality of the errors presented by aws-lite
, please only append to this objectstatusCode
(number or undefined) - resulting status code of the API response; if an HTTP connection error occurred, no statusCode
will be presentutils
(object)
The error()
method may return nothing, a new or mutated version of the error payload it was passed, a string, an object, or a JS error. An example:
// Improve clarity of error output
export default {
service: 'lambda',
methods: {
GetFunctionConfiguration: {
error: async (err, utils) => {
if (err.statusCode === 400 &&
err?.error?.message?.match(/validation/)) {
// Append a property to be clearly displayed along with the other error data
err.metadata.type = 'Validation error'
}
return err
}
}
}
}
request()
, response()
, and error()
are all passed a second argument of helper utilities and data pertaining to the client:
awsjsonMarshall
(function)
awsjsonUnmarshall
(function)
config
(object)
credentials
objectcredentials
(object)
accessKeyId
, secretAccessKey
, and sessionToken
being used in this requestsecretAccessKey
and sessionToken
are present in this object, but non-enumerableregion
(string)
config
object if overridden per-requestAn example of plugin utils:
async function request (params, utils) {
let awsStyle = utils.awsjsonMarshall({ ok: true, hi: 'there' })
console.log(marshalled) // { ok: { BOOL: true }, hi: { S: 'there' } }
let plain = utils.awsjsonUnmarshall({ ok: { BOOL: true }, hi: { S: 'there' } })
console.log(unmarshalled) // { ok: true, hi: 'there' }
console.log(config) // { profile: 'my-profile', autoloadPlugins: true, ... }
console.log(credentials) // { accessKeyId: 'abc123...' } secrets are non-enumerable
console.log(region) // 'us-west-1'
}
@aws-lite/*
pluginsaws-lite/*
plugins can have auto-generated and hand-written types. These type definitions are authored as .d.ts
files and distributed as separate, optional dependencies. The "ambient" types are automatically loaded by editors (using the official TS Lang Server like VSCode) or declared specifically in a TypeScript project's tsconfig
.
Generally, types are available as @aws-lite/<plugin>-types
packages. For example, once you have installed @aws-lite/client
and @aws-lite/dynamodb
as dependencies, add the DynamoDB types as a dev dependency:
npm i -D @aws-lite/dynamodb-types
In a JavaScript project, code completion (aka Intellisense) for input and output types will be loaded automatically for awsLite.dynamodb.<method>
calls.
In a TypeScript project, include the types in your tsconfig.json
:
{
"compilerOptions": {
"types": [
"@aws-lite/dynamodb-types"
]
}
}
AWS has (as of this writing) nearly 300 service APIs – aws-lite
would love your help in authoring and maintaining official (and unofficial) plugins!
npm it
@aws-lite/*
plugin:
plugins
filenpm run gen
It is advisable you have AWS credentials on your local development machine for manual verification of any client or service plugin changes
@aws-lite/*
pluginsSimilar to the Definitely Typed (@types
) model, aws-lite
releases packages maintained by third parties under the @aws-lite/*
namespace.
Plugins released within the @aws-lite/*
namespace are expected to conform to the following standards:
@aws-lite/*
plugins should read more or less like the others, and broadly adhere to the following style:
@aws-lite/lambda
, @aws-lite/lambda-1
, @aws-lite/lambda-new
, etc.aws-lite
project, plugins should ideally have zero external dependencies
tape
as the runner and tap-arc
as the output parseraws-lite-plugin-
package prefixDue to the mission-critical nature of this project, we strive for 100% test coverage on the core client. (We also acknowledge that 100% coverage does not mean 0 bugs, so meaningful and thorough tests are much appreciated.)
Due to the nature of interacting with AWS services, manual validation is not only often necessary, but in many cases it's required. (For example: running automated test suites on EKS may be slow, onerous, and financially expensive.)
One should expect that running the live AWS client test suite (npm run test:live
) will result in a limited number of free tier resources to be created in the account corresponding to your supplied (or default) AWS credentials. These resources should never exceed the free tier under normal circumstances.
@aws-lite/*-types
packages@aws-lite/*
plugins can have auto-generated and hand-written types. These type definitions are authored as index.d.ts
files and distributed as separate, optional dependencies.
After adding a plugin and running npm run gen
, a types/
directory will be added to the newly created plugin directory. Each time npm run gen
is run, the plugin implementation is analyzed and the types are regenerated automatically.
It is possible to opt out of automatic type generation by setting types: false
in the plugins
file.
It also possible to maintain hand-written types alongside generated types by specifying imports and method types outside of their respective "fences" in the generated index.d.ts
file. For example, to provide a custom type for the s3
plugin's PutObject
method, you could do the following:
// plugins/s3/types/index.d.ts
declare interface AwsLiteS3 {
// $METHODS_START
/** edited for brevity */
GetObject: (input: ) => Promise<GetObjectResponse>
HeadObject: (input: ) => Promise<HeadObjectResponse>
ListObjectsV2: (input: ) => Promise<ListObjectsV2Response>
// $METHODS_END
PutObject: (input: ACustomInputType) => Promise<ACustomResponseType>
}
Because the PutObject
method is defined outside of the $METHODS_START
and $METHODS_END
fences, it will not be overwritten by the next run of npm run gen
. Further, the generator won't create a duplicate PutObject
method inside the fences.
@aws-lite/*
pluginsTo release an update to a @aws-lite/*
plugin, use the npm run plugin
script with similar syntax to the npm version
command, like so:
npm run plugin [major|minor|patch] $plugin
npm run plugin minor dynamodb
npm run plugin patch $plugin-types
npm run plugin patch dynamodb-types
FAQs
A simple, fast, extensible AWS client
The npm package @aws-lite/client receives a total of 2,323 weekly downloads. As such, @aws-lite/client popularity was classified as popular.
We found that @aws-lite/client demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.