Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated May 1, 2024

Migrate from v5 to v6

This guide will help you migrate Amplify JavaScript v5's Storage APIs to the new v6's Storage APIs.

Removed Options

Many input and output properties have been removed in favor of alternatives with a better developer experience. If you have identified a use case that you feel isn't covered in this migration guide, please open a GitHub issue.

The input options below have been removed in v6 from Storage APIs:

  • provider: Custom providers are no longer supported in v6. All v5 examples below use the input/output values expected for the default S3 Provider.
  • customPrefix: You can add a prefix resolver to your Storage Config to customize the key prefix per accessLevel. See Using a Custom Prefix for migration details.
  • track: Auto-tracking Storage events in pinpoint has been removed in V6. You can track Storage events using the Analytics category. See Tracking storage events for migration details.
  • ServerSideEncryption, SSECustomerAlgorithm, SSECustomerKey, SSECustomerKeyMD5, and SSEKMSKeyId: These options have been deprecated because Server-Side Encryption is automatically applied using default keys by S3 since Jan 2023.
  • cacheControl: This option has been removed and now defers to the HTTP caching behavior configured by the runtime.
  • bucket: The bucket is required for Storage configuration object as part of Amplify.configure() and has therefore been removed from input options.

Note: We have remapped all capitalized input and output keys to "camelCase" in v6.

Deprecated Options

Starting with version 6.2.0, the following API parameters and output options have been marked as deprecated and may be removed in a future version.

  • key: A new path parameter has been introduced and will be used instead of the key parameter in input and output options. The path parameter allows for flexible path construction based on user's identity id.

  • accessLevel: File accessLevel with scoped public, protected, and private permissions is now deprecated. Instead, Amplify (Gen2) introduces a more refined access control mechanism. To offer similar flexibility to clients, we will utilize the path parameter for input options.

Storage.put

The Storage.put API has been renamed to uploadData in v6. There are a few changes in the experience from v5 to v6 in addition to those mentioned above:

  • We removed the ACL option: the S3 Bucket ACL provides extra access control options that Amplify does not currently support. ACL is no longer recommended according to S3 security best practices.
  • The Expires option has been deprecated.
  • The resumable option has been removed from config as all upload tasks are resumable by default in v6. You can find the pause, resume, and cancel functions on uploadData's return value. See the v6 Storage documentation for details.
  • The progressCallback option on upload and download operations has been replaced by onProgress in v6. The function input property names loaded and total now map to transferredBytes and totalBytes.
Input

V5

// AWSS3 (Default) Provider
key: string
object: {
ACL?: string; // removed in V6
CacheControl?: string; // removed in V6
ContentDisposition? string;
ContentEncoding?: string;
ContentMD5?: string;
ContentType?: string;
Expires?: Date; // removed in V6
Metadata?: Record<string, string>;
ServerSideEncryption?: string; // removed in V6
SSECustomerAlgorithm?: string; // removed in V6
SSECustomerKey?: string; // removed in V6
SSECustomerKeyMD5?: string; // removed in V6
SSEKMSKeyId?: string; // removed in V6
Bucket: string;
Key: string;
Body?: string | ReadableStream<any> | Blob | Uint8Array | Buffer;
Tagging?: string; // removed in V6
}
// non-resumable config
config?: {
level?: 'public' | 'protected' | 'private';
provider?: string; // removed in V6
customPrefix?: { // removed in V6
private?: string;
public?: string;
protected?: string;
};
track?: boolean; // removed in V6
progressCallback?: (progress: any) => any;
serverSideEncryption?: string; // removed in V6
SSECustomerAlgorithm?: string; // removed in V6
SSECustomerKey?: string; // removed in V6
SSECustomerKeyMD5?: string; // removed in V6
SSEKMSKeyId?: string; // removed in V6
acl?: string; // removed in V6
bucket?: string;
cacheControl?: string; // removed in V6
contentDisposition?: string;
contentEncoding?: string;
contentType?: string;
expires?: Date;
metadata?: Record<string, string>;
tagging?: string; // removed in V6
useAccelerateEndpoint?: boolean;
resumable?: boolean; // Not required in v6: all put operations are resumable
}
// resumable config
config?: {
// All of the standard config above plus
progressCallback?: (progress: {
loaded: number;
total: number;
}) => any; // More specific than progressCallback above
completeCallback?: (event: { key?: string }) => any;
errorCallback?: (err: any) => any;
}

