Front-End Web & Mobile

Amplify Storage: Now with fullstack TypeScript, powered by Amazon S3

We are delighted to share with you a brand new experience for file storage using AWS Amplify! This powerful storage solution seamlessly integrates with Amazon Simple Storage Service (Amazon S3) and offers developers greater control and flexibility over their file structure via Amplify’s fullstack TypeScript developer experience. Whether you’re an experienced developer or new to the field, Amplify Storage offers an intuitive approach to managing cloud-based file storage, ensuring a seamless integration with your application’s requirements.

Today, we are announcing a fullstack TypeScript experience for AWS Amplify Storage. With Amplify Storage, you can now:

  1. Define a storage bucket in less than 5 lines of code
  2. Configure path-based access permissions
  3. Upload and download files from the storage backend using Amplify’s zero-config UI components and client libraries
  4. Manage files and folders using the newly redesigned file browser in the Amplify console

Earlier this week, Amplify’s new Gen 2 developer experience became generally available. To learn more about this launch beyond storage, you can read the announcement blog post.

New and simplified storage interface built on top of Amazon S3

Let’s explore Amplify Storage features more in depth with code samples to demonstrate its capabilities. You can refer to the social room demo app here for a working sample of the storage features showcased in this post.

Deploy a storage bucket in less than 5 lines of code

One of the standout features of Amplify is its TypeScript-based backend building experience, which empowers you to configure and manage your storage resources with just a few lines of code. You can also instantly test your storage functionality using your sandbox environment that enables every developer on the team to iterate independently.

You can add storage to your backend configuration by creating a new folder amplify/storage and a resource.ts file under amplify/storage/ .

Adding storage folder and backend file

We define the storage backend using the defineStorage function and specifying a friendly name for the storage resource. It’s important to note that while this name serves as an alias to easily identify your storage resource, the actual S3 bucket created will have a unique identifier appended to it, ensuring a distinct and secure storage location for your application.

The following code goes into your newly-created amplify/storage/resource.ts file. Here, we are defining the storage backend object and naming our storage resource “gen2-multi-cursor-demo-app”.

import { defineStorage } from "@aws-amplify/backend";

export const storage = defineStorage({
  name: 'gen2-multi-cursor-demo-app'
  })
});

We then include this storage object in our backend definition in amplify/backend.ts file. Your backend.ts file should now look like this

import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';
import { storage } from './storage/resource';

const backend = defineBackend({
  auth,
  data,
  storage
});

With the storage resource defined, your sandbox environment immediately recognizes the changes and initiates the deployment of a local version of your storage resource. This seamless integration allows you to start local development and begin leveraging the powerful storage capabilities right away.

Customize path-based access permissions for storage backend

Amplify Storage offers granular control over access management for your storage resource. You can define fine-grained access permissions for individual file paths, catering to various user roles such as guest users, authenticated users, owners, user groups, and even functions to align with your specific requirements.

In the demo app, we want to enable signed-in users of your app to upload images in each room. Additionally, we want to allow non-signed-in (guest) users to view the uploaded images. To achieve this functionality, you’ll need to define specific access controls for your storage resource.

The desired access permissions are as follows:

  1. Guest users should have read-only access, allowing them to view the uploaded images.
  2. Authenticated (signed-in) users should have full access, enabling them to upload new images, view existing ones, and even clear all images if needed.

For the above rules, the access definition will look like the following

import { defineStorage } from "@aws-amplify/backend";

export const storage = defineStorage({
  name: 'gen2-multi-cursor-demo-app',
  access: allow => ({
    'room/*': [
      allow.guest.to(['get']),
      allow.authenticated.to(['get', 'write', 'delete'])
    ]
  })
});

It’s important to note that we have implemented a deny-by-default policy for our storage resource. This ensures that users will not have access to any file path or directory within the storage resource unless explicitly granted permission through the amplify/storage/resource.ts file. By following this principle, we maintain tight control over access privileges and minimize potential vulnerabilities.

Additionally, you can also provide full access to user groups. In this example, if you want to give admin users full access to the storage resource, you need to first ensure that you have created the necessary user group in your auth backend i.e. amplify/auth/resource.ts. Let’s call this resource “admins”. Your amplify/auth/resource.ts file will look like this

import { defineAuth } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
    email: true
  },
  groups: ['admin']
});

You can now specify access for this group separately in amplify/storage/resource.ts file

export const storage = defineStorage({
  name: 'gen2-multi-cursor-demo-app',
  access: (allow) => ({
    'room/*': [
      allow.guest.to(['get']),
      allow.authenticated.to(['get', 'write', 'delete']),
      allow.groups(['admin']).to(['read', 'write', 'delete'])
    ]
  })
});

To dive deeper into customizing your storage backend definition with authorization capabilities, refer to our comprehensive documentation here.

Uploading and downloading files with Amplify’s UI and client libraries

Use Amplify’s UI components – Storage Image and Storage Manager to implement image display and file upload functionality in minutes or Amplify library to to further customize file management features to meet your app’s needs. The new path parameter in our client APIs will enable you to access custom-defined structure and access permissions.

Before we build on top of the demo app, make sure you have npx ampx sandbox running in a terminal window. This cloud sandbox allows you to test your app against the configured storage backend.

With the storage backend set up, it’s time to update our app to interact with this new functionality. We will be using the demo app from our launch blog post, you can also clone it from the samples repo (branch: day 1).

Base state of app

We are going to use the uploadData API from Amplify Storage to upload an image to the configured storage backend. We’ll also use the getUrl API to get presigned URLs (temporary URLs that provide secure access to private files) for the uploaded images, so we can display them.

In the following code sample you can see the usage of getUrl and uploadData APIs. Please refer to the PictureManager.tsx for the rest of the code sample that includes updating the data models.

import { getUrl, uploadData } from "aws-amplify/storage";
.
.
  // getUrl API
  const imageUrls = (
    await Promise.all(
      pictures.map(async (item) => await getUrl({ path: item.path }))
    )
  ).map((item) => item.url.toString());
.
.
  // uploadData API
  const result = await uploadData({
    path: `room/${roomId}/${file.name}`,
    data: file,
  }).result;
.
.

Next, let’s implement a way for users to clear the uploaded images so they can start fresh if they wish. For this, we’ll be using the remove API from Amplify Storage.

await Promise.all(pictures.map((item) => remove({ path: item.path })));

Note: For the complete working sample of this demo application, please refer to the Amplify Social Room sample app (branch: day 2).

You now have an app that looks like the following. You can upload and clear images in different rooms.

Demo app with files uploaded

Now when you commit your changes do a git push to your working branch. All your backend changes will be automatically deployed as part of Amplify’s CI/CD workflow. You can now access the URL after the build is ready to share your updates with your team!

Redesigned Storage File browser in Amplify console

To compliment these new Storage capabilities, there is now a dedicated tab in the Amplify console to manage the content in your storage resource. As you test your deployed app, this interface allows you to visualize updates to your storage resource. You can seamlessly add, move, and remove files and folders, enabling you to organize and maintain your storage resource on-demand, directly from the console’s user-friendly environment.

Amplify Console's Storage tab

Conclusion

Congratulations! You have now implemented and deployed a fully functioning app using Amplify Gen 2’s latest backend building and storage capabilities! Keep an eye out for more updates in the coming days, as we continue to unveil additional Amplify features!

Amplify is an open source project and and we are always looking for feedback from the community on how we can empower you to build the most performant and scalable apps. We would love to hear from you on any of our channels – you can engage in discussion on our Discord server or open feature requests or bugs in our respective GitHub projects. To learn more about Amplify and to start building with it, visit our documentation guides!