Join us from October 8-10 in New York City to learn the latest tips, trends, and news about GraphQL federation and API platform engineering.Join us for GraphQL Summit 2024 in NYC
Docs
Start for Free

11. Write your first subscription


In this section, you will use subscriptions to get notified whenever someone books a flight 🚀! Subscriptions allow to be notified in real time whenever an event happens on your server. The fullstack backend supports based on WebSockets.

Write your subscription

Open your Sandbox back up, click on the Schema tab at the far left. In addition to queries and mutations, you will see a third type of , subscriptions. Click on subscriptions to see the tripsBooked :

The definition of tripsBooked in the schema

This subscription doesn't take any and returns a single named tripsBooked. Since you can book multiple trips at once, tripsBooked is an Int. It will contain the number of trips booked at once or -1 if a trip has been cancelled.

Click the play button to the left of tripsBooked to open the subscription in Explorer. Open a new tab, then check the tripsBooked button to have the subscription added:

The initial definition of the TripsBooked subscription

Again, rename your subscription so it's easier to find:

The subscription after rename

Click the Submit button, and your subscription will start listening to events. You can tell it's up and running because a panel will pop up at the lower left where subscription data will come in:

The UI showing that it's listening for subscription updates

Test your subscription

Open a new tab in Explorer. In this new tab, add code to book a trip like on step 9, but with a hard-coded ID:

mutation BookTrip {
bookTrips(launchIds: ["93"]){
message
}
}

Do not forget to include the authentication header. At the bottom of the Sandbox Explorer pane where you add operations, there's a Headers section:

Adding a login token to explorer

Click the Submit Operation button. If everything went well, you just booked a trip! At the top of the right panel, you'll see the success JSON for your your BookTrip , and below it, updated JSON for the TripsBooked subscription:

Subscription success in Explorer

Continue booking and/or canceling trips, you will see events coming in the subscription panel in real time. After some time, the server might close the connection and you'll have to restart your subscription to keep receiving events.

Add the subscription to the project

Now that your subscription is working, add it to your project. Create a file named TripsBooked.graphql next to schema.graphqls and your other files and paste the contents of the subscription. The process is similar to what you did for queries and :

app/src/main/graphql/TripsBooked.graphql
subscription TripsBooked {
tripsBooked
}

Configure your ApolloClient to use subscriptions

The tutorial uses the subscriptions-transport-ws protocol for subscriptions. For more options like the newer graphql-ws or Appsync, see GraphQL over WebSocket protocols.

In Apollo.kt, configure a webSocketServerUrl for your ApolloClient:

Apollo.kt
val apolloClient = ApolloClient.Builder()
.serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/graphql")
.webSocketServerUrl("wss://apollo-fullstack-tutorial.herokuapp.com/graphql")
.okHttpClient(...)
.build()

wss:// is the protocol for WebSocket.

Display a SnackBar when a trip is booked/cancelled

In MainActivity, register your subscription and keep a reference to the returned coroutine Flow.

Use collectAsState to get the latest value of the Flow as a state. When the Flow emits a value, it will be stored in tripBookedResponse and trigger a re of the UI thanks to the LaunchedEffect that depends on it.

MainActivity.kt
RocketReserverTheme {
val tripBookedFlow = remember { apolloClient.subscription(TripsBookedSubscription()).toFlow() }
val tripBookedResponse: ApolloResponse<TripsBookedSubscription.Data>? by tripBookedFlow.collectAsState(initial = null)
LaunchedEffect(tripBookedResponse) {
if (tripBookedResponse == null) return@LaunchedEffect
val message = when (tripBookedResponse!!.data?.tripsBooked) {
null -> "Subscription error"
-1 -> "Trip cancelled"
else -> "Trip booked! 🚀"
}
// TODO use the message
}

Now let's display the message in a Material SnackBar.

To do this, you'll need to create a SnackbarHostState and call showSnackbar on it. Don't forget to also pass it to the Scaffoldbelow:

MainActivity.kt
val snackbarHostState = remember { SnackbarHostState() }
val tripBookedFlow = (...)
(...)
val message = (...)
snackbarHostState.showSnackbar(
message = message,
duration = SnackbarDuration.Short
)
}
Scaffold(
topBar = { TopAppBar({ Text(stringResource(R.string.app_name)) }) },
snackbarHost = { SnackbarHost(snackbarHostState) },
) { paddingValues ->

Handle errors

Like for queries and mutations, the subscription will throw an error if the connection is lost or any other protocol error happens. To handle these situations, you can configure the client to retry the subscription with the webSocketReopenWhen function. Return true to retry, false to stop. To avoid retrying too often, you can use the attempt parameter to delay the retry:

Apollo.kt
val apolloClient = ApolloClient.Builder()
(...)
.webSocketReopenWhen { throwable, attempt ->
Log.d("Apollo", "WebSocket got disconnected, reopening after a delay", throwable)
delay(attempt * 1000)
true
}

Test your code

Build and run your app and go back to Sandbox Explorer, and select the tab where you set up the BookTrip mutation. Book a new trip while your app is open, you should see a SnackBar 🚀:

A trip has been booked

This concludes the tutorial, congratulations! 🎉

More resources

Use the rest of this documentation for more advanced topics like Caching or Gradle configuration.

Feel free to ask questions by either opening an issue on our GitHub repo, joining the community or stopping by our channel in the KotlinLang Slack(get your invite here).

And if you want to dig more and see GraphQL in real-world apps, you can take a look at these open source projects using :

Previous
10. Authenticate your operations
Next
File types
Rate articleRateEdit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company