Every AWS call needs credentials, and the AWS SDK for JavaScript v3 makes credential resolution explicit and composable. Instead of a hidden chain baked into the client, v3 exposes each strategy as a small function you can import from @aws-sdk/credential-providers and pass to the credentials option.
This page covers the three you reach for most: fromEnv, fromIni, and fromNodeProviderChain. Understanding them means you never have to guess where a call got its credentials from.
Choosing a provider by environment keeps local, CI, and production behavior explicit and predictable.
import { S3Client } from "@aws-sdk/client-s3";import { fromEnv, fromIni, fromNodeProviderChain,} from "@aws-sdk/credential-providers";function credentialsForEnv() { if (process.env.CI) return fromEnv(); // CI injects keys as env vars if (process.env.NODE_ENV === "development") { return fromIni({ profile: "dev" }); // local dev uses a named profile } return fromNodeProviderChain(); // prod: SSO, container, or instance role}const s3 = new S3Client({ region: "us-east-1", credentials: credentialsForEnv(),});
What this demonstrates:
Each provider is a function that returns a credential resolver the client calls when it needs to sign.
fromEnv is deterministic in CI, where secrets arrive as environment variables.
fromIni targets one named profile for local work, so you never send calls with the wrong account.
fromNodeProviderChain covers production, where credentials come from an IAM role via container or instance metadata.
The composite default. It tries sources in order: environment variables, SSO, shared config/credentials, then container credentials (ECS/Fargate), then EC2 instance metadata (IMDS).
The first source that yields credentials wins, and the result is cached and refreshed as needed.
This is what a v3 client uses when you pass no credentials at all, so calling it explicitly mainly documents intent.
On EC2, ECS, and Lambda it resolves the attached IAM role automatically, which is the recommended production posture.
boto3 also has a credential chain, but it is implicit: you configure it through ~/.aws/config, environment variables, Session(profile_name=...), or botocore config, and the chain runs inside the client. v3 surfaces the same strategies as named, importable functions you pass in. The resolution order of fromNodeProviderChain mirrors boto3's default chain closely; the difference is that v3 makes the choice a value in your code rather than ambient configuration.
Expecting fromEnv to read a profile - it only reads environment variables and will fail if they are unset. Fix: use fromIni for profile-based credentials.
Assuming a provider runs at construction - providers are lazy and resolve on first signing. Fix: do not rely on client construction to validate credentials; make a cheap call to verify.
IMDS timeouts off of EC2 - fromNodeProviderChain may probe instance metadata and pause when run somewhere without IMDS. Fix: provide env or profile credentials locally so the chain resolves before reaching IMDS.
Wrong profile silently used - omitting profile falls back to default or AWS_PROFILE, which may not be what you want. Fix: pass fromIni({ profile }) explicitly in multi-account setups.
Hardcoding keys instead of a provider - inline credentials: { accessKeyId, secretAccessKey } leaks secrets into source. Fix: use a provider or an IAM role, never literals.
Forgetting the session token - temporary credentials need AWS_SESSION_TOKEN too. Fix: let the provider resolve full temporary credentials rather than passing only key and secret.
A function that resolves AWS credentials when the client needs to sign a request. You import it from @aws-sdk/credential-providers and pass it as the credentials option.
What does fromEnv read?
Only environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optionally AWS_SESSION_TOKEN. It fails if they are not set.
What does fromIni read?
A named profile from the shared config and credentials files. It supports assumed-role and SSO profiles, and defaults to default or AWS_PROFILE when no profile is given.
What order does fromNodeProviderChain try?
Environment variables, SSO, shared config/credentials, container credentials (ECS), then EC2 instance metadata. The first source that yields credentials wins.
Do I need to pass credentials at all?
No. Omitting the credentials option uses the node default chain, equivalent to fromNodeProviderChain(). Pass a provider only to narrow or override the source.
When are credentials actually resolved?
Lazily, on the first request that needs signing - not at client construction. That is why an invalid provider may not error until the first call.
Which provider should I use in Lambda?
None explicitly. Attach an IAM role and let the default chain resolve the role's temporary credentials automatically.
Why does my app hang when run outside EC2?
The default chain may probe EC2 instance metadata (IMDS) and wait. Provide env or profile credentials locally so the chain resolves before it reaches IMDS.
Can I share one provider across clients?
Yes. Build the provider once and pass the same value to every client so they all resolve credentials identically, with shared caching.
How does this compare to boto3?
boto3 runs a similar chain implicitly, configured through sessions and config files. v3 exposes the same strategies as named functions you pass in, making the choice explicit in code.