diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index c812c8d9894..b0d80164c6d 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -843,6 +843,9 @@ export const directory = { } ] }, + { + path: 'src/pages/[platform]/frontend/local-context/index.mdx' + }, { path: 'src/pages/[platform]/frontend/server-side-rendering/index.mdx', children: [ diff --git a/src/pages/[platform]/frontend/connect-to-existing-resources/index.mdx b/src/pages/[platform]/frontend/connect-to-existing-resources/index.mdx index 826899e520c..a29d35b0d2b 100644 --- a/src/pages/[platform]/frontend/connect-to-existing-resources/index.mdx +++ b/src/pages/[platform]/frontend/connect-to-existing-resources/index.mdx @@ -3,7 +3,7 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Connect to existing AWS resources', description: - 'Use Amplify client libraries with your own AWS infrastructure. Configure Auth, Data, Storage, Analytics, Geo, and Notifications programmatically or via amplify_outputs.json — no Amplify backend required.', + 'Use Amplify client libraries with your own AWS infrastructure. Configure Auth, Data, Storage, Analytics, Geo, and Notifications programmatically or via amplify_outputs.json, with no Amplify backend required.', platforms: [ 'android', 'angular', @@ -32,7 +32,7 @@ export function getStaticProps(context) { Amplify client libraries can be used **independently** without the Amplify backend workflow. If you've provisioned AWS resources with CDK, Terraform, CloudFormation, or the AWS Console, you can connect Amplify libraries directly to those resources. -This means you can adopt Amplify's client libraries for authentication, data, storage, and more — while keeping full control over your infrastructure. +This means you can adopt Amplify's client libraries for authentication, data, storage, and more, while keeping full control over your infrastructure. ## When to use this approach @@ -45,39 +45,90 @@ This means you can adopt Amplify's client libraries for authentication, data, st -There are two ways to configure Amplify client libraries with your own resources: +The default path is a generated `amplify_outputs.json` file that you hand to `Amplify.configure()`: -### Option 1: Manual `amplify_outputs.json` +```typescript +import { Amplify } from 'aws-amplify'; +import outputs from './amplify_outputs.json'; -Create an `amplify_outputs.json` file in your project with the configuration for your resources. See the [full `amplify_outputs.json` specification](/[platform]/reference/amplify_outputs/) for all supported fields. +Amplify.configure(outputs); +``` -```json title="amplify_outputs.json" -{ - "version": "1", - "auth": { - "aws_region": "us-east-1", - "user_pool_id": "us-east-1_abc123", - "user_pool_client_id": "abcdef123456" - }, - "storage": { - "aws_region": "us-east-1", - "bucket_name": "my-app-bucket" - } -} +When you own the infrastructure there is no generated file, so you provide the configuration yourself. There are three ways to do that: + + + + + +`createConfigurationBuilder`, exported from `aws-amplify`, assembles a `ResourcesConfig` step by step. Every method returns the builder, and `build()` produces a frozen `ResourcesConfig` that `Amplify.configure()` accepts: + +```typescript +import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify'; + +const config = createConfigurationBuilder() + .auth({ + Cognito: { + userPoolId: 'us-east-1_abc123', + userPoolClientId: 'abcdef123456' + } + }) + .storage({ S3: { bucket: 'my-app-bucket', region: 'us-east-1' } }) + .build(); + +Amplify.configure(config); +// or +const ctx = createAmplifyContext(config); ``` -Then configure Amplify in your app: +| Method | Description | +|--------|-------------| +| `from(seed)` | Merges an existing configuration in. Accepts anything `Amplify.configure()` accepts: a `ResourcesConfig`, a legacy `aws-exports` object, `amplify_outputs.json`, or **another builder**. Can be called repeatedly to accumulate, and is also available as `createConfigurationBuilder({ from })`. | +| `add(category, value)` | Adds a category, **replacing** it entirely if already present. | +| `patch(category, partial)` | Deep-merges a partial into a category, keeping nested values you don't mention. Creates the category if it doesn't exist. | +| `auth()`, `api()`, `storage()`, `analytics()`, `geo()`, `notifications()`, `interactions()`, `predictions()` | Shorthands for `add()` with that category: `.auth(config)` is `.add('Auth', config)`. | +| `build()` | Returns the finished, frozen `ResourcesConfig`. | + +**Configure part of a configuration.** `patch()` deep-merges into a category, so you can change one nested value and leave the rest of it alone. This is the difference between the two write methods: `add('Auth', …)` (and its `auth()` shorthand) discards whatever `Auth` held, while `patch()` keeps it. ```typescript -import { Amplify } from 'aws-amplify'; -import outputs from './amplify_outputs.json'; +const config = createConfigurationBuilder({ from: outputs }) + // Everything else under Auth.Cognito survives + .patch('Auth', { Cognito: { userPoolId: 'us-east-1_dev123' } }) + // Add or overwrite a category outright, whether or not the outputs file carries it + .add('Storage', { S3: { bucket: 'my-platform-bucket', region: 'us-east-1' } }) + .build(); +``` -Amplify.configure(outputs); +**Extend a base builder.** A builder is itself a valid seed, so one shared base can produce environment-specific configurations without being mutated: + +```typescript +// Owned by your platform team, never passed to configure() directly +const base = createConfigurationBuilder({ from: outputs }); + +const dev = createConfigurationBuilder({ from: base }) + .patch('Auth', { Cognito: { userPoolId: 'us-east-1_dev123' } }) + .build(); + +const prod = createConfigurationBuilder({ from: base }) + .patch('Auth', { Cognito: { userPoolId: 'us-east-1_prod789' } }) + .build(); +``` + +**Write generators.** Because `build()` returns a plain object, a function that closes over the shared base turns a single varying value into a whole configuration: + +```typescript +const withUserPool = (userPoolId: string) => + createConfigurationBuilder({ from: base }) + .patch('Auth', { Cognito: { userPoolId } }) + .build(); + +Amplify.configure(withUserPool('us-east-1_dev123')); ``` -### Option 2: Direct `Amplify.configure()` with a `ResourcesConfig` object + + -Pass the configuration directly without a JSON file: +Pass a `ResourcesConfig` object straight to `Amplify.configure()`. Nothing is merged for you, so every call states the complete configuration: ```typescript import { Amplify } from 'aws-amplify'; @@ -102,6 +153,45 @@ Amplify.configure({ }); ``` +This is the most direct option when your app has exactly one configuration and it never varies at runtime. + + + + +Keep the configuration in a file of your own and import it, the way a generated `amplify_outputs.json` is imported. Amplify only cares about the shape, so the file name is yours to choose. See the [full `amplify_outputs.json` specification](/[platform]/reference/amplify_outputs/) for every supported field: + +```json title="my_resources.json" +{ + "version": "1.5", + "auth": { + "aws_region": "us-east-1", + "user_pool_id": "us-east-1_abc123", + "user_pool_client_id": "abcdef123456" + }, + "storage": { + "aws_region": "us-east-1", + "bucket_name": "my-app-bucket" + } +} +``` + +```typescript +import { Amplify, createAmplifyContext } from 'aws-amplify'; +import resources from './my_resources.json'; + +Amplify.configure(resources); +// or +const ctx = createAmplifyContext(resources); +``` + +Because the file is checked in as data, this option keeps the configuration out of your application code while still letting you hold several of them: one file per environment, each imported where it is needed. + + + + + +The built `ResourcesConfig` is a plain object either way, so it works anywhere a configuration is accepted: pass it to `Amplify.configure()` to set it globally, or to `createAmplifyContext()` to create an isolated [local context](/[platform]/frontend/local-context/) instead of touching global state. + @@ -114,7 +204,7 @@ Create an `amplify_outputs.json` file in your Xcode project. See the [full speci ```json title="amplify_outputs.json" { - "version": "1", + "version": "1.5", "auth": { "aws_region": "us-east-1", "user_pool_id": "us-east-1_abc123", @@ -152,9 +242,9 @@ try Amplify.configure(config) ``` This approach is ideal for: -- **Unit testing** — Configure Amplify without bundling JSON files -- **Environment switching** — Build different configurations for dev/staging/prod -- **Dynamic configuration** — Fetch configuration from a remote source at runtime +- **Unit testing:** Configure Amplify without bundling JSON files +- **Environment switching:** Build different configurations for dev/staging/prod +- **Dynamic configuration:** Fetch configuration from a remote source at runtime @@ -168,7 +258,7 @@ Create an `amplify_outputs.json` file in your `app/src/main/res/raw/` directory. ```json title="amplify_outputs.json" { - "version": "1", + "version": "1.5", "auth": { "aws_region": "us-east-1", "user_pool_id": "us-east-1_abc123", @@ -207,9 +297,9 @@ Amplify.configure(config, applicationContext) ``` This approach is ideal for: -- **Unit testing** — Configure Amplify without bundling JSON files -- **Environment switching** — Build different configurations for dev/staging/prod -- **Dynamic configuration** — Fetch configuration from a remote source at runtime +- **Unit testing:** Configure Amplify without bundling JSON files +- **Environment switching:** Build different configurations for dev/staging/prod +- **Dynamic configuration:** Fetch configuration from a remote source at runtime @@ -223,7 +313,7 @@ Create an `amplify_outputs.json` file with your resource configuration. See the ```json title="amplify_outputs.json" { - "version": "1", + "version": "1.5", "auth": { "aws_region": "us-east-1", "user_pool_id": "us-east-1_abc123", @@ -267,9 +357,9 @@ await Amplify.configure(config); ``` This approach is ideal for: -- **Unit testing** — Configure Amplify without bundling files -- **Environment switching** — Build different configurations for dev/staging/prod -- **Dynamic configuration** — Fetch configuration from a remote source at runtime +- **Unit testing:** Configure Amplify without bundling files +- **Environment switching:** Build different configurations for dev/staging/prod +- **Dynamic configuration:** Fetch configuration from a remote source at runtime @@ -279,7 +369,49 @@ Connect to an existing Cognito User Pool and Identity Pool. + + + + +```typescript +import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify'; + +const config = createConfigurationBuilder() + .auth({ + Cognito: { + userPoolId: 'us-east-1_abc123', + userPoolClientId: 'abcdef123456', + identityPoolId: 'us-east-1:11111111-2222-3333-4444-555555555555', + loginWith: { + email: true + }, + signUpVerificationMethod: 'code', + userAttributes: { + email: { required: true } + }, + allowGuestAccess: true, + passwordFormat: { + minLength: 8, + requireLowercase: true, + requireUppercase: true, + requireNumbers: true, + requireSpecialCharacters: true + } + } + }) + .build(); + +Amplify.configure(config); +// or +const ctx = createAmplifyContext(config); +``` + + + + ```typescript +import { Amplify } from 'aws-amplify'; + Amplify.configure({ Auth: { Cognito: { @@ -306,6 +438,10 @@ Amplify.configure({ }); ``` + + + + @@ -439,16 +575,33 @@ await Amplify.configure(config); ### Auth required fields + + +Fields of `ResourcesConfig['Auth']`, the object both tabs above build: + +| Field | Required | Description | +|-------|----------|-------------| +| `Cognito.userPoolId` | With a user pool | Cognito User Pool ID | +| `Cognito.userPoolClientId` | With a user pool | Cognito app client ID | +| `Cognito.identityPoolId` | With an identity pool | Cognito Identity Pool ID, needed for guest access and for IAM-signed requests | + +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/aws-amplify%406.22.0/packages/core/src/singleton/Auth/types.ts) for the full type. + + + + + | Field | Required | Description | |-------|----------|-------------| | `awsRegion` | Yes | AWS region (e.g. `us-east-1`) | | `userPoolId` | Yes | Cognito User Pool ID | | `userPoolClientId` | Yes | Cognito app client ID | -| `identityPoolId` | No | Cognito Identity Pool ID (needed for guest access and IAM-based auth) | -| `passwordPolicy` | No | Password requirements (min length, character types) | -| `oauth` | No | OAuth/Hosted UI configuration (social sign-in) | -| `mfaConfiguration` | No | MFA mode: `NONE`, `OPTIONAL`, or `REQUIRED` | -| `mfaMethods` | No | MFA types: `SMS`, `TOTP` | + +Every other field is optional. See the [`amplify_outputs.json` schema](https://github.com/aws-amplify/amplify-backend/blob/%40aws-amplify%2Fclient-config%401.11.1/packages/client-config/src/client-config-schema/schema_v1.5.json) for the full shape. + + ## Configure Data (AWS AppSync) @@ -456,26 +609,59 @@ Connect to an existing AppSync GraphQL API. -```json title="amplify_outputs.json" -{ - "version": "1", - "data": { - "aws_region": "us-east-1", - "url": "https://abc123.appsync-api.us-east-1.amazonaws.com/graphql", - "api_key": "da2-abcdefghijklmno", - "default_authorization_type": "API_KEY", - "authorization_types": ["API_KEY"] - } -} + + + + +```typescript +import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify'; + +const config = createConfigurationBuilder() + .api({ + GraphQL: { + endpoint: + 'https://abc123.appsync-api.us-east-1.amazonaws.com/graphql', + region: 'us-east-1', + apiKey: 'da2-abcdefghijklmno', + defaultAuthMode: 'apiKey' + } + }) + .build(); + +Amplify.configure(config); +// or +const ctx = createAmplifyContext(config); ``` + + + ```typescript import { Amplify } from 'aws-amplify'; -import outputs from './amplify_outputs.json'; -Amplify.configure(outputs); +Amplify.configure({ + API: { + GraphQL: { + endpoint: + 'https://abc123.appsync-api.us-east-1.amazonaws.com/graphql', + region: 'us-east-1', + apiKey: 'da2-abcdefghijklmno', + defaultAuthMode: 'apiKey' + } + } +}); ``` + + + + + + +In a `ResourcesConfig` object the AppSync API lives under `API.GraphQL`, and `defaultAuthMode` takes the client-side value, one of `apiKey`, `userPool`, `identityPool`, `oidc`, `lambda`, or `none`, rather than the `API_KEY` style used in `amplify_outputs.json`. + + + @@ -540,13 +726,34 @@ await Amplify.configure(config); ### Data required fields + + +Fields of `ResourcesConfig['API']`, the object both tabs above build: + +| Field | Required | Description | +|-------|----------|-------------| +| `GraphQL.endpoint` | Yes | AppSync GraphQL endpoint URL | +| `GraphQL.defaultAuthMode` | Yes | `apiKey`, `userPool`, `identityPool`, `oidc`, `lambda`, or `none`. `iam` is the deprecated spelling of `identityPool` | +| `GraphQL.apiKey` | With `apiKey` auth | AppSync API key | +| `GraphQL.region` | With `identityPool` auth | Region used to sign the request | + +Every other field is optional. See [`APIConfig`](https://github.com/aws-amplify/amplify-js/blob/aws-amplify%406.22.0/packages/core/src/singleton/API/types.ts) for the full type. + + + + + | Field | Required | Description | |-------|----------|-------------| | `awsRegion` | Yes | AWS region | | `url` | Yes | AppSync GraphQL endpoint URL | -| `defaultAuthorizationType` | Yes | Default auth mode: `API_KEY`, `AMAZON_COGNITO_USER_POOLS`, `AWS_IAM`, or `OPENID_CONNECT` | -| `authorizationTypes` | Yes | All supported auth modes | -| `apiKey` | No | Required if using `API_KEY` auth | +| `defaultAuthorizationType` | Yes | Default auth mode: API key, Cognito user pools, IAM, Lambda, or OIDC | +| `authorizationTypes` | Yes | Every auth mode the API accepts, from the same list | +| `apiKey` | With API key auth | AppSync API key | + +Every other field is optional. See the [`amplify_outputs.json` schema](https://github.com/aws-amplify/amplify-backend/blob/%40aws-amplify%2Fclient-config%401.11.1/packages/client-config/src/client-config-schema/schema_v1.5.json) for the full shape. + + ## Configure Storage (Amazon S3) @@ -554,7 +761,35 @@ Connect to an existing S3 bucket. + + + + +```typescript +import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify'; + +const config = createConfigurationBuilder() + .auth({ + Cognito: { + userPoolId: 'us-east-1_abc123', + userPoolClientId: 'abcdef123456', + identityPoolId: 'us-east-1:11111111-2222-3333-4444-555555555555' + } + }) + .storage({ S3: { bucket: 'my-app-bucket', region: 'us-east-1' } }) + .build(); + +Amplify.configure(config); +// or +const ctx = createAmplifyContext(config); +``` + + + + ```typescript +import { Amplify } from 'aws-amplify'; + Amplify.configure({ Auth: { Cognito: { @@ -572,6 +807,53 @@ Amplify.configure({ }); ``` + + + + +### Multiple buckets + + + + + +```typescript +const config = createConfigurationBuilder() + .storage({ + S3: { + bucket: 'primary-bucket', + region: 'us-east-1', + buckets: { + media: { bucketName: 'my-media-bucket', region: 'us-east-1' }, + logs: { bucketName: 'my-logs-bucket', region: 'us-west-2' } + } + } + }) + .build(); +``` + + + + +```typescript +Amplify.configure({ + Storage: { + S3: { + bucket: 'primary-bucket', + region: 'us-east-1', + buckets: { + media: { bucketName: 'my-media-bucket', region: 'us-east-1' }, + logs: { bucketName: 'my-logs-bucket', region: 'us-west-2' } + } + } + } +}); +``` + + + + + @@ -696,7 +978,7 @@ final config = AmplifyOutputs( - + Storage requires Auth (Cognito Identity Pool) for authorization. Always configure Auth alongside Storage. @@ -704,11 +986,31 @@ Storage requires Auth (Cognito Identity Pool) for authorization. Always configur ### Storage required fields + + +Fields of `ResourcesConfig['Storage']`, the object both tabs above build: + +| Field | Required | Description | +|-------|----------|-------------| +| `S3.bucket` | For the default bucket | Bucket used by calls that do not name one | +| `S3.region` | For the default bucket | Region of that bucket | +| `S3.buckets[name].bucketName`, `S3.buckets[name].region` | With additional buckets | Each extra bucket is keyed by the friendly name you pass to the Storage APIs | + +Every other field is optional. See [`StorageConfig`](https://github.com/aws-amplify/amplify-js/blob/aws-amplify%406.22.0/packages/core/src/singleton/Storage/types.ts) for the full type. + + + + + | Field | Required | Description | |-------|----------|-------------| | `awsRegion` | Yes | AWS region | | `bucketName` | Yes | Default S3 bucket name | -| `buckets` | No | Additional named buckets for multi-bucket setups | +| `buckets[].name`, `buckets[].bucketName`, `buckets[].awsRegion` | With additional buckets | All three are required on every entry | + +Every other field is optional. See the [`amplify_outputs.json` schema](https://github.com/aws-amplify/amplify-backend/blob/%40aws-amplify%2Fclient-config%401.11.1/packages/client-config/src/client-config-schema/schema_v1.5.json) for the full shape. + + ## Configure Analytics (Amazon Pinpoint) @@ -716,18 +1018,47 @@ Connect to an existing Pinpoint application. -```json title="amplify_outputs.json" -{ - "version": "1", - "analytics": { - "amazon_pinpoint": { - "aws_region": "us-east-1", - "app_id": "abc123def456" + + + + +```typescript +import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify'; + +const config = createConfigurationBuilder() + .analytics({ + Pinpoint: { + appId: 'abc123def456', + region: 'us-east-1' + } + }) + .build(); + +Amplify.configure(config); +// or +const ctx = createAmplifyContext(config); +``` + + + + +```typescript +import { Amplify } from 'aws-amplify'; + +Amplify.configure({ + Analytics: { + Pinpoint: { + appId: 'abc123def456', + region: 'us-east-1' } } -} +}); ``` + + + + @@ -784,35 +1115,101 @@ await Amplify.configure(config); +### Analytics required fields + + + +Fields of `ResourcesConfig['Analytics']`, the object both tabs above build: + +| Field | Required | Description | +|-------|----------|-------------| +| `Pinpoint.appId` | Yes | Pinpoint application (project) ID | +| `Pinpoint.region` | Yes | Region of the Pinpoint project | + +Every other field is optional. See [`AnalyticsConfig`](https://github.com/aws-amplify/amplify-js/blob/aws-amplify%406.22.0/packages/core/src/singleton/Analytics/types.ts) for the full type. + + + + + +| Field | Required | Description | +|-------|----------|-------------| +| `amazonPinpoint.awsRegion` | Yes | AWS region of the Pinpoint project | +| `amazonPinpoint.appId` | Yes | Pinpoint application (project) ID | + +Every other field is optional. See the [`amplify_outputs.json` schema](https://github.com/aws-amplify/amplify-backend/blob/%40aws-amplify%2Fclient-config%401.11.1/packages/client-config/src/client-config-schema/schema_v1.5.json) for the full shape. + + + ## Configure Geo (Amazon Location Service) Connect to existing Location Service resources. -```json title="amplify_outputs.json" -{ - "version": "1", - "geo": { - "aws_region": "us-east-1", - "maps": { - "items": { - "myMap": { "style": "VectorEsriStreets" } + + + + +```typescript +import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify'; + +const config = createConfigurationBuilder() + .geo({ + LocationService: { + region: 'us-east-1', + maps: { + items: { myMap: { style: 'VectorEsriStreets' } }, + default: 'myMap' }, - "default": "myMap" - }, - "search_indices": { - "items": ["myPlaceIndex"], - "default": "myPlaceIndex" - }, - "geofence_collections": { - "items": ["myGeofenceCollection"], - "default": "myGeofenceCollection" + searchIndices: { + items: ['myPlaceIndex'], + default: 'myPlaceIndex' + }, + geofenceCollections: { + items: ['myGeofenceCollection'], + default: 'myGeofenceCollection' + } + } + }) + .build(); + +Amplify.configure(config); +// or +const ctx = createAmplifyContext(config); +``` + + + + +```typescript +import { Amplify } from 'aws-amplify'; + +Amplify.configure({ + Geo: { + LocationService: { + region: 'us-east-1', + maps: { + items: { myMap: { style: 'VectorEsriStreets' } }, + default: 'myMap' + }, + searchIndices: { + items: ['myPlaceIndex'], + default: 'myPlaceIndex' + }, + geofenceCollections: { + items: ['myGeofenceCollection'], + default: 'myGeofenceCollection' + } } } -} +}); ``` + + + + @@ -873,6 +1270,38 @@ Amplify.configure(config, applicationContext) +### Geo required fields + + + +Fields of `ResourcesConfig['Geo']`, the object both tabs above build: + +| Field | Required | Description | +|-------|----------|-------------| +| `LocationService.region` | Yes | Region of the Location Service resources | +| `LocationService.maps.items`, `LocationService.maps.default` | With `maps` | Maps keyed by name, plus the default map name | +| `LocationService.searchIndices.items`, `LocationService.searchIndices.default` | With `searchIndices` | Place index names, plus the default index | +| `LocationService.geofenceCollections.items`, `LocationService.geofenceCollections.default` | With `geofenceCollections` | Collection names, plus the default collection | + +Every other field is optional. See [`GeoConfig`](https://github.com/aws-amplify/amplify-js/blob/aws-amplify%406.22.0/packages/core/src/singleton/Geo/types.ts) for the full type. + + + + + +| Field | Required | Description | +|-------|----------|-------------| +| `awsRegion` | Yes | AWS region of the Location Service resources | +| `maps.items`, `maps.default` | With `maps` | Maps keyed by name, plus the default map name | +| `searchIndices.items`, `searchIndices.default` | With `searchIndices` | Place index names, plus the default index | +| `geofenceCollections.items`, `geofenceCollections.default` | With `geofenceCollections` | Collection names, plus the default collection | + +Every other field is optional. See the [`amplify_outputs.json` schema](https://github.com/aws-amplify/amplify-backend/blob/%40aws-amplify%2Fclient-config%401.11.1/packages/client-config/src/client-config-schema/schema_v1.5.json) for the full shape. + + + +Each resource group is optional on its own, but configure the ones your app actually calls: the Geo APIs need a default for the resource type they use. + ## Configure Notifications (Push) Connect to an existing Pinpoint application for push notifications. @@ -914,11 +1343,11 @@ Amplify.configure(config, applicationContext) - + ```json title="amplify_outputs.json" { - "version": "1", + "version": "1.5", "notifications": { "aws_region": "us-east-1", "amazon_pinpoint_app_id": "abc123def456", @@ -929,6 +1358,108 @@ Amplify.configure(config, applicationContext) + + + + + + +```typescript +import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify'; + +const config = createConfigurationBuilder() + .notifications({ + PushNotification: { + Pinpoint: { appId: 'abc123def456', region: 'us-east-1' } + }, + InAppMessaging: { + Pinpoint: { appId: 'abc123def456', region: 'us-east-1' } + } + }) + .build(); + +Amplify.configure(config); +// or +const ctx = createAmplifyContext(config); +``` + + + + +```typescript +import { Amplify } from 'aws-amplify'; + +Amplify.configure({ + Notifications: { + PushNotification: { + Pinpoint: { appId: 'abc123def456', region: 'us-east-1' } + }, + InAppMessaging: { + Pinpoint: { appId: 'abc123def456', region: 'us-east-1' } + } + } +}); +``` + + + + + + + +### Notifications required fields + + + +Fields of `ResourcesConfig['Notifications']`, the object both tabs above build. At least one of `PushNotification` and `InAppMessaging` must be present: + +| Field | Required | Description | +|-------|----------|-------------| +| `PushNotification.Pinpoint.appId`, `PushNotification.Pinpoint.region` | For Pinpoint push | Pinpoint project backing push device registration | +| `PushNotification.CustomerProfiles.endpoint`, `PushNotification.CustomerProfiles.region` | For Amazon Connect push | Amazon Connect Customer Profiles endpoint backing push device registration | +| `InAppMessaging.Pinpoint.appId`, `InAppMessaging.Pinpoint.region` | For in-app messaging | Pinpoint project backing in-app messages | + +Every other field is optional. See [`NotificationsConfig`](https://github.com/aws-amplify/amplify-js/blob/aws-amplify%406.22.0/packages/core/src/singleton/Notifications/types.ts) for the full type. + + + + + +| Field | Required | Description | +|-------|----------|-------------| +| `awsRegion` | For Pinpoint | AWS region of the Pinpoint project | +| `amazonPinpointAppId` | For Pinpoint | Pinpoint application ID | +| `channels` | For Pinpoint | Channels to enable: APNS, FCM, in-app messaging, email, or SMS. At least one entry, no duplicates | + +A Pinpoint setup needs all three: the region, the app ID, and at least one channel. + +Every other field is optional. See the [`amplify_outputs.json` schema](https://github.com/aws-amplify/amplify-backend/blob/%40aws-amplify%2Fclient-config%401.11.1/packages/client-config/src/client-config-schema/schema_v1.5.json) for the full shape. + + + + + +Configure either a Pinpoint project or an Amazon Connect endpoint, or both: + +| Field | Required | Description | +|-------|----------|-------------| +| `awsRegion` | For Pinpoint | AWS region of the Pinpoint project | +| `amazonPinpointAppId` | For Pinpoint | Pinpoint application ID | +| `channels` | For Pinpoint | Channels to enable: APNS, FCM, in-app messaging, email, or SMS. At least one entry, no duplicates | +| `amazonConnect.awsRegion`, `amazonConnect.endpoint` | For Amazon Connect | Amazon Connect Customer Profiles endpoint backing push device registration. Both are required on the entry | + +A Pinpoint setup needs all three Pinpoint fields: the region, the app ID, and at least one channel. + +Every other field is optional. See the [`amplify_outputs.json` schema](https://github.com/aws-amplify/amplify-backend/blob/%40aws-amplify%2Fclient-config%401.11.1/packages/client-config/src/client-config-schema/schema_v1.5.json) for the full shape. + + + + + +In a `ResourcesConfig` object there is no `channels` list: which channels are active follows from the providers you configure, `PushNotification` and `InAppMessaging`. The `amazon_connect` block in `amplify_outputs.json` becomes `Notifications.PushNotification.CustomerProfiles` with `endpoint` and `region`. + + + ## Multi-category configuration You can configure multiple services together. This example sets up Auth, Data, and Storage in a single configuration. @@ -1028,7 +1559,45 @@ await Amplify.configure(config); + + + + +```typescript +import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify'; + +const config = createConfigurationBuilder() + .auth({ + Cognito: { + userPoolId: 'us-east-1_abc123', + userPoolClientId: 'abcdef123456', + identityPoolId: 'us-east-1:11111111-2222-3333-4444-555555555555' + } + }) + .api({ + GraphQL: { + endpoint: + 'https://abc123.appsync-api.us-east-1.amazonaws.com/graphql', + region: 'us-east-1', + defaultAuthMode: 'userPool' + } + }) + .storage({ S3: { bucket: 'my-app-bucket', region: 'us-east-1' } }) + .build(); + +Amplify.configure(config); +// or +const ctx = createAmplifyContext(config); +``` + +Chaining also means a category can come from somewhere else entirely: seed the builder with a shared configuration and add only what this app owns. + + + + ```typescript +import { Amplify } from 'aws-amplify'; + Amplify.configure({ Auth: { Cognito: { @@ -1039,7 +1608,9 @@ Amplify.configure({ }, API: { GraphQL: { - endpoint: 'https://abc123.appsync-api.us-east-1.amazonaws.com/graphql', + endpoint: + 'https://abc123.appsync-api.us-east-1.amazonaws.com/graphql', + region: 'us-east-1', defaultAuthMode: 'userPool' } }, @@ -1052,6 +1623,10 @@ Amplify.configure({ }); ``` + + + + @@ -1182,7 +1757,28 @@ await Amplify.configure(config); -You can maintain separate `amplify_outputs.json` files per environment: +Keep one configuration per environment and select it at startup. With the builder, derive each one from a shared base so only the differing fields are restated. The generator pattern from [How it works](#how-it-works) applies directly: + +```typescript +import { Amplify, createConfigurationBuilder } from 'aws-amplify'; +import outputs from './amplify_outputs.json'; + +const base = createConfigurationBuilder({ from: outputs }); + +const withUserPool = (userPoolId: string) => + createConfigurationBuilder({ from: base }) + .patch('Auth', { Cognito: { userPoolId } }) + .build(); + +const configs = { + dev: withUserPool('us-east-1_dev123'), + prod: withUserPool('us-east-1_prod789') +}; + +Amplify.configure(configs[selectedEnvironment]); +``` + +If the environments share nothing, keep a separate outputs file per environment instead: ```typescript import devOutputs from './amplify_outputs.dev.json'; @@ -1192,6 +1788,21 @@ const outputs = process.env.NODE_ENV === 'production' ? prodOutputs : devOutputs Amplify.configure(outputs); ``` +### Several environments at the same time + +`Amplify.configure()` sets one process-wide configuration, so selecting an environment this way means the whole app moves with it. To keep more than one environment live at once, pass each configuration to `createAmplifyContext()` instead of to `configure()`, then hand the resulting context to the category APIs: + +```typescript +import { createAmplifyContext } from 'aws-amplify'; + +const environments = { + dev: createAmplifyContext(withUserPool('us-east-1_dev123')), + prod: createAmplifyContext(withUserPool('us-east-1_prod789')) +}; +``` + +See [Local context](/[platform]/frontend/local-context/) for what a context isolates, how to use one, and why it is a client-side API. + ## `amplify_outputs.json` schema reference diff --git a/src/pages/[platform]/frontend/local-context/index.mdx b/src/pages/[platform]/frontend/local-context/index.mdx new file mode 100644 index 00000000000..53f5bb0870c --- /dev/null +++ b/src/pages/[platform]/frontend/local-context/index.mdx @@ -0,0 +1,203 @@ +import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; + +export const meta = { + title: 'Local context', + description: + 'Create an isolated, local AmplifyContext with createAmplifyContext and pass it directly to Amplify category APIs, without relying on global singleton configuration.', + platforms: [ + 'angular', + 'javascript', + 'nextjs', + 'react', + 'react-native', + 'vue' + ] +}; + +export const getStaticPaths = async () => { + return getCustomStaticPath(meta.platforms); +}; + +export function getStaticProps(context) { + return { + props: { + platform: context.params.platform, + meta + } + }; +} + +By default, Amplify uses a single, process-wide configuration created by `Amplify.configure()`. Every category API call (for example `signIn`, `fetchAuthSession`, or `getCurrentUser`) reads from this global singleton. That is the right default when your app talks to exactly one backend as one user at a time, but it means there is only ever **one** configuration and **one** auth state in the process. + +`aws-amplify` exposes `createAmplifyContext` for everything else. It builds a **local** `AmplifyContext`: an isolated configuration and auth handle that you pass explicitly to category APIs as their **first argument**. Each context carries its own resource configuration and its own per-context auth instance, so contexts never share state with one another or with the global singleton. + +This is not a server-side API. For server-side runtimes, keep using the server exports and `runWithAmplifyServerContext`. See [Use a local context or a server context?](#use-a-local-context-or-a-server-context). + +## When to use a local context + +Reach for `createAmplifyContext` whenever a single global configuration is not enough: + +- **Multiple profiles signed in at once:** for example a work profile and a personal profile side by side, each with its own session, without signing one out to use the other. +- **Tenant switching:** hold a context per tenant/organization so switching does not tear down and re-configure Amplify globally. +- **Environment switching:** point a build at alpha, beta, pre-prod, or prod backends from the same running app, which is useful for internal tooling, QA harnesses, and dashboards that compare environments side by side. + + + +**Tip:** If your app talks to a single backend as one user at a time, keep using `Amplify.configure()` and call the APIs without a context argument. Nothing changes for you: the context parameter is optional and fully backward compatible. + + + +## Create and use a 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 returns a new, isolated context object, ready to use immediately. + +```typescript +import { createAmplifyContext } from 'aws-amplify'; +import { signIn, getCurrentUser, fetchUserAttributes } from 'aws-amplify/auth'; +import outputs from './amplify_outputs.json'; + +// Create the context +const ctx = createAmplifyContext(outputs); + +// Sign in against the configuration carried by `ctx` +await signIn(ctx, { username, password }); + +// Read the current user from that same context +const user = await getCurrentUser(ctx); +const attributes = await fetchUserAttributes(ctx); +``` + + + + +The context argument is optional. `Amplify.configure()` sets a single process-wide context, and calling an API without a leading context falls back to it, exactly as before local contexts existed. Nothing about this changes when you adopt `createAmplifyContext` elsewhere in your app. + +```typescript +import { Amplify } from 'aws-amplify'; +import { signIn, getCurrentUser, fetchUserAttributes } from 'aws-amplify/auth'; +import outputs from './amplify_outputs.json'; + +// Set the global context +Amplify.configure(outputs); + +// Uses the global context, no context argument +await signIn({ username, password }); + +const user = await getCurrentUser(); +const attributes = await fetchUserAttributes(); +``` + + + + + +### Library options are per context + +`createAmplifyContext` takes the same optional `libraryOptions` as `Amplify.configure()` as its second argument, and those options belong to that context alone. A custom token provider, credentials provider, or key-value storage passed here is used only by calls that receive this context: it is never shared with another context or with the global singleton. + +```typescript +const ctx = createAmplifyContext(outputs, { + Auth: { + tokenProvider, + credentialsProvider + } +}); +``` + +If you do not pass any, the context resolves its own per-context Amazon Cognito token and credentials providers backed by `localStorage`. Either way the resulting token store is scoped to the context, which is what lets two contexts hold two signed-in users at the same time. + +## Use multiple isolated contexts + +Because each context is fully isolated, you can hold several at once and they will not share configuration or auth state. Each keeps its own session, so a user signed in through one context is unaffected by the others. + +### Multiple profiles signed in at once + +A user can be signed in to a work profile and a personal profile simultaneously, each with its own configuration and its own independent session. + +```typescript +import { createAmplifyContext } from 'aws-amplify'; +import { signIn, getCurrentUser } from 'aws-amplify/auth'; +import workOutputs from './amplify_work_outputs.json'; +import personalOutputs from './amplify_personal_outputs.json'; + +const workProfile = createAmplifyContext(workOutputs); +const personalProfile = createAmplifyContext(personalOutputs); + +await signIn(workProfile, { username: workEmail, password: workPassword }); +await signIn(personalProfile, { username: personalEmail, password: personalPassword }); + +// Both sessions are live and independent +const workUser = await getCurrentUser(workProfile); +const personalUser = await getCurrentUser(personalProfile); +``` + +### Switching environments + +Point the same running app at different backends, for example alpha, beta, pre-prod, and prod, without reconfiguring Amplify globally. This is useful for internal tools and QA dashboards that compare environments side by side. + +```typescript +import { createAmplifyContext } from 'aws-amplify'; +import { fetchAuthSession } from 'aws-amplify/auth'; +import alphaOutputs from './outputs/alpha.json'; +import betaOutputs from './outputs/beta.json'; +import prodOutputs from './outputs/prod.json'; + +const environments = { + alpha: createAmplifyContext(alphaOutputs), + beta: createAmplifyContext(betaOutputs), + prod: createAmplifyContext(prodOutputs) +}; + +// Select the environment at runtime, no global reconfiguration needed +const ctx = environments[selectedEnvironment]; +const session = await fetchAuthSession(ctx); +``` + + + +**Note:** Before local contexts, switching backends at runtime meant calling `Amplify.configure()` again, which replaced the configuration for the whole app and reset the global auth state. Holding one context per environment avoids that. + + + +## Error handling + +Context resolution surfaces two typed errors with stable `name` values, so you can catch them uniformly: + +| 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 `undefined` was passed as the leading context argument followed by other arguments. | +| `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. | + +Catch them by `name`. The error classes are internal, so there is nothing to import, and a stable name keeps working even when two copies of a package end up in the same bundle: + +```typescript +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; +} +``` + +## Use a local context or a server context? + +Local contexts are for **client-side** code. Server-side runtimes have their own mechanism, and the two are not interchangeable: + +- **Client:** use `createAmplifyContext` from `aws-amplify` and pass the context to the standard category APIs, as shown above. +- **Server:** use `runWithAmplifyServerContext` from `@aws-amplify/adapter-nextjs`, calling the APIs exported from the `aws-amplify//server` sub-paths inside the `operation` callback. See [Server-Side Rendering](/[platform]/frontend/server-side-rendering/). + +The server exports exist to constrain the API surface: only a subset of Amplify APIs is supported server-side, and the `/server` sub-paths are what make that subset explicit. Reaching for a local context on the server would sidestep that boundary and let you call APIs that are not supported in a server runtime. Use the server context instead: it also derives auth state from the incoming request's cookies, which a local context does not do. + + + +Need more than one Amplify configuration, or more than one signed-in identity, at the same time in your client app? A local `AmplifyContext` is the recommended pattern. + + diff --git a/src/pages/[platform]/frontend/server-side-rendering/index.mdx b/src/pages/[platform]/frontend/server-side-rendering/index.mdx index 21ffbac0bc0..46b04609498 100644 --- a/src/pages/[platform]/frontend/server-side-rendering/index.mdx +++ b/src/pages/[platform]/frontend/server-side-rendering/index.mdx @@ -78,6 +78,12 @@ You can use the exported `runWithAmplifyServerContext` function to call Amplify **Tip:** You only need to call the `createServerRunner` function once and reuse the `runWithAmplifyServerContext` function throughout. + + +**Note:** If your **client-side** code needs to hold several Amplify configurations at once, for example one per tenant or per environment, see [Local context](/[platform]/frontend/local-context/). For server-side code, use `runWithAmplifyServerContext` with the `/server` exports as described on this page. + + +