Connect to existing AWS resources
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.
When to use this approach
- You already have AWS resources provisioned with CDK, Terraform, or CloudFormation
- You want to use Amplify client libraries without adopting the Amplify backend (
ampx) workflow - You need to connect to shared infrastructure managed by a platform team
- You want programmatic control over configuration for testing or environment switching
How it works
The default path is a generated amplify_outputs.json file that you hand to Amplify.configure():
import { Amplify } from 'aws-amplify';import outputs from './amplify_outputs.json';
Amplify.configure(outputs);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:
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);// orconst ctx = createAmplifyContext(config);| 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.
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();Extend a base builder. A builder is itself a valid seed, so one shared base can produce environment-specific configurations without being mutated:
// Owned by your platform team, never passed to configure() directlyconst 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:
const withUserPool = (userPoolId: string) => createConfigurationBuilder({ from: base }) .patch('Auth', { Cognito: { userPoolId } }) .build();
Amplify.configure(withUserPool('us-east-1_dev123'));Pass a ResourcesConfig object straight to Amplify.configure(). Nothing is merged for you, so every call states the complete configuration:
import { Amplify } from 'aws-amplify';
Amplify.configure({ Auth: { Cognito: { userPoolId: 'us-east-1_abc123', userPoolClientId: 'abcdef123456', identityPoolId: 'us-east-1:11111111-2222-3333-4444-555555555555', loginWith: { email: true } } }, Storage: { S3: { bucket: 'my-app-bucket', region: 'us-east-1' } }});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 for every supported field:
{ "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" }}import { Amplify, createAmplifyContext } from 'aws-amplify';import resources from './my_resources.json';
Amplify.configure(resources);// orconst 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 instead of touching global state.
Configure Auth (Amazon Cognito)
Connect to an existing Cognito User Pool and Identity Pool.
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);// orconst ctx = createAmplifyContext(config);import { Amplify } from 'aws-amplify';
Amplify.configure({ 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 } } }});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 for the full type.
Configure Data (AWS AppSync)
Connect to an existing AppSync GraphQL API.
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);// orconst ctx = createAmplifyContext(config);import { Amplify } from 'aws-amplify';
Amplify.configure({ API: { GraphQL: { endpoint: 'https://abc123.appsync-api.us-east-1.amazonaws.com/graphql', region: 'us-east-1', apiKey: 'da2-abcdefghijklmno', defaultAuthMode: 'apiKey' } }});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 for the full type.
Configure Storage (Amazon S3)
Connect to an existing S3 bucket.
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);// orconst ctx = createAmplifyContext(config);import { Amplify } from 'aws-amplify';
Amplify.configure({ 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' } }});Multiple buckets
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();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' } } } }});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 for the full type.
Configure Analytics (Amazon Pinpoint)
Connect to an existing Pinpoint application.
import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify';
const config = createConfigurationBuilder() .analytics({ Pinpoint: { appId: 'abc123def456', region: 'us-east-1' } }) .build();
Amplify.configure(config);// orconst ctx = createAmplifyContext(config);import { Amplify } from 'aws-amplify';
Amplify.configure({ Analytics: { Pinpoint: { appId: 'abc123def456', region: 'us-east-1' } }});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 for the full type.
Configure Geo (Amazon Location Service)
Connect to existing Location Service resources.
import { Amplify, createAmplifyContext, createConfigurationBuilder } from 'aws-amplify';
const config = createConfigurationBuilder() .geo({ LocationService: { region: 'us-east-1', maps: { items: { myMap: { style: 'VectorEsriStreets' } }, default: 'myMap' }, searchIndices: { items: ['myPlaceIndex'], default: 'myPlaceIndex' }, geofenceCollections: { items: ['myGeofenceCollection'], default: 'myGeofenceCollection' } } }) .build();
Amplify.configure(config);// orconst ctx = createAmplifyContext(config);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' } } }});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 for the full type.
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.
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);// orconst ctx = createAmplifyContext(config);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 for the full type.
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.
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);// orconst 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.
import { Amplify } from 'aws-amplify';
Amplify.configure({ 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' } }});Environment-specific configuration
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 applies directly:
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:
import devOutputs from './amplify_outputs.dev.json';import prodOutputs from './amplify_outputs.prod.json';
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:
import { createAmplifyContext } from 'aws-amplify';
const environments = { dev: createAmplifyContext(withUserPool('us-east-1_dev123')), prod: createAmplifyContext(withUserPool('us-east-1_prod789'))};See Local context for what a context isolates, how to use one, and why it is a client-side API.
amplify_outputs.json schema reference
For the full schema of all supported configuration fields, see the amplify_outputs.json reference.