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:

1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Effect": "Allow",
6 "Action": ["s3:ListBucket"],
7 "Resource": ["arn:aws:s3:::myBucket"]
8 },
9 {
10 "Effect": "Allow",
11 "Action": [
12 "s3:PutObject",
13 "s3:GetObject"
14 ],
15 "Resource": ["arn:aws:s3:::myBucket/*"]
16 }
17 ]
18}

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.

1input S3ObjectInput {
2 bucket: String!
3 key: String!
4 region: String!
5 localUri: String
6 mimeType: String
7 }

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.

1type Mutation {
2 ...other mutations here...
3 putPostWithPhoto(
4 id: ID!,
5 author: String!,
6 title: String,
7 content: String,
8 url: String,
9 ups: Int,
10 downs: Int,
11 photo: S3ObjectInput
12 version: Int!
13 ): Post
14 }
15 type S3Object {
16 bucket: String!
17 key: String!
18 region: String!
19 }
20 input S3ObjectInput {
21 bucket: String!
22 key: String!
23 region: String!
24 localUri: String
25 mimeType: String
26 }
27 type Post {
28 id: ID!
29 author: String!
30 title: String
31 content: String
32 url: String
33 ups: Int
34 downs: Int
35 photo: S3Object
36 version: Int!
37 }

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.

1$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:

1public class ClientFactory {
2 // ...other code...
3 private static volatile AWSAppSyncClient client;
4 private static volatile S3ObjectManagerImplementation s3ObjectManager;
5 public static AWSAppSyncClient getInstance(Context context) {
6 if (client == null) {
7 client = AWSAppSyncClient.builder()
8 .context(context)
9 .awsConfiguration(new AWSConfiguration(context))
10 .s3ObjectManager(getS3ObjectManager(context)) // Here you initialize the s3 object manager.
11 .build();
12 }
13 return client;
14 }
15 // Copy the below two methods and add the .s3ObjectManager builder parameter
16 // initialize and fetch the S3 Client
17 public static final S3ObjectManagerImplementation getS3ObjectManager(final Context context) {
18 if (s3ObjectManager == null) {
19 AmazonS3Client s3Client = new AmazonS3Client(getCredentialsProvider(context));
20 s3Client.setRegion(Region.getRegion("us-east-1")); // you can set the region of bucket here
21 s3ObjectManager = new S3ObjectManagerImplementation(s3Client);
22 }
23 return s3ObjectManager;
24 }
25 // initialize and fetch cognito credentials provider for S3 Object Manager
26 public static final AWSCredentialsProvider getCredentialsProvider(final Context context){
27 final CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
28 context,
29 Constants.COGNITO_IDENTITY, // Identity pool ID
30 Regions.fromName(Constants.COGNITO_REGION) // Region
31 );
32 return credentialsProvider;
33 }
34}

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:

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

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

1public class AddPostActivity extends AppCompatActivity {
2 // ...other code...
3 // Photo selector application code.
4 private static int RESULT_LOAD_IMAGE = 1;
5 private String photoPath;
6 public void choosePhoto() {
7 Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
8 startActivityForResult(i, RESULT_LOAD_IMAGE);
9 }
10 @Override
11 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
12 super.onActivityResult(requestCode, resultCode, data);
13 if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
14 Uri selectedImage = data.getData();
15 String[] filePathColumn = {MediaStore.Images.Media.DATA};
16 Cursor cursor = getContentResolver().query(selectedImage,
17 filePathColumn, null, null, null);
18 cursor.moveToFirst();
19 int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
20 String picturePath = cursor.getString(columnIndex);
21 cursor.close();
22 // String picturePath contains the path of selected Image
23 photoPath = picturePath;
24 }
25 }
26 // Actual mutation code
27 private void save() {
28 final String title = ((EditText) findViewById(R.id.updateTitle)).getText().toString();
29 final String author = ((EditText) findViewById(R.id.updateAuthor)).getText().toString();
30 final String url = ((EditText) findViewById(R.id.updateUrl)).getText().toString();
31 final String content = ((EditText) findViewById(R.id.updateContent)).getText().toString();
32 S3ObjectInput s3ObjectInput = S3ObjectInput.builder()
33 .bucket("YOUR_BUCKET_NAME")
34 .key("public/"+ UUID.randomUUID().toString())
35 .region("us-east-1")
36 .localUri(photoPath)
37 .mimeType("image/jpg").build();
38 PutPostWithPhotoMutation addPostMutation = PutPostWithPhotoMutation.builder()
39 .title(title)
40 .author(author)
41 .url(url)
42 .content(content)
43 .ups(0)
44 .downs(0)
45 .photo(s3ObjectInput)
46 .expectedVersion(1)
47 .build();
48 ClientFactory.getInstance(this).mutate(addPostMutation).enqueue(postsCallback);
49 }
50 // Mutation callback code
51 private GraphQLCall.Callback<PutPostWithPhotoMutation.Data> postsCallback = new GraphQLCall.Callback<PutPostWithPhotoMutation.Data>() {
52 @Override
53 public void onResponse(@Nonnull final Response<PutPostWithPhotoMutation.Data> response) {
54 runOnUiThread(new Runnable() {
55 @Override
56 public void run() {
57 Toast.makeText(AddPostActivity.this, "Added post", Toast.LENGTH_SHORT).show();
58 AddPostActivity.this.finish();
59 }
60 });
61 }
62 @Override
63 public void onFailure(@Nonnull final ApolloException e) {
64 runOnUiThread(new Runnable() {
65 @Override
66 public void run() {
67 Log.e("", "Failed to perform AddPostMutation", e);
68 Toast.makeText(AddPostActivity.this, "Failed to add post", Toast.LENGTH_SHORT).show();
69 AddPostActivity.this.finish();
70 }
71 });
72 }
73 };
74}