Conversation
Documents the two client-library APIs that shipped with the AmplifyContext migration in aws-amplify v6.21.0 and v6.22.0. New page frontend/local-context covers createAmplifyContext: what a local context is, when to use one on the client (several profiles signed in at once, tenant switching, environment switching), how to create and use one, that libraryOptions are per context, the two context errors, and why server-side code keeps using runWithAmplifyServerContext with the /server exports. connect-to-existing-resources now leads with Amplify.configure(outputs) and presents the alternatives as tabbed boxes: configuration builder, manual ResourcesConfig, and a hand-written outputs file. Each category example gets the same two-tab treatment, and every example shows the createAmplifyContext alternative next to Amplify.configure. The required-fields tables described amplify_outputs.json fields while every JS example builds a ResourcesConfig, so they are split per platform group: JS readers get ResourcesConfig[K] paths, native platforms keep the outputs fields. Both are trimmed to required fields and link to the type definition or the schema for the rest. The outputs tables are also refreshed against schema v1.5: EMAIL as an MFA method, AWS_LAMBDA as an authorization type, amazon_connect, storage buckets, and the relaxed notifications required list. The server-side rendering page links to the new page for client-side code that needs several configurations at once.
|
|
||
| ```typescript | ||
| import { getCurrentUser } from 'aws-amplify/auth'; | ||
| import { NoAmplifyContextError } from 'aws-amplify'; |
There was a problem hiding this comment.
[major] NoAmplifyContextError isn't exported from aws-amplify (nor from aws-amplify/auth or @aws-amplify/core's main entry). In 6.22.0 it's only exported from @aws-amplify/core/internals/utils (packages/core/src/libraryUtils.ts:118), so this snippet won't compile.
The error file defines a stable name constant (NO_AMPLIFY_CONTEXT_ERROR_NAME), so catching by name works without the import: drop this line and use if (error instanceof Error && error.name === 'NoAmplifyContextError') below. (Alternatively, amplify-js could add a public export.)
There was a problem hiding this comment.
Fixed in 2472841, and thanks for the pointer to the name constant. The import is gone and the snippet catches by name instead:
if (error instanceof Error && error.name === "NoAmplifyContextError") {I confirmed your reading of the export surface: in 6.22.0 the only path to these classes is @aws-amplify/core/internals/utils, so importing from aws-amplify would not have compiled.
One correction for the record, which does not change the fix: packages/core/src/libraryUtils.ts:118-121 re-exports both the class and NO_AMPLIFY_CONTEXT_ERROR_NAME, so it is not name-constant-only.
I also added a sentence saying the classes are internal, so a reader does not go hunting for an import, and that a stable name keeps working when a bundle ends up with duplicate copies of @aws-amplify/core.
|
|
||
| ## Error handling | ||
|
|
||
| Context resolution surfaces two typed errors with stable `name` and `code` values, so you can catch them uniformly: |
There was a problem hiding this comment.
[minor] These errors don't carry a code: NoAmplifyContextError / InvalidAmplifyContextError only pass name, message and recoverySuggestion to AmplifyError.
| Context resolution surfaces two typed errors with stable `name` and `code` values, so you can catch them uniformly: | |
| Context resolution surfaces two typed errors with stable `name` values, so you can catch them uniformly: |
There was a problem hiding this comment.
Fixed in 2472841: the line now reads "two typed errors with stable name values".
Verified against the source rather than taking it on trust: both error classes pass only name, message and recoverySuggestion to AmplifyError, so there was never a code to catch on.
| | Error | When it is thrown | | ||
| | --- | --- | | ||
| | `NoAmplifyContextError` | An API was called without a context AND `Amplify.configure()` has not been called yet (no global context exists), or an explicit `undefined` was passed as the leading context argument. | | ||
| | `InvalidAmplifyContextError` | An `AmplifyContext` was passed in a position other than the first argument. | |
There was a problem hiding this comment.
[minor] This covers only one of the two triggers. It's also thrown when a value that isn't an AmplifyContext is passed in the context (first) position (assertCtxArg.ts:32, vs. the later-position case in resolveCtxArgs.ts:40).
| | `InvalidAmplifyContextError` | An `AmplifyContext` was passed in a position other than the first argument. | | |
| | `InvalidAmplifyContextError` | A value that is not an `AmplifyContext` was passed as the first argument, or an `AmplifyContext` was passed in a position other than the first argument. | |
There was a problem hiding this comment.
Fixed in 2472841. The row now names both triggers:
A value that is not an
AmplifyContextwas passed as the first argument, or anAmplifyContextwas passed in a position other than the first argument.
Confirmed both paths in the source: assertOptionalCtxArg throws on a defined-but-unbranded first argument, and resolveCtxArgs throws on a context that arrives in a later position.
|
|
||
| | Error | When it is thrown | | ||
| | --- | --- | | ||
| | `NoAmplifyContextError` | An API was called without a context AND `Amplify.configure()` has not been called yet (no global context exists), or an explicit `undefined` was passed as the leading context argument. | |
There was a problem hiding this comment.
[nit] An explicit leading undefined only throws when another argument follows it (resolveCtxArgs.ts:47 checks args.length > 1). A lone getCurrentUser(undefined) falls back to the global context. Maybe "…or undefined was passed as the leading context argument followed by other arguments."
There was a problem hiding this comment.
Fixed in 2472841, using your wording: "or undefined was passed as the leading context argument followed by other arguments".
Confirmed the args.length > 1 gate in resolveCtxArgs, so a lone getCurrentUser(undefined) does fall back to the global context exactly as you describe.
|
|
||
| <Block name="Local context"> | ||
|
|
||
| Import `createAmplifyContext` from `aws-amplify`, pass your backend outputs to create the context, then hand that context to any category API as its **first** positional argument. Unlike `Amplify.configure()`, creating a context does **not** set a global context and does **not** dispatch any Hub events: it simply returns a new, isolated context object, ready to use immediately. |
There was a problem hiding this comment.
[nit] Drop "simply" (STYLEGUIDE §3): "…it returns a new, isolated context object…"
There was a problem hiding this comment.
Fixed in 2472841: "it returns a new, isolated context object". "simply" is gone from the page.
| const personalUser = await getCurrentUser(personalProfile); | ||
| ``` | ||
|
|
||
| ### Switching environments |
There was a problem hiding this comment.
[minor] Runtime environment switching is now explained here and twice on connect-to-existing-resources ("How it works" and "Several environments at the same time" / "Switching environments"). Consider keeping the full explanation in one place and linking to it from the other, so the three don't drift.
There was a problem hiding this comment.
Addressed in 2472841 by keeping the full explanation on this page and cutting the duplicate on the other one.
"Several environments at the same time" on connect-to-existing-resources now shows only the configuration-construction half, one createAmplifyContext() per environment, and links here for what a context isolates and why it is a client-side API. The duplicated fetchAuthSession calls and the isolation paragraph are gone, so there are two places instead of three and only one of them is the explanation.
|
|
||
| <InlineFilter filters={['swift', 'android', 'flutter']}> | ||
|
|
||
| Fields of the `auth` block in `amplify_outputs.json`: |
There was a problem hiding this comment.
[minor] The heading says these are fields of the block in amplify_outputs.json, but the table uses camelCase (awsRegion, userPoolId, userPoolClientId). The JSON keys, including in this page's own hand-written examples, are snake_case (aws_region, user_pool_id, user_pool_client_id). Same for the data, storage, analytics, geo and notifications native tables (752, 1013, 1145, 1304, 1442). Either switch to snake_case or relabel them as the typed builder's property names.
There was a problem hiding this comment.
Good catch, and it took two passes to land. I first took your snake_case option in 2472841, then reversed it in 64ef5ba and took your second one instead, because snake_case turned out to be the wrong half of the alternative here.
The reason: each of these six tables sits directly under a code example that builds AmplifyOutputsData in Swift and Kotlin or AmplifyOutputs in Dart, not under the JSON file. So the job of the table is to document that typed object, the same way the JS tables document ResourcesConfig[K]. Spelling the rows aws_region made the heading true but left every row matching no example on the page.
The tables are back to camelCase and the "Fields of the block in amplify_outputs.json" heading is deleted from all six, which is what made camelCase misleading in the first place.
Three consequences worth flagging, all verified against AmplifyOutputsData.swift rather than translated by eye:
- The Analytics rows are now
amazonPinpoint.awsRegionandamazonPinpoint.appId. The removed heading was carrying that nesting, and the example above the table nests the same way. - The
amazonConnectrow is removed rather than re-spelled.amazon_connectexists in schema v1.5, butAmplifyOutputsData.Notificationsdeclares onlyawsRegion,amazonPinpointAppIdandchannels, so documenting it in a code-shaped table would have described a field these platforms cannot set. - The
defaultAuthorizationTypevalues are now prose, "API key, Cognito user pools, IAM, Lambda, or OIDC", because each platform spells them as its own enum,.apiKeyin Swift and Dart againstAwsAppsyncAuthorizationType.API_KEYin Kotlin, so no single literal list is correct for all three.
Each table still closes with a link to the v1.5 schema for the full field set.
There was a problem hiding this comment.
Thanks. Dropping the headings and keeping camelCase reads right to me: I checked the rows against AmplifyOutputsData.swift, AmplifyOutputsData.kt and the Dart amplify_outputs/* classes, and auth, data, storage, geo and the new amazonPinpoint.* analytics rows match on all three.
[minor] One regression from 64ef5ba: the amazonConnect row was removed because Notifications "declares only awsRegion, amazonPinpointAppId and channels". That's true for Swift and Dart, but amplify-android does declare it:
// amplify-android core/src/main/java/com/amplifyframework/core/configuration/AmplifyOutputsData.kt
data class Notifications(
val awsRegion: String? = null,
val amazonPinpointAppId: String? = null,
val channels: List<AmazonPinpointChannels> = emptyList(),
val amazonConnect: AmazonConnect? = null
) {
data class AmazonConnect(
val awsRegion: String,
val endpoint: String
)
}So Android readers no longer learn they can configure Amazon Connect push. Could you restore the amazonConnect.awsRegion, amazonConnect.endpoint row (and the "Pinpoint or Amazon Connect" wording) inside an ['android'] filter?
[nit] The JS-only paragraph below the table still says "amazonConnect becomes Notifications.PushNotification.CustomerProfiles". With the native row gone, that name no longer points at anything on the page; the key in amplify_outputs.json is amazon_connect.
There was a problem hiding this comment.
Right on both counts, fixed in d785d21.
I verified your Kotlin snippet against the repo rather than taking it on trust, and also checked the other two so the filter split is correct:
- amplify-android
AmplifyOutputsData.kt:170-181:NotificationscarriesamazonConnect: AmazonConnect?with requiredawsRegionandendpoint. - amplify-swift
AmplifyOutputsData.swift:244-248:awsRegion,amazonPinpointAppId,channelsonly. - amplify-flutter
NotificationsOutputs: same three, no Amazon Connect field.
So the table is now split rather than shared: Swift and Flutter keep the three Pinpoint rows, and ["android"] gets a fourth row for amazonConnect.awsRegion / amazonConnect.endpoint plus a lead-in saying you configure either a Pinpoint project or an Amazon Connect endpoint, or both. Rendered and checked per route: the string amazonConnect appears on android and on neither swift nor flutter.
My error was generalising from one SDK to all three, which is exactly what the shared table invited. Worth noting the same hazard applies to the other five native tables, though those I did check across all three and they do match.
On the nit: the JS note now names the outputs-file key, "The amazon_connect block in amplify_outputs.json becomes Notifications.PushNotification.CustomerProfiles". That is the name a JS reader actually meets, and it is what parseNotifications maps from, so the sentence no longer depends on a row that is now Android-only.
Gates re-run on the staged state: prettier clean, cspell 3042 files 0 issues, 64 suites / 314 tests pass.
One consequence you should know about: this push dismissed osama-rizk's approval, since dismiss_stale_reviews is on. Philipp decided the regression was worth that.
|
|
||
| </InlineFilter> | ||
|
|
||
| In a `ResourcesConfig` object there is no `channels` list: which channels are active follows from the providers you configure, `PushNotification` and `InAppMessaging`. `amazonConnect` becomes `Notifications.PushNotification.CustomerProfiles` with `endpoint` and `region`. |
There was a problem hiding this comment.
[minor] This paragraph is after the native </InlineFilter> and before the next heading, so Swift/Android/Flutter readers see it. ResourcesConfig is a JS-only concept; wrap it in <InlineFilter filters={['angular', 'javascript', 'nextjs', 'react', 'react-native', 'vue']}>.
There was a problem hiding this comment.
Fixed in 2472841: the paragraph is wrapped in <InlineFilter filters={["angular", "javascript", "nextjs", "react", "react-native", "vue"]}>.
Verified by rendering rather than by reading the MDX: the swift, android and flutter routes now contain the string ResourcesConfig zero times, and the JS routes still show the paragraph.
|
|
||
| </BlockSwitcher> | ||
|
|
||
| <Callout informational> |
There was a problem hiding this comment.
[nit] informational isn't a Callout prop (it's ignored, so this still renders the default info style). <Callout info> matches the rest of the repo.
| <Callout informational> | |
| <Callout info> |
There was a problem hiding this comment.
Fixed in 2472841: <Callout info>, matching the rest of the repo. No informational prop is left on the page.
|
|
||
| There is no region field: the region is read from the user pool and identity pool IDs. `Cognito` accepts the user pool fields, the identity pool fields, or both, so an identity-pool-only configuration cannot carry `userPoolId`. | ||
|
|
||
| Every other field is optional. See [`AuthConfig`](https://github.com/aws-amplify/amplify-js/blob/main/packages/core/src/singleton/Auth/types.ts) for the full type. |
There was a problem hiding this comment.
[nit] This and the other type/schema links point at blob/main/..., so their content can move underneath the docs. Pinning to a release tag (e.g. aws-amplify@6.22.0) would be steadier, though main matches existing repo convention.
There was a problem hiding this comment.
Pinned in 2472841. All twelve links moved off blob/main: the six amplify-js type links to aws-amplify@6.22.0, and the six schema links to @aws-amplify/client-config@1.11.1.
I went with pinning over the existing repo convention because these links are cited as the authority for a specific field set, so silent drift underneath them is worse here than the inconsistency. Checked that each resolves, and that the pinned schema file really carries the 1.5 version constant.
osama-rizk
left a comment
There was a problem hiding this comment.
Impressively accurate docs PR — I cross-checked the API against the shipped code: createAmplifyContext(config, libraryOptions?) signature and ctx-first usage match, the createConfigurationBuilder methods (add/patch/auth/storage/api/build) match exactly, the headline field-table fix is right (JS uses ResourcesConfig paths region/userPoolId/endpoint while Swift/Android/Flutter keep the awsRegion outputs fields), "version": "1.5" + AWS_LAMBDA are consistent with the schema refresh, and the nav registers frontend/local-context directly above SSR. One real bug in the error-handling example, inline.
|
|
||
| ```typescript | ||
| import { getCurrentUser } from 'aws-amplify/auth'; | ||
| import { NoAmplifyContextError } from 'aws-amplify'; |
There was a problem hiding this comment.
This example imports a symbol that aws-amplify doesn't export, so it won't compile as written. NoAmplifyContextError / InvalidAmplifyContextError are internal to @aws-amplify/core (errors/) and thrown internally — I checked and there's no re-export from the aws-amplify umbrella root, @aws-amplify/core's public index.ts, or libraryUtils (which re-exports the name constant NO_AMPLIFY_CONTEXT_ERROR_NAME, not the class). A reader copying this gets Module '"aws-amplify"' has no exported member 'NoAmplifyContextError'.
Separately, error instanceof NoAmplifyContextError (line 182) isn't Amplify's convention even where a class is exported — Amplify errors extend AmplifyError with a stable name, and instanceof breaks across duplicate package copies. Use name-based catching, which works regardless of the export surface:
try {
await fetchAuthSession();
} catch (error) {
if (error?.name === 'NoAmplifyContextError') {
// handle unconfigured / missing context
}
throw error;
}(Verified against the context-migration branch; worth a quick confirm against the released v6.22 aws-amplify types, but the name-based pattern is correct either way and matches what the error classes' own JSDoc points to.)
There was a problem hiding this comment.
Both points were right and both are fixed in 2472841. The import is gone and the snippet catches by name:
import { getCurrentUser } from "aws-amplify/auth";
try {
const user = await getCurrentUser(ctx);
} catch (error) {
if (error instanceof Error && error.name === "NoAmplifyContextError") {
// No context available: call configure() or create one with createAmplifyContext()
}
throw error;
}Your second point is the one that changed my thinking. I had treated the missing export as the whole defect, but instanceof would have been the wrong pattern even with a public class, exactly for the duplicate-package-copies reason you give. The page now says so in prose, so a reader does not reintroduce it.
One correction on a sub-claim, which does not affect your conclusion or the fix: libraryUtils re-exports both the class and the name constant. packages/core/src/libraryUtils.ts:112-121 lists NoAmplifyContextError, NO_AMPLIFY_CONTEXT_ERROR_NAME, InvalidAmplifyContextError and INVALID_AMPLIFY_CONTEXT_ERROR_NAME together. So it is not name-constant-only. Your conclusion holds either way: that path is @aws-amplify/core/internals/utils, which is not somewhere docs should send readers, and nothing reaches the aws-amplify root.
Confirmed against released 6.22.0 rather than the migration branch, as you suggested. Same picture there, so Module "aws-amplify" has no exported member was exactly what a reader would have hit.
I kept error instanceof Error && ahead of the name check rather than your error?.name, because the file is TypeScript and a bare property read on an unknown catch binding does not narrow. Same behaviour at runtime.
soberm raised the same defect independently at line 177, and this commit answers both.
…pages Catch the context errors by name instead of importing them. The classes are internal to @aws-amplify/core (only reachable via core/internals/utils), so importing NoAmplifyContextError from aws-amplify does not compile; a stable name also survives duplicate package copies in one bundle. Correct three claims about the errors: they carry no code field, InvalidAmplifyContextError also fires for a non-context value in the first position, and a leading undefined only throws when another argument follows it. Spell the native required-fields tables with the real amplify_outputs.json keys (aws_region, user_pool_id, default_authorization_type, ...) rather than camelCase, since the heading says these are fields of the JSON file; each table notes that the typed builders take the same fields in camelCase. Pin the type and schema links to aws-amplify@6.22.0 and @aws-amplify/client-config@1.11.1 so their content cannot move underneath the docs. Keep the runtime environment-switching explanation on the local context page only; connect-to-existing-resources now shows how to build the per-environment configurations and links there for what a context isolates. Wrap the ResourcesConfig note after the Notifications tables in the JS InlineFilter; it sat where Swift, Android and Flutter readers saw it. Use the Callout info prop, which exists, instead of informational, which is ignored. And drop "simply" per the styleguide.
…onfiguration Revert the required-fields tables on the Swift, Android and Flutter routes to the camelCase names their typed configuration actually takes (awsRegion, userPoolId, defaultAuthorizationType, ...) and drop the "fields of the <x> block in amplify_outputs.json" heading above each one. The tables sit under code examples that build AmplifyOutputsData, so they document that object rather than the JSON file, the same way the JS tables document ResourcesConfig[K]. Prefix the Analytics rows with amazonPinpoint, since the removed heading was what carried that nesting and the example above the table nests the same way. Drop the amazonConnect row: schema v1.5 carries amazon_connect, but AmplifyOutputsData.Notifications declares only awsRegion, amazonPinpointAppId and channels, so there is no such field to configure on these platforms. State defaultAuthorizationType's values in prose, because each platform spells them as its own enum (.apiKey in Swift and Dart, API_KEY in Kotlin) and no single literal list is correct for all three.
64ef5ba dropped the amazonConnect row from the shared native Notifications table on the grounds that AmplifyOutputsData.Notifications declares only awsRegion, amazonPinpointAppId and channels. That holds for amplify-swift and amplify-flutter but not for amplify-android, whose Notifications carries amazonConnect: AmazonConnect? with required awsRegion and endpoint (core/src/main/java/com/amplifyframework/core/configuration/AmplifyOutputsData.kt), so Android readers lost a field they can configure. Split the table: Swift and Flutter keep the three Pinpoint rows, Android gets a fourth amazonConnect row and lead-in wording saying either provider, or both, can be configured. Name the outputs-file key in the JS note as well. It said amazonConnect, which matched no name a JS reader sees now that the native row is platform-filtered; the file's key is amazon_connect and that is what parseNotifications maps onto Notifications.PushNotification.CustomerProfiles.
Description of changes:
Two client-library APIs shipped with the AmplifyContext migration in
aws-amplifyv6.21.0 and v6.22.0 and had no documentation:createAmplifyContextandcreateConfigurationBuilder. This adds a page for the first and works the second into the existing configuration page.New page:
frontend/local-context. A local context is an isolated configuration plus its own auth state, created withcreateAmplifyContextand passed to a category API as its first argument. The page covers when to reach for one on the client (two profiles signed in at once, tenant switching, switching between alpha/beta/pre-prod/prod from one running app), how to create and use one, thatlibraryOptionsbelong to the context that received them, the two context errors, and why server-side code keeps usingrunWithAmplifyServerContextwith the/serverexports instead.Restructured:
frontend/connect-to-existing-resources. The page leads with the defaultAmplify.configure(outputs), then offers three ways to provide the configuration yourself as tabs: the configuration builder, a manualResourcesConfig, and a hand-written outputs file you import. Each per-category example gets the same two-tab treatment so the page reads consistently, and every example showscreateAmplifyContext(config)besideAmplify.configure(config).Fixed: the required-fields tables described the wrong object. Every JS example on that page builds a
ResourcesConfig, but the tables listedamplify_outputs.jsonfields, so a JS reader sawawsRegionabove a snippet typingregion, andurlabove one typingendpoint. The tables are now split per platform group: JS getsResourcesConfig[K]paths taken from the type definitions, Swift/Android/Flutter keep the outputs fields their typed builders take. Both are trimmed to fields that are actually required and link to the type definition or the schema for everything else.The outputs tables are also refreshed against schema v1.5, which the page was several versions behind:
EMAILas an MFA method,AWS_LAMBDAas an authorization type,amazon_connect,storage.buckets, the five now-requiredpassword_policyfields, and the relaxednotificationsrequired list. Hand-written JSON examples say"version": "1.5"rather than"1", which no longer validates against the current schema.src/directory/directory.mjsregisters the new page in the nav, directly above Server-Side Rendering. That page in turn links to the new one for client-side code that needs several configurations at once.Note for reviewers: the Swift, Android and Flutter pages change too, in the shared tables and the
versionvalues, not in their code examples.Related GitHub issue #, if available:
no linked issue: documents an already-released library feature
Instructions
If this PR should not be merged upon approval for any reason, please submit as a DRAFT
Which product(s) are affected by this PR (if applicable)?
Which platform(s) are affected by this PR (if applicable)?
Please add the product(s)/platform(s) affected to the PR title
Checks
Does this PR conform to the styleguide?
Does this PR include filetypes other than markdown or images? Please add or update unit tests accordingly.
src/directory/directory.mjsgains one nav entry; the existing directory unit tests cover it (64 suites / 314 tests pass).Are any files being deleted with this PR? If so, have the needed redirects been created?
No files deleted, so no redirects are needed.
Are all links in MDX files using the MDX link syntax rather than HTML link syntax?
All links use MDX syntax.
When this PR is ready to merge, please check the box below