Docs
Launch GraphOS Studio

9. Write your first mutation


In this section, you will write your first to log in to the backend.

A is used to change data on your server. Here the login mutation will create a session based on your email address.

Note: The way you log in to this particular server might differ from the way you log in with your own server. Login is often handled by middleware, or a layer totally separate from GraphQL, like Oauth. Also note that a typical authentication flow should require a password but for this tutorial, anyone is allowed to book flights with a valid email address!

Prototype your mutation in Sandbox Explorer

Open your Sandbox Explorer and click on the plus symbol to add a new tab. Next, click on the icon to get back to looking at your schema, and select "" to look at your list of mutations:

The list of available mutations

Scroll down to take a look at the login :

The definition of login in the schema

Click the play button to the right to open that in the tab. When it opens, click the plus sign to add the :

The login mutation after initially being added

Notice the red error indication - this is because the type returned by the is User, which is not a leaf type: you need to choose which of the user's s the will return. For our purposes, we only need the token , so add it by clicking the plus sign next to it.

You'll also notice that email wasn't automatically added as an even though it doesn't seem to have a default value: that's because email is of type String - which remember, in GraphQL, means that it's not required (although obviously you won't get too far without it).

Click the plus sign next to the email to have that argument added:

The operation with the email argument

You'll also notice that Sandbox has added a to your "Variables" section to match the login email.

Click the Submit button your . You'll see that since you sent null for the email address, you get back null for the login:

Results of passing a null email

Now, replace null in the Query s section with an actual email address:

(Sandbox Explorer)
{ "email": "me@example.com" }

Press the Submit button, and this time you'll get an actual response:

Results of passing an actual email

Next, copy the , either manually or using the three-dot menu's "Copy operation" option.

Add the mutation to the project

Now that your is working, add it to your project. Create a file named Login.graphql next to schema.graphqls and your other GraphQL files and paste the contents of the :

app/src/main/graphql/Login.graphql
mutation Login($email: String!) {
login(email: $email) {
token
}
}

Note: we've also marked the email as non-nullable by adding ! to the end of the type, since we always want to pass a value for it.

Build your project to generate the LoginMutation class.

Connect the Submit button to your mutation

To navigate back to the previous screen after logging-in, add a navigateBack lambda parameter to the Login composable:

app/src/main/java/com/example/rocketreserver/Login.kt
@Composable
fun Login(navigateBack: () -> Unit) {

Again, it should be initialized in MainActivity:

app/src/main/java/com/example/rocketreserver/MainActivity.kt
composable(route = NavigationDestinations.LOGIN) {
Login(
navigateBack = {
navController.popBackStack()
}
)
}

Go back to Login.kt and create a function to execute the Login :

app/src/main/java/com/example/rocketreserver/Login.kt
private suspend fun login(email: String): Boolean {
val response = try {
apolloClient.mutation(LoginMutation(email = email)).execute()
} catch (e: ApolloException) {
Log.w("Login", "Failed to login", e)
return false
}
if (response.hasErrors()) {
Log.w("Login", "Failed to login: ${response.errors?.get(0)?.message}")
return false
}
val token = response.data?.login?.token
if (token == null) {
Log.w("Login", "Failed to login: no token returned by the backend")
return false
}
TokenRepository.setToken(token)
return true
}

The possible error cases are handled and a boolean is returned to indicate if the login was successful or not. If it was, the token is saved in the TokenRepository.

Note that the function is marked as suspend and so will need to be called from a coroutine. To do that, declare a scope in Login:

app/src/main/java/com/example/rocketreserver/Login.kt
// Submit button
val scope = rememberCoroutineScope()

Then, in the onClick lambda, replace the TODO with a call to login(), and handle the result:

app/src/main/java/com/example/rocketreserver/Login.kt
Button(
modifier = Modifier
.padding(top = 32.dp)
.fillMaxWidth(),
onClick = {
scope.launch {
val ok = login(email)
if (ok) navigateBack()
}
}
) {
Text(text = "Submit")
}

To improve the UX, add a loading indicator that will be shown while the login is in progress (let's also disable the button to avoid multiple clicks):

app/src/main/java/com/example/rocketreserver/Login.kt
// Submit button
var loading by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
Button(
modifier = Modifier
.padding(top = 32.dp)
.fillMaxWidth(),
enabled = !loading,
onClick = {
loading = true
scope.launch {
val ok = login(email)
loading = false
if (ok) navigateBack()
}
}
) {
if (loading) {
Loading()
} else {
Text(text = "Submit")
}
}

Test the login

Go to the details screen, click Book and in the Login screen, enter your email and click Submit. You now have a token that allows you to authenticate your s.

The login screen

In the next section, you will use authenticated operations to book a flight.

Previous
8. Add a details view
Next
10. Authenticate your operations
Edit on GitHubEditForumsDiscord