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

Using GraphQL API

You are currently viewing the AWS SDK for Mobile documentation which is a collection of low-level libraries. Use the Amplify libraries for all new app development. Learn more

Note: Please review the documentation for API before you proceed with the rest of this section.

You can also upload and download Amazon S3 Objects using AWS AppSync, a GraphQL based solution to build data-driven apps with real-time and offline capabilities. Sometimes you might want to create logical objects that have more complex data, such as images or videos, as part of their structure. For example, you might create a Person type with a profile picture or a Post type that has an associated image. You can use AWS AppSync to model these as GraphQL types. If any of your mutations have a variable with bucket, key, region, mimeType, and localUri fields, the SDK uploads the file to Amazon S3 for you.

Attach the following policy to your IAM role to grant it programmatic read-write access to your bucket:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::myBucket"]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": ["arn:aws:s3:::myBucket/*"]
}
]
}

Schema Setup

If any mutations have an input type S3ObjectInput with fields bucket, key, region, mimeType and localUri fields, the SDK will upload the file to S3.

input S3ObjectInput {
bucket: String!
key: String!
region: String!
localUri: String
mimeType: String
}

For example, to add a photo field in the Post type. Update Post type, add the new S3ObjectInput type and add a new mutation, putPostWithPhoto.

type Mutation {
...other mutations here...
putPostWithPhoto(
id: ID!,
author: String!,
title: String,
content: String,
url: String,
ups: Int,
downs: Int,
photo: S3ObjectInput
version: Int!
): Post
}
type S3Object {
bucket: String!
key: String!
region: String!
}
input S3ObjectInput {
bucket: String!
key: String!
region: String!
localUri: String
mimeType: String
}
type Post {
id: ID!
author: String!
title: String
content: String
url: String
ups: Int
downs: Int
photo: S3Object
version: Int!
}

Next, update the putPostWithPhoto mutation resolver to use PutItemWithS3Object template for request mapping and Return single item for response mapping from the AppSync console. Next, update the response mapping template for the photo field.

$util.toJson($util.dynamodb.fromS3ObjectJson($context.source.file))

Client Code

To use complex objects, you need AWS Identity and Access Management credentials for reading and writing to Amazon S3. These can be separate from the other authentication credentials used in the AWS AppSync client. Credentials for complex objects are set in the S3ObjectManagerImplementation builder parameter, which you can use like the following:

public class ClientFactory {
// ...other code...
private static volatile AWSAppSyncClient client;
private static volatile S3ObjectManagerImplementation s3ObjectManager;
public static AWSAppSyncClient getInstance(Context context) {
if (client == null) {
client = AWSAppSyncClient.builder()
.context(context)
.awsConfiguration(new AWSConfiguration(context))
.s3ObjectManager(getS3ObjectManager(context)) // Here you initialize the s3 object manager.
.build();
}
return client;
}
// Copy the below two methods and add the .s3ObjectManager builder parameter
// initialize and fetch the S3 Client
public static final S3ObjectManagerImplementation getS3ObjectManager(final Context context) {
if (s3ObjectManager == null) {
AmazonS3Client s3Client = new AmazonS3Client(getCredentialsProvider(context));
s3Client.setRegion(Region.getRegion("us-east-1")); // you can set the region of bucket here
s3ObjectManager = new S3ObjectManagerImplementation(s3Client);
}
return s3ObjectManager;
}
// initialize and fetch cognito credentials provider for S3 Object Manager
public static final AWSCredentialsProvider getCredentialsProvider(final Context context){
final CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
context,
Constants.COGNITO_IDENTITY, // Identity pool ID
Regions.fromName(Constants.COGNITO_REGION) // Region
);
return credentialsProvider;
}
}

The SDK uploads the file found at the localUri when the bucket, key, region, localUri, and mimeType are all provided. Now, the SDK uploads any field which has S3ObjectInput type in the mutation. The only requirement from a developer is to provide the correct bucket, key, region, localUri, and mimeType. Example: Add the following permissions to AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Then, in your activity where you are adding a post, update the code as follows:

public class AddPostActivity extends AppCompatActivity {
// ...other code...
// Photo selector application code.
private static int RESULT_LOAD_IMAGE = 1;
private String photoPath;
public void choosePhoto() {
Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, RESULT_LOAD_IMAGE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
cursor.close();
// String picturePath contains the path of selected Image
photoPath = picturePath;
}
}
// Actual mutation code
private void save() {
final String title = ((EditText) findViewById(R.id.updateTitle)).getText().toString();
final String author = ((EditText) findViewById(R.id.updateAuthor)).getText().toString();
final String url = ((EditText) findViewById(R.id.updateUrl)).getText().toString();
final String content = ((EditText) findViewById(R.id.updateContent)).getText().toString();
S3ObjectInput s3ObjectInput = S3ObjectInput.builder()
.bucket("YOUR_BUCKET_NAME")
.key("public/"+ UUID.randomUUID().toString())
.region("us-east-1")
.localUri(photoPath)
.mimeType("image/jpg").build();
PutPostWithPhotoMutation addPostMutation = PutPostWithPhotoMutation.builder()
.title(title)
.author(author)
.url(url)
.content(content)
.ups(0)
.downs(0)
.photo(s3ObjectInput)
.expectedVersion(1)
.build();
ClientFactory.getInstance(this).mutate(addPostMutation).enqueue(postsCallback);
}
// Mutation callback code
private GraphQLCall.Callback<PutPostWithPhotoMutation.Data> postsCallback = new GraphQLCall.Callback<PutPostWithPhotoMutation.Data>() {
@Override
public void onResponse(@Nonnull final Response<PutPostWithPhotoMutation.Data> response) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(AddPostActivity.this, "Added post", Toast.LENGTH_SHORT).show();
AddPostActivity.this.finish();
}
});
}
@Override
public void onFailure(@Nonnull final ApolloException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e("", "Failed to perform AddPostMutation", e);
Toast.makeText(AddPostActivity.this, "Failed to add post", Toast.LENGTH_SHORT).show();
AddPostActivity.this.finish();
}
});
}
};
}