Skip to content

Mutations

The DGS framework supports Mutations with the same constructs as data fetchers, using the @DgsData annotation. The following is a simple example of a mutation:

type Mutation {
    addRating(title: String, stars: Int):Rating
}

type Rating {
    avgStars: Float
}
@DgsComponent
public class RatingMutation {
    @DgsData(parentType = "Mutation", field = "addRating")
    public Rating addRating(DataFetchingEnvironment dataFetchingEnvironment) {
        int stars = dataFetchingEnvironment.getArgument("stars");

        if(stars < 1) {
            throw new IllegalArgumentException("Stars must be 1-5");
        }

        String title = dataFetchingEnvironment.getArgument("title");
        System.out.println("Rated " + title + " with " + stars + " stars") ;

        return new Rating(stars);
    }
}

Note that the code above retrieves the input data for the Mutation by calling the DataFetchingEnvironment.getArgument method, just as data fetchers do for their arguments.

Input Types

In the example above the input was two standard scalar types. You can also use complex types, and you should define these as input types in your schema. An input type is almost the same as a type in GraphQL, but with some extra rules.

According to the GraphQL specification an input type should always be passed to the data fetcher as a Map. This means the DataFetchingEnvironment.getArgument for an input type is a Map, and not the Java/Kotlin representation that you might have. The framework has a convenience mechanism around this, which will be discussed next. Let's first look at an example that uses DataFetchingEnvironment directly.

type Mutation {
    addRating(input: RatingInput):Rating
}

input RatingInput {
    title: String,
    stars: Int
}

type Rating {
    avgStars: Float
}
@DgsComponent
public class RatingMutation {
    @DgsData(parentType = "Mutation", field = "addRating")
    public Rating addRating(DataFetchingEnvironment dataFetchingEnvironment) {

        Map<String,Object> input = dataFetchingEnvironment.getArgument("input");
        RatingInput ratingInput = new ObjectMapper().convertValue(input, RatingInput.class);

        System.out.println("Rated " + ratingInput.getTitle() + " with " + ratingInput.getStars() + " stars") ;

        return new Rating(ratingInput.getStars());
    }
}

class RatingInput {
    private String title;
    private int stars;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getStars() {
        return stars;
    }

    public void setStars(int stars) {
        this.stars = stars;
    }
}

Input arguments as data fetcher method parameters

The framework makes it easier to get input arguments. You can specify arguments as method parameters of a data fetcher.

@DgsComponent
public class RatingMutation {
    @DgsData(parentType = "Mutation", field = "addRating")
    public Rating addRating(@InputArgument("input") RatingInput ratingInput) {
        //No need for custom parsing anymore!
        System.out.println("Rated " + ratingInput.getTitle() + " with " + ratingInput.getStars() + " stars") ;

        return new Rating(ratingInput.getStars());
    }
}

The @InputArgument annotation is important to specify the name of the input argument, because arguments can be specified in any order. If no annotation is present, the framework tries to use the parameter name, but this is only possible if the code is compiled with specific compiler settings. Input type parameters can be combined with a DataFetchingEnvironment parameter.

@DgsComponent
public class RatingMutation {
    @DgsData(parentType = "Mutation", field = "addRating")
    public Rating addRating(@InputArgument("input") RatingInput ratingInput, DataFetchingEnvironment dfe) {
        //No need for custom parsing anymore!
        System.out.println("Rated " + ratingInput.getTitle() + " with " + ratingInput.getStars() + " stars") ;
        System.out.println("DataFetchingEnvironment: " + dfe.getArgument(ratingInput));

        return new Rating(ratingInput.getStars());
    }
}

Kotlin data types

In Kotlin, you can use Data Classes to represent input types. However, make sure its fields are either var or add a @JsonProperty to each constructor argument, and use jacksonObjectMapper() to create a Kotlin-compatible Jackson mapper.

data class RatingInput(var title: String, var stars: Int)