Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 3794

How to Return anydata on GraphQL Mutation

$
0
0

1. Introduction

When working with GraphQL mutations, we often encounter situations where returning a fixed type is too limiting. For example, sometimes we may want to return a simple success message, and other times a detailed object depending on the input or business logic.

In this tutorial, we’ll explore how to return anydata from a GraphQL mutation using Java.

2. Understanding anydata

In the context of GraphQL, anydata refers to any type of value that a mutation can return to the client.

This can vary depending on the use case or the logic within the mutation. For example, the mutation might return:

  • A simple message (String)
  • A complex object (like a User)
  • A custom wrapper that includes multiple fields

GraphQL doesn’t have a built-in type like JavaScript’s any, so we define the schema to allow flexible return types.

3. Defining the GraphQL Schema

After setting up our dependencies, let’s move on to defining our GraphQL schema. This is where we describe the structure of our mutation and the types of data it can return.

We’ll create a file named schema.graphqls under the src/main/resources directory:

type Mutation {
    updateProfile(name: String!, type: String!): AnydataResponse
}
union AnydataResponse = SimpleMessage | UserProfile
type SimpleMessage {
    message: String!
}
type UserProfile {
    id: ID!
    name: String!
    status: String!
}

In this example, we define a mutation called updateProfile. Depending on the input, the mutation may return either a simple message or a detailed user profile. Since the return type can vary, we use the union type in GraphQL to support multiple return types under a single name.

4. Creating the Models in Java

Now that we’ve defined our GraphQL schema, let’s create the corresponding Java classes for each GraphQL type. These models represent the possible return values for our updateProfile mutation.

We’ll create the following classes:

  • A marker interface AnydataResponse
  • SimpleMessage
  • UserProfile

4.1. Marker Interface – AnydataResponse

Since GraphQL unions map to multiple Java types, let’s use a common marker interface that both return types implement:

public interface AnydataResponse {
}

This interface doesn’t have any methods. It’s only used to tell GraphQL Java that both SimpleMessage and UserProfile are valid types for the AnydataResponse union.

4.2. SimpleMessage

This class holds a basic String message:

public class SimpleMessage implements AnydataResponse {
    private String message;
    // constructor, setters and getters
}

4.3. UserProfile

This class represents a user profile:

public class UserProfile implements AnydataResponse {
    private String id;
    private String name;
    private String status;
    //constructor, setters and getters
}

5. Implementing the Mutation Resolver

Now that we’ve created our model classes, let’s implement the actual mutation logic in Java. This is where we define what happens when a client calls the updateProfile mutation:

public DataFetcher<AnydataResponse> updateProfile() {
    return new DataFetcher<AnydataResponse>() {
        @Override
        public AnydataResponse get(DataFetchingEnvironment environment) {
            String name = environment.getArgument("name");
            String type = environment.getArgument("type");
            if ("message".equalsIgnoreCase(type)) {
                return new SimpleMessage("Profile updated for: " + name);
            } else if ("user".equalsIgnoreCase(type)) {
                return new UserProfile("123", name, "ACTIVE");
            } else {
                return new SimpleMessage("Unknown type provided.");
            }
        }
    };
}

In this example, we define a method called updateProfile() that returns a DataFetcher, which is a standard way to implement custom logic in GraphQL Java. Inside the get() method of the DataFetcher, we retrieve the name and type arguments that are passed in by the client when calling the mutation.

We then use the type value to decide what kind of response to return. If the value of type is “message“, we create and return a SimpleMessage object that includes a short confirmation message. On the other hand, if the type is “user“, we return a complete UserProfile object.

6. Testing the Mutation

Now, we’ll set up the GraphQL engine, link the schema to the resolver, and write a basic unit test to ensure the mutation returns the expected results.

Let’s begin with a configuration class that initializes the GraphQL instance:

@Bean
public GraphQL graphQL() {
    SchemaParser schemaParser = new SchemaParser();
    SchemaGenerator schemaGenerator = new SchemaGenerator();
    TypeDefinitionRegistry typeRegistry = schemaParser.parse(
      new InputStreamReader(getClass().getResourceAsStream("/schema.graphqls"))
    );
    RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
      .type("Mutation", builder ->
        builder.dataFetcher("updateProfile", new MutationResolver().updateProfile()))
      .build();
    GraphQLSchema schema = schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
    return GraphQL.newGraphQL(schema).build();
}

Next, we can verify the mutation using a basic unit test:

String mutationMsg = "mutation { updateProfile(name: \"Alice\", type: \"message\") { ... on SimpleMessage { message } } }";
String mutationUser = "mutation { updateProfile(name: \"Bob\", type: \"user\") { ... on UserProfile { id name status } } }";
ExecutionResult result = graphQL.execute(ExecutionInput.newExecutionInput().query(mutationMsg).build());
Map<String, Object> data = result.getData();
Map<String, Object> updateProfile = (Map<String, Object>) data.get("updateProfile");
assertEquals("Profile updated for Alice", updateProfile.get("message"));
ExecutionResult result = graphQL.execute(ExecutionInput.newExecutionInput().query(mutationUser).build());
data = result.getData();
userProfile = (Map<String, Object>) data.get("updateProfile");
assertEquals("Bob", userProfile.get("name"));
assertEquals("ACTIVE", userProfile.get("status"));

In these examples, we invoke the updateProfile mutation with different input values to test the return types. The “… on” syntax uses GraphQL’s inline fragments to extract fields based on the actual return type – SimpleMessage or UserProfile.

7. Conclusion

In this article, we explored how to return anydata from a GraphQL mutation using unions and custom logic. This approach makes our API flexible and able to handle various response types easily. 

As always, the source code is available over on GitHub.

The post How to Return anydata on GraphQL Mutation first appeared on Baeldung.
       

Viewing all articles
Browse latest Browse all 3794

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>