V6

input: {
path: string | ({identityId}: {identityId: string}) => string;
data: string | Blob | ArrayBufferView | ArrayBuffer;
options?: {
useAccelerateEndpoint?: boolean;
onProgress?: (event: {
transferredBytes: number;
totalBytes?: number;
}) => void;
contentDisposition?: string;
contentEncoding?: string;
contentType?: string;
metadata?: Record<string, string>;
};
}
Output

V5

// Resumable
StoragePutOutput {
resume(): any;
pause(): any;
percent: number;
isInProgress: boolean;
}
// Non-resumable
StoragePutOutput {
key: string;
};

V6

UploadDataOutput {
cancel: (message?: string) => void;
pause: () => void;
resume: () => void;
readonly state:
| 'IN_PROGRESS'
| 'PAUSED'
| 'CANCELED'
| 'SUCCESS'
| 'ERROR';
result: Promise<Item>;
}
Item {
path: string;
lastModified?: Date;
size?: number;
eTag?: string;
metadata?: Record<string, string>;
versionId?: string;
contentType?: string;
}
import { uploadData } from 'aws-amplify/storage';
const handleUpload = async (path: string, data: string | Blob) => {
const operation = uploadData({
path,
data
});
const result = await operation.result;
}
import { Storage } from 'aws-amplify';
const handleUpload = async (key: string, data: string | Blob) => {
// Upload a file with access level `public`
const result = await Storage.put(key, data, {
level: 'public',
});
}

Monitor Upload Progress

The progressCallback option has been replaced by onProgress in v6. The function input property names loaded and total now map to transferredBytes and totalBytes.

import { uploadData } from 'aws-amplify/storage';
const handleUpload = async (path: string, data: string | Blob) => {
const operation = uploadData({
path,
data,
options: {
onProgress: ({ transferredBytes, totalBytes }) => {
// Progress implementation
}
}
});
const result = await operation.result;
}
import { Storage } from 'aws-amplify';
const handleUpload = async (key: string, data: string | Blob) => {
// Upload a file with access level `public`
const result = await Storage.put(key, data, {
level: 'public',
progressCallback: ({ loaded, total }) => {
// Progress implementation
}
});
}

Storage.get

The Storage.get API has been separated into two APIs in v6, getURL and downloadData. There are a few changes in the experience from v5 to v6 in addition to those mentioned above:

  • Content options (contentDisposition, contentLanguage, contentEncoding, and contentType) are no longer supported in the get API as these values are already provided in the uploaded file.
  • The following response keys inherited from AWS SDK in v5 have have been removed from the API output in v6:
    • DeleteMarker
    • AcceptRanges
    • Expiration
    • Restore
    • ChecksumXXX
    • MissingMeta
    • VersionId
    • ContentRange
    • Expires
    • WebsiteRedirectLocation
    • BucketKeyEnabled
    • StorageClass
    • RequestCharged
    • ReplicationStatus
    • PartsCount
    • TagCount
    • ObjectLockMode
    • ObjectLockRetainUntilDate
    • ObjectLockLegalHoldStatus
  • The progressCallback option on upload and download operations has been replaced by onProgress in v6. The function input property names loaded and total now map to transferredBytes and totalBytes.
Input

V5

// Assumes default S3 Provider
key: string
options: {
level?: 'public' | 'protected' | 'private';
provider?: 'AWSS3';
customPrefix?: { // removed in V6
private?: string;
public?: string;
protected?: string;
};
track?: boolean; // removed in V6
download?: boolean; // removed in V6
expires?: number;
identityId?: string;
progressCallback?: (progress: any) => any;
cacheControl?: string; // removed in V6
contentDisposition?: string; // removed in V6
contentEncoding?: string; // removed in V6
contentLanguage?: string; // removed in V6
contentType?: string; // removed in V6
SSECustomerAlgorithm?: string; // removed in V6
SSECustomerKey?: string; // removed in V6
SSECustomerKeyMD5?: string; // removed in V6
validateObjectExistence?: boolean;
};

V6

// getUrl
{
path: string | ({identityId}: {identityId: string}) => string;
options?: {
useAccelerateEndpoint?: boolean;
};
}

// downloadData
input: {
path: string | ({identityId}: {identityId: string}) => string;
options?: {
useAccelerateEndpoint?: boolean;
onProgress?: (event: {
transferredBytes: number;
totalBytes?: number;
}) => void;
bytesRange?: {
start: number;
end: number;
};
};
}
Output

V5

// download: false
string // presigned url
// download: true
S3ProviderGetOutput {
Body?: Blob;
DeleteMarker?: boolean; // removed in V6
AcceptRanges?: string; // removed in V6
Expiration?: string; // removed in V6
Restore?: string; // removed in V6
LastModified?: Date;
ContentLength?: number;
ETag?: string;
ChecksumCRC32?: string; // removed in V6
ChecksumCRC32C?: string; // removed in V6
ChecksumSHA1?: string; // removed in V6
ChecksumSHA256?: string; // removed in V6
MissingMeta?: number; // removed in V6
VersionId?: string; // removed in V6
CacheControl?: string; // removed in V6
ContentDisposition?: string; // removed in V6
ContentEncoding?: string; // removed in V6
ContentLanguage?: string; // removed in V6
ContentRange?: string; // removed in V6
ContentType?: string;
Expires?: Date; // removed in V6
WebsiteRedirectLocation?: string; // removed in V6
ServerSideEncryption?: ServerSideEncryption | string; // removed in V6
Metadata?: Record<string, string>;
SSECustomerAlgorithm?: string; // removed in V6
SSECustomerKeyMD5?: string; // removed in V6
SSEKMSKeyId?: string; // removed in V6
BucketKeyEnabled?: boolean; // removed in V6
StorageClass?: StorageClass | string; // removed in V6
RequestCharged?: RequestCharged | string; // removed in V6
ReplicationStatus?: ReplicationStatus | string; // removed in V6
PartsCount?: number; // removed in V6
TagCount?: number; // removed in V6
ObjectLockMode?: ObjectLockMode | string; // removed in V6
ObjectLockRetainUntilDate?: Date; // removed in V6
ObjectLockLegalHoldStatus?: ObjectLockLegalHoldStatus | string; // removed in V6
$metadata: { // removed in V6
httpStatusCode?: number;
requestId?: string
}
}

V6

// getUrl
GetUrlOutput {
url: URL;
expiresAt: Date;
}

// downloadData
DownloadDataOutput {
cancel: (message?: string) => void;
readonly state: TransferTaskState;
result: Promise<StorageDownloadDataOutput<Item>>;
}
StorageDownloadDataOutput<Item> {
path: string;
lastModified?: Date;
size?: number;
eTag?: string;
metadata?: Record<string, string>;
versionId?: string;
contentType?: string;
body: {
blob: () => Promise<Blob>;
json: () => Promise<any>;
text: () => Promise<string>;
};
}

Get URL Only (Default)

import { getUrl } from 'aws-amplify/storage';
const handleGetUrl = async (path: string) => {
const url = await getUrl({
path,
options: {
validateObjectExistence: true
},
});
}
import { Storage } from 'aws-amplify';
const handleGetUrl = async (key: string) => {
const url = await Storage.get(key, {
validateObjectExistence: true
});
}

Download File Content to Memory

import { downloadData } from 'aws-amplify/storage';
const handleDownload = async (path: string) => {
const { body, eTag } = await downloadData({ path }).result;
}
import { Storage } from 'aws-amplify';
const handleDownload = async (key: string) => {
const { Body, ETag } = await Storage.get(key, {
download: true
});
}

Monitor Download Progress

The progressCallback option has been replaced by onProgress in v6. The function input property names loaded and total now map to transferredBytes and totalBytes.

import { downloadData } from 'aws-amplify/storage';
const handleDownload = async (path: string) => {
const { body, eTag } = await downloadData({
path,
options: {
onProgress: ({ transferredBytes, totalBytes }) => {
// Progress implementation
}
}
}).result;
}
import { Storage } from 'aws-amplify';
const handleDownload = async (key: string) => {
const { Body, ETag } = await Storage.get(key, {
download: true,
progressCallback: ({ loaded, total }) => {
// Progress implementation
}
});
}

Storage.copy

In addition to the changes mentioned above, the copy API in v6 no longer supports content options (contentDisposition, contentLanguage, and contentType) as these values are already provided by the existing file.

Input

V5

// Assumes default S3 Provider
src: {
key: string;
level?: string;
identityId?: string;
}
dest: {
level?: string;
key: string;
}
config?: {
provider?: 'AWSS3';
customPrefix?: { // removed in V6
public?: string;
protected?: string;
private?: string;
};
track?: boolean; // removed in V6
bucket?: string;
cacheControl?: string; // removed in V6
contentDisposition?: string; // removed in V6
contentLanguage?: string; // removed in V6
contentType?: string; // removed in V6
expires?: Date;
tagging?: string; // removed in V6
acl?:
| 'private'
| 'authenticated-read'
| 'aws-exec-read'
| 'bucket-owner-full-control'
| 'bucket-owner-read'
| 'public-read'
| 'public-read-write'
| string;
metadata?: Record<string, string>;
serverSideEncryption?: 'AES256' | 'aws:kms';
SSECustomerAlgorithm?: string; // removed in V6
SSECustomerKey?: string; // removed in V6
SSECustomerKeyMD5?: string; // removed in V6
SSEKMSKeyId?: string; // removed in V6
}

V6

input: {
source: {
path: string | ({identityId}: {identityId: string}) => string;
};
destination: {
path: string | ({identityId}: {identityId: string}) => string;
};
}
Output

V5

S3ProviderCopyOutput {
key: string;
}

V6

CopyOutput {
path: string;
}
import { copy } from 'aws-amplify/storage';
const handleCopy = async (sourcePath: string, destinationPath: string) => {
const { path } = await copy({
source: {
path: sourcePath
},
destination: {
path: destinationPath
},
});
};
import { Storage } from 'aws-amplify';
const handleCopy = async (sourceKey: string, destinationKey: string) => {
const { key } = await Storage.copy(
src: {
key: sourceKey
},
dest: {
key: destinationKey
}
)
};

Storage.remove

In addition to the changes mentioned above, the output of the remove API in v6 now only returns the path of the removed file. DeleteMarker, VersionId, RequestCharged, and $metadata have been removed from the output.

Input

V5

// Assumes default S3 Provider
key: string
config?: {
level?: 'public' | 'protected' | 'private';
provider?: 'AWSS3';
customPrefix?: { // removed in V6
public?: string;
protected?: string;
private?: string;
};
track?: boolean; // removed in V6
bucket?: string;
}

V6

input: {
path: string | ({identityId}: {identityId: string}) => string;
options?: {
useAccelerateEndpoint?: boolean;
};
}
Output

V5

DeleteMarker?: boolean; // removed in V6
VersionId?: string; // removed in V6
RequestCharged?: 'requester' | string; // removed in V6
$metadata: { // removed in V6
httpStatusCode?: number;
requestId?: string;
extendedRequestId?: string;
cfId?: string;
attempts?: number;
totalRetryDelay?: number;
};

V6

RemoveOutput {
path: string;
}
import { remove } from 'aws-amplify/storage';
const handleRemove = async (path: string) => {
await remove({
path,
});
}
import { Storage } from 'aws-amplify';
const handleRemove = async (key: string, accessLevel: string) => {
await Storage.remove(key, {
// accessLevel is required if other than 'public'
level: accessLevel
});
}

Storage.list

There are a few changes in the list API from v5 to v6 in addition to those mentioned above:

  • The hasNextToken output property has been removed. You can check that nextToken is defined to check if there are more items to retrieve.
Input

V5

// Assumes default S3 Provider
path: string,
config?: {
level?: 'public' | 'protected' | 'private';
provider?: 'AWSS3';
customPrefix?: { // removed in V6
private?: string;
public?: string;
protected?: string;
};
track?: boolean; // removed in V6
bucket?: string;
pageSize?: number | 'ALL';
identityId?: string;
nextToken?: string;
}

V6

// Paginated List
input {
path: string | ({identityId}: {identityId: string}) => string;
options?: {
listAll?: false;
pageSize?: number;
nextToken?: string;
useAccelerateEndpoint?: boolean;
};
}
// List All
input {
path: string | ({identityId}: {identityId: string}) => string;
options?: {
listAll: true;
useAccelerateEndpoint?: boolean;
};
}
Output

V5

StorageListOutput {
results: S3ProviderListOutputItem[];
nextToken?: string;
hasNextToken: boolean; // removed in V6
};
S3ProviderListOutputItem {
key: string;
eTag: string;
lastModified: Date;
size: number;
}

V6

// Paginated List
ListPaginateOutput {
items: ListOutputItem[];
nextToken?: string;
}
// List All
ListAllOutput {
items: ListOutputItem[];
}
ListOutputItem {
path: string;
lastModified?: Date;
size?: number;
eTag?: string;
}

Paginated List (Default)

import { list } from 'aws-amplify/storage';
const PAGE_SIZE = 20;
let nextToken = undefined;
let hasNextPage = true;
const loadNextPage = async (path: string) => {
if (hasNextPage) {
let response = await list({
path,
options: {
pageSize: PAGE_SIZE,
nextToken: nextToken
}
});
if (response.nextToken) {
nextToken = response.nextToken;
} else {
nextToken = undefined;
hasNextPage = false;
}
// render list items from response.results
}
};
import { Storage } from 'aws-amplify';
const PAGE_SIZE = 20;
let nextToken = undefined;
let hasNextPage = true;
const loadNextPage = async () => {
if (hasNextPage) {
let response = await Storage.list('', {
pageSize: PAGE_SIZE,
nextToken: nextToken
});
if (response.hasNextToken) {
nextToken = response.nextToken;
} else {
nextToken = undefined;
hasNextPage = false;
}
// render list items from response.results
}
};

All Files

import { list } from 'aws-amplify/storage';
const handleListAll = async (
path: string
) => {
const { items } = list({
path,
options: {
listAll: true,
}
});
}
import { Storage } from 'aws-amplify';
const handleListAll = async (
prefix: string,
accessLevel: string,
id: string
) => {
const { results } = Storage.list(prefix, {
pageSize: 'ALL',
// level and identityId not required if level is 'public'
level: accessLevel,
identityId: id
});
}

Storage.getProperties

In addition to the changes mentioned above, the getProperties API in v6 also adds path to the result.

Input

V5

key: string
config?: {
level?: 'public' | 'protected' | 'private';
provider?: string; // removed in V6
customPrefix?: { // removed in V6
private?: string;
public?: string;
protected?: string;
};
track?: boolean; // removed in V6
SSECustomerAlgorithm?: string; // removed in V6
SSECustomerKey?: string; // removed in V6
SSECustomerKeyMD5?: string; // removed in V6
}

V6

input: {
path: string | ({identityId}: {identityId: string}) => string;
options?: {
useAccelerateEndpoint?: boolean;
};
}
Output

V5

StorageGetPropertiesOutput {
contentType: string;
contentLength: number;
eTag: string;
lastModified: Date;
metadata: Record<string, string>;
};

V6

GetPropertiesOutput {
path: string;
lastModified?: Date;
size?: number';
eTag?: string';
metadata?: Record<string, string>';
versionId?: string;
contentType?: string;
}
import { getProperties } from 'aws-amplify/storage';
const handleGetProperties = async (
path: string,
) => {
const result = await getProperties({
path
});
}
import { Storage } from 'aws-amplify';
const handleGetProperties = async (
key: string,
accessLevel: string
) => {
const result = await Storage.getProperties(key, {
// level not required if level is 'public'
level: accessLevel,
});
}

Storage.cancel

The process for cancelling a request has changed in v6. In v5, you have to maintain the promise or UploadTask reference returned from a Storage API call, and supply it as input to the Storage.cancel API. In v6, cancel is a function returned with the result of a Storage operation. To cancel an operation, you will call operation.cancel().

import { downloadData } from 'aws-amplify/storage';
const operation = downloadData({
path: "photos/1.jpg"
});
operation.response.then(result => {
// GET operation completed successfully
}).catch(error => {
// If the error is because the request was cancelled you can confirm here.
if(isCancelError(error)) {
// 'my message for cancellation'
console.log(error.message);
}
})
// To cancel the above request
operation.cancel('my message for cancellation');
import { Storage } from 'aws-amplify';
const operation = Storage.get(key, options);
operation.then(result => {
// GET operation completed successfully
}).catch(error => {
// If the error is because the request was cancelled you can confirm here.
if(Storage.isCancel(error)) {
// 'my message for cancellation'
console.log(error.message);
}
});
// To cancel the above request
Storage.cancel(operation, 'my message for cancellation');

Using a Custom Prefix (Deprecated)

If you would like to upload or download items with a customPrefix, you can configure a prefix resolver in the second parameter of Amplify.configure, libraryOptions. Keep in mind that you will need to resolve each accessLevel in the custom prefixResolver. The example below shows the default for protected and private access levels and a customPrefix for guest.

import { uploadData } from 'aws-amplify/storage';
import { Amplify } from 'aws-amplify';
import amplifyconfig from './amplifyconfiguration.json';
const libraryOptions = {
Storage: {
S3: {
prefixResolver: async ({ accessLevel, targetIdentityId }) => {
if (accessLevel === 'guest') {
return 'publicPrefix/';
} else if (accessLevel === 'protected') {
return `protected/${targetIdentityId}/`;
} else {
return `private/${targetIdentityId}/`;
}
}
}
}
};
Amplify.configure(amplifyConfig, libraryOptions);
const handleUpload = async (key: string, data: string | Blob) => {
// Upload a file with access level `guest` as the equivalent of `public` in v5
const operation = uploadData({
key,
data,
options: {
accessLevel: 'guest'
}
});
const result = await operation.result;
}
import { Storage } from 'aws-amplify';
const handleUpload = async (key: string, data: string | Blob) => {
// Upload a file with access level `public`
const result = await Storage.put(key, data, {
level: 'public',
customPrefix: {
public: 'publicPrefix'
}
});
}

Tracking storage events

The track option has been removed from Storage and its APIs in v6. Previously, in v5, enabling this option would automatically send analytics events to Pinpoint following the success or failure of Storage API calls. The analytic events sent were defined by Amplify and therefore not configurable. In v6, this option has been removed but you can continue to track Storage API results in a more predictable and configurable way using the Analytics category.

The table below lists the analytics events previously associated to v5 APIs and the corresponding v6 APIs they should now be associated with instead.

Analytics eventV5 APIV6 API
'copy'copycopy
'download'get (download: true)downloadData
'getSignedUrl'getgetUrl
'getProperties'getPropertiesgetProperties
'upload'putuploadData
'delete'removeremove
'list'listlist

The example below records an analytics event similar to what you would have seen in v5, but does not prescribe a format or content that needs to be followed. Instead, it is fully configurable and should be structured to best suit your analytics needs. Learn more about using the Analytics category.

import { list } from 'aws-amplify/storage';
import { record } from 'aws-amplify/analytics';
const path = 'public/photos/';
try {
const result = await list({ path });
record({
name: 'list',
attributes: { result: 'success' },
});
// Do something with the result
} catch (error) {
record({
name: 'list',
attributes: { result: 'failure' },
});
// Do something with the error
};
import { Storage } from 'aws-amplify';
const prefix = 'photos/';
Storage.configure({ track: true });
try {
const result = Storage.list(prefix);
// Do something with the result
} catch (error) {
// Do something with the error
}