Page updated Jan 16, 2024

Advanced workflows

Amplify Android v1 is now in Maintenance Mode until May 31st, 2024. This means that we will continue to include updates to ensure compatibility with backend services and security. No new features will be introduced in v1.

Please use the latest version (v2) of Amplify Library for Android to get started.

If you are currently using v1, follow these instructions to upgrade to v2.

Amplify libraries should be used for all new cloud connected applications. If you are currently using the AWS Mobile SDK for Android, you can access the documentation here.

This section describes different use cases for constructing your own custom GraphQL requests and how to approach it. You may want to construct your own GraphQL request if you want to

  • retrieve only a subset of the data to reduce data transfer
  • retrieve nested objects at a depth that you choose
  • combine multiple operations into a single request
  • send custom headers to your AppSync endpoint

A GraphQL request is automatically generated for you when using AWSAPIPlugin with the existing workflow. For example, if you have a Todo model, a mutation request to save the Todo will look like this:

1Todo todo = Todo.builder()
2 .name("My first todo")
3 .description("todo description")
4 .build();
5
6Amplify.API.mutate(ModelMutation.create(todo),
7 result -> Log.i("ApiDemo", "Mutation succeeded."),
8 failure -> Log.e("ApiDemo", "Mutation failed.", failure)
9);
1val todo = Todo.builder()
2 .name("My first todo")
3 .description("todo description")
4 .build()
5
6Amplify.API.mutate(ModelMutation.create(todo),
7 { Log.i("ApiDemo", "Mutation succeeded") },
8 { Log.e("ApiDemo", "Mutation failed", it) }
9)
1val todo = Todo.builder()
2 .name("My first todo")
3 .description("todo description")
4 .build()
5
6try {
7 val result = Amplify.API.mutate(ModelMutation.create(todo))
8 Log.i("ApiDemo", "Mutation succeeded")
9} catch (error: ApiException) {
10 Log.e("ApiDemo", "Mutation failed", error)
11}
1Todo todo = Todo.builder()
2 .name("My first todo")
3 .description("todo description")
4 .build();
5
6Amplify.API.mutate(ModelMutation.create(todo))
7 .subscribe(
8 result -> Log.i("ApiDemo", "Mutation succeeded"),
9 failure -> Log.e("ApiDemo", "Mutation failed", failure)
10 );

Underneath the covers, a request is generated with a GraphQL document and variables and sent to the AppSync service.

1{
2 "query": "mutation createTodo($input: CreateTodoInput!) {
3 createTodo(input: $input) {
4 id
5 name
6 description
7 }
8 }",
9 "variables": "{
10 "input": {
11 "id": "[UNIQUE-ID]",
12 "name": "my first todo",
13 "description": "todo description"
14 }
15 }
16}

The different parts of the document are described as follows

  • mutation - the operation type to be performed, other operation types are query and subscription
  • createTodo($input: CreateTodoInput!) - the name and input of the operation.
  • $input: CreateTodoInput! - the input of type CreateTodoInput! referencing the variables containing JSON input
  • createTodo(input: $input) - the mutation operation which takes a variable input from $input
  • the selection set containing id, name, and description are fields specified to be returned in the response

You can learn more about the structure of a request from GraphQL Query Language and AppSync documentation. To test out constructing your own requests, open the AppSync console using amplify console api and navigate to the Queries tab.

Subset of data

The selection set of the document specifies which fields are returned in the response. For example, if you are displaying a view of the Todo without the description, you can construct the document to omit the field. You can learn more about selection sets here.

1query getTodo($id: ID!) {
2 getTodo(id: $id) {
3 id
4 name
5 }
6}

The response data will look like this

1{
2 "data": {
3 "getTodo": {
4 "id": "111",
5 "name": "my first todo"
6 }
7 }
8}

First, create your own GraphQLRequest

1private GraphQLRequest<Todo> getTodoRequest(String id) {
2 String document = "query getTodo($id: ID!) { "
3 + "getTodo(id: $id) { "
4 + "id "
5 + "name "
6 + "}"
7 + "}";
8 return new SimpleGraphQLRequest<>(
9 document,
10 Collections.singletonMap("id", id),
11 Todo.class,
12 new GsonVariablesSerializer());
13}
1private fun getTodoRequest(id: String): GraphQLRequest<Todo> {
2 val document = ("query getTodo(\$id: ID!) { "
3 + "getTodo(id: \$id) { "
4 + "id "
5 + "name "
6 + "}"
7 + "}")
8 return SimpleGraphQLRequest(
9 document,
10 mapOf("id" to id),
11 Todo::class.java,
12 GsonVariablesSerializer())
13}

Then, query for the Todo by a todo id

1Amplify.API.query(getTodoRequest("[TODO_ID]"),
2 response -> Log.d("MyAmplifyApp", "response" + response),
3 error -> Log.e("MyAmplifyApp", "error" + error)
4);
1Amplify.API.query(getTodoRequest("[TODO_ID]"),
2 { Log.d("MyAmplifyApp", "Response = $it") },
3 { Log.e("MyAmplifyApp", "Error!", it) }
4)
1try {
2 val response = Amplify.API.query(getTodoRequest("[TODO_ID]"))
3 Log.d("MyAmplifyApp", "Query response = $response")
4} catch (error: ApiException) {
5 Log.e("MyAmplifyApp", "Query failed", error)
6}

Nested Data

If you have a relational model, you can retrieve the nested object by creating a GraphQLRequest with a selection set containing the nested object's fields. For example, in this schema, the Post can contain multiple comments and notes.

1enum PostStatus {
2 ACTIVE
3 INACTIVE
4}
5
6type Post @model {
7 id: ID!
8 title: String!
9 rating: Int!
10 status: PostStatus!
11 comments: [Comment] @hasMany(indexName: "byPost", fields: ["id"])
12 notes: [Note] @hasMany(indexName: "byNote", fields: ["id"])
13}
14
15type Comment @model {
16 id: ID!
17 postID: ID! @index(name: "byPost", sortKeyFields: ["content"])
18 post: Post! @belongsTo(fields: ["postID"])
19 content: String!
20}
21
22type Note @model {
23 id: ID!
24 postID: ID! @index(name: "byNote", sortKeyFields: ["content"])
25 post: Post! @belongsTo(fields: ["postID"])
26 content: String!
27}

If you only want to retrieve the comments, without the notes, create a GraphQLRequest for the Post with nested fields only containing the comment fields.

1private GraphQLRequest<Post> getPostWithCommentsRequest(String id) {
2 String document = "query getPost($id: ID!) { "
3 + "getPost(id: $id) { "
4 + "id "
5 + "title "
6 + "rating "
7 + "status "
8 + "comments { "
9 + "items { "
10 + "id "
11 + "postID "
12 + "content "
13 + "} "
14 + "} "
15 + "} "
16 + "}";
17 return new SimpleGraphQLRequest<>(
18 document,
19 Collections.singletonMap("id", id),
20 Post.class,
21 new GsonVariablesSerializer());
22}
1private fun getPostWithCommentsRequest(id: String): GraphQLRequest<Post> {
2 val document = ("query getPost(\$id: ID!) { "
3 + "getPost(id: \$id) { "
4 + "id "
5 + "title "
6 + "rating "
7 + "status "
8 + "comments { "
9 + "items { "
10 + "id "
11 + "postID "
12 + "content "
13 + "} "
14 + "} "
15 + "} "
16 + "}")
17 return SimpleGraphQLRequest(
18 document,
19 mapOf("id" to id),
20 Post::class.java,
21 GsonVariablesSerializer())
22}

Then query for the Post:

1Amplify.API.query(getPostWithCommentsRequest("[POST_ID]"),
2 response -> Log.d("MyAmplifyApp", "response" + response),
3 error -> Log.e("MyAmplifyApp", "error" + error)
4);
1Amplify.API.query(getPostWithCommentsRequest("[POST_ID]"),
2 { Log.d("MyAmplifyApp", "Response = $it") },
3 { Log.e("MyAmplifyApp", "Error!", it) }
4)
1try {
2 val response = Amplify.API.query(getPostWithCommentsRequest("[POST_ID]"))
3 Log.d("MyAmplifyApp", "Query response = $response")
4} catch (error: ApiException) {
5 Log.e("MyAmplifyApp", "Query error", error)
6}

Combining Multiple Operations

When you want to perform more than one operation in a single request, you can place them within the same document. For example, to retrieve a Post and a Todo

1private GraphQLRequest<String> getPostAndTodo(String postId, String todoId) {
2 String document = "query get($postId: ID!, $todoId: ID!) { "
3 + "getPost(id: $postId) { "
4 + "id "
5 + "title "
6 + "rating "
7 + "} "
8 + "getTodo(id: $todoId) { "
9 + "id "
10 + "name "
11 + "} "
12 + "}";
13 Map<String, Object> variables = new HashMap<>();
14 variables.put("postId", postId);
15 variables.put("todoId", todoId);
16 return new SimpleGraphQLRequest<>(
17 document,
18 variables,
19 String.class,
20 new GsonVariablesSerializer());
21}
1private fun getPostAndTodo(postId: String, todoId: String): GraphQLRequest<String> {
2 val document = ("query get(\$postId: ID!, \$todoId: ID!) { "
3 + "getPost(id: \$postId) { "
4 + "id "
5 + "title "
6 + "rating "
7 + "} "
8 + "getTodo(id: \$todoId) { "
9 + "id "
10 + "name "
11 + "} "
12 + "}")
13
14 return SimpleGraphQLRequest(
15 document,
16 mapOf("postId" to postId, "todoId" to todoId),
17 String::class.java,
18 GsonVariablesSerializer())
19}

Adding Headers to Outgoing Requests

By default, the API plugin includes appropriate authorization headers on your outgoing requests. However, you may have an advanced use case where you wish to send additional request headers to AppSync.

To specify your own headers, use the configureClient() configuration option on the AWSApiPlugin's builder. Specify the name of one of the configured APIs in your amplifyconfiguration.json. Apply customizations to the underlying OkHttp instance by providing a lambda expression as below.

1AWSApiPlugin plugin = AWSApiPlugin.builder()
2 .configureClient("yourApiName", okHttpBuilder -> {
3 okHttpBuilder.addInterceptor(chain -> {
4 Request originalRequest = chain.request();
5 Request updatedRequest = originalRequest.newBuilder()
6 .addHeader("customHeader", "someValue")
7 .build();
8 return chain.proceed(updatedRequest);
9 });
10 })
11 .build();
12Amplify.addPlugin(plugin);
1val plugin = AWSApiPlugin.builder()
2 .configureClient("yourApiName") { okHttpBuilder ->
3 okHttpBuilder.addInterceptor { chain ->
4 val originalRequest = chain.request()
5 val updatedRequest = originalRequest.newBuilder()
6 .addHeader("customHeader", "someValue")
7 .build()
8 chain.proceed(updatedRequest)
9 }
10 }
11 .build()
12Amplify.addPlugin(plugin)
1val plugin = AWSApiPlugin.builder()
2 .configureClient("yourApiName") { okHttpBuilder ->
3 okHttpBuilder.addInterceptor { chain ->
4 val originalRequest = chain.request()
5 val updatedRequest = originalRequest.newBuilder()
6 .addHeader("customHeader", "someValue")
7 .build()
8 chain.proceed(updatedRequest)
9 }
10 }
11 .build()
12Amplify.addPlugin(plugin)
1AWSApiPlugin plugin = AWSApiPlugin.builder()
2 .configureClient("yourApiName", okHttpBuilder -> {
3 okHttpBuilder.addInterceptor(chain -> {
4 Request originalRequest = chain.request();
5 Request updatedRequest = originalRequest.newBuilder()
6 .addHeader("customHeader", "someValue")
7 .build();
8 return chain.proceed(updatedRequest);
9 });
10 })
11 .build();
12RxAmplify.addPlugin(plugin);