Open
Description
In v4, a codegen option generateInputBuilders
has been added to generate builders for Input Objects.
For this input object:
input UserInput {
firstName: String!
lastName: String!
email: String
}
the generated class will look like this:
data class UserInput(
val firstName: String,
val lastName: String,
val email: Optional<String?> = Optional.Absent,
) {
class Builder {
private var firstName: String? = null
private var lastName: String? = null
private var email: Optional<String?> = Optional.Absent
fun firstName(firstName: String): Builder {
this.firstName = firstName
return this
}
fun lastName(lastName: String): Builder {
this.lastName = lastName
return this
}
fun email(email: String?): Builder {
this.email = Optional.Present(email)
return this
}
fun build(): UserInput = UserInput(
firstName = firstName ?: error("missing value for firstName"),
lastName = lastName ?: error("missing value for lastName"),
email = email,
)
}
}
The check for non optional arguments is done at runtime in the build()
method.
Usage:
UserInput.Builder()
.firstName("John") // Could forget
.lastName("Doe") // Could forget
.email("[email protected]")
.build()
Putting them in the builder's constructor would make this a build time check:
data class UserInput(
val firstName: String,
val lastName: String,
val email: Optional<String?> = Optional.Absent,
) {
class Builder(
private val firstName: String,
private val lastName: String,
) {
private var email: Optional<String?> = Optional.Absent
fun email(email: String?): Builder {
this.email = Optional.Present(email)
return this
}
fun build(): UserInput = UserInput(
firstName = firstName,
lastName = lastName,
email = email,
)
}
}
Usage:
UserInput.Builder(
firstName = "John", // Can't forget to supply it
lastName = "Doe", // Can't forget to supply it
)
.email("[email protected]")
.build()
However:
- this makes the builder's API inconsistent (some arguments are setters / some are constructor arguments)
- for Input types that only have non optional arguments, the builder's constructor will be the same as the input type's constructor itself, but with the disadvantage of more verbosity of the builder pattern
- this is prone to the issue of the arguments that can change order with a new version of a schema (issue Arguments order for generated Kotlin/Java code #4659).
Other considerations:
- making this change later will probably be difficult
- this Kotlin issue discusses a feature request to enforce named arguments
- in case of
@oneOf
input objects, we would generate only builder constructors for each input field.