3. Setting up subscriptions
10m

Overview

Let's start to bring our to life. We'll start by adding our Subscription type, and finish by running our locally with rover dev.

In this lesson, we will:

  • Introduce the Subscription type in the messages
  • Add a corresponding Mutation to send a new message
  • our with rover dev
  • Save the we'll use throughout the course

The chat feature and the schema

Before we write our logic, let's walk through exactly how we want our chat feature to work.

To enable realtime communication between guest and host, we have a few considerations.

  1. We'd first need to create a conversation for all of our messages to go to.
  2. Next, we'd need a way to send a message to a particular conversation.
  3. We also need a way to subscribe to messages in a particular conversation (so we're notified whenever a new message is sent!)

It's clear that we need to add some new types and to our schema to support this feature. This is a course about , so let's start with the Subscription type!

Adding the Subscription type

Open up the messages directory, and navigate to the schema.graphql file in the src folder.

Our schema needs an that will enable us to listen for messages in a particular conversation. We'll define this as our new Subscription type's very first —so make some room in the file and add the following code.

messages/src/schema.graphql
type Subscription {
listenForMessageInConversation(id: ID!): Message
}

As we discussed in the last lesson, this accepts an id for the conversation it's listening to. With each new event, it returns the Message that was sent to the conversation. (Or, at least, that's what we'll wire up this to do! For now, we're still missing the logic.)

That's it for the Subscription type—but we're not done with our schema just yet.

Adding the new Mutation field

Next, find the Mutation type in schema.graphql. Right now you'll see it has just one : createConversation. This accepts a recipientId and returns a new Conversation (between whoever is logged in, and that recipient).

schema.graphql
type Mutation {
# Initiate a new conversation with a particular user
createConversation(recipientId: ID!): Conversation
}

We've already got a way to create a conversation; now we need a way to send new messages to it! For that we'll need a new Mutation .

Let's add a new called sendMessage.

type Mutation {
# Initiate a new conversation with a particular user
createConversation(recipientId: ID!): Conversation
# Submit a new message to a specified conversation
sendMessage
}

We'll need to send some data along when we call this , so we'll provide an called message. Rather than setting it as a Message type, however, we'll make use of the NewMessageInput type located further down in our schema. This lets us specify both text as well as conversationId so that the message we send ends up in the right place. And let's be sure to make our NewMessageInput non-nullable—no empty messages allowed!

sendMessage(message: NewMessageInput!)

Finally, for our 's return type, we'll just return the Message that we created.

sendMessage(message: NewMessageInput!): Message

Note: You can learn more about the input type, as well as other types and features in Side Quest: Intermediate Schema Design.

Booting up rover dev

Our schema is all set, but we still haven't actually gotten our up and running. We're in development mode, so let's try things out locally. The gives us a way of doing just that: with the rover dev command.

With rover dev, we can feed in the data about our locally running . takes care of booting up a local , which we can then execute against. It's everything we need to validate the types, , and logic we add to our graph.

Note: As we mentioned, rover dev is a great tool for development. It's important that you do not run this command in production!

We're going to provide with a configuration file that tells it where to find our running and read their schemas.

Good news—this configuration file already exists inside our router directory! Jump in there, and let's open up supergraph.yaml.

📦 router
┣ 📄 router-config.yaml
┣ 📄 supergraph.yaml
┗ 📄 .env

Here's what's inside:

supergraph.yaml
federation_version: =2.8.0
subgraphs:
accounts:
routing_url: http://localhost:4002
schema:
file: ../accounts/src/schema.graphql # Schema provided via file
messages:
routing_url: http://localhost:4001
schema:
file: ../messages/src/schema.graphql # Schema provided via file

It's just a few lines, but this file tells exactly how to run and where to find the to compose into a . We've provided the details for both accounts and messages: where they're running, and the relative path to their schema file.

The rover dev command

To run rover dev, we'll need to first provide our credentials: this is what connects to our Studio organization and the graph we created in the first lesson of this course. You'll need both of the values we stored in router/.env.

Let's open up a terminal to the router directory and build out our command.

Terminal in router directory
APOLLO_KEY="..." \
APOLLO_GRAPH_REF="..." \
rover dev \
--supergraph-config supergraph.yaml

Make sure you swap in your unique values for both APOLLO_KEY and APOLLO_GRAPH_REF.

This command first connects to our in Studio and determines that we have Enterprise permissions. Next, it boots up the rover dev process, and feeds in the supergraph.yaml file we wrote as the configuration that should use when composing a local .

When you run this command, you should see a bunch of output in the terminal:

supergraph config loaded successfully
downloading the 'supergraph' plugin from https://rover.apollo.dev/tar/supergraph/x86_64-apple-darwin/v2.8.0
warning: Do not run this command in production! It is intended for local development only.
==> Watching /messages/src/schema.graphql for changes
==> Watching /accounts/accounts.graphql for changes
WARN: telemetry.instrumentation.spans.mode is currently set to 'deprecated', either explicitly or via defaulting. Set telemetry.instrumentation.spans.mode explicitly in your router.yaml to 'spec_compliant' for log and span attributes that follow OpenTelemetry semantic conventions. This option will be defaulted to 'spec_compliant' in a future release and eventually removed altogether
==> your supergraph is running! head to http://localhost:4000 to query your supergraph
successfully composed with version =2.8.0

Great! Let's open up http://localhost:4000 in the browser where the is running.

http://localhost:4000

A screenshot of Sandbox running our local supergraph

This places us in the Sandbox Explorer; it's a development environment where we can build and run them against our .

Testing out the supergraph

Our is up and running! And because our database has been pre-seeded with some data, we can run a few to test it out.

Try the following :

An operation to get the currently logged in user
query GetMe {
me {
id
name
profileDescription
lastActiveTime
isLoggedIn
}
}

If you run this as-is, you'll see an error in the Response panel. This is because we actually need to be authenticated first.

http://localhost:4000

A screenshot of Sandbox showing a failed operation because we are unauthenticated

We can fix this by clicking on the Headers tab, located on the window at the bottom of the Operation panel. Next, we'll click on the New Header button.

This gives us a space where we can define a header key and value.

http://localhost:4000

A screenshot of Sandbox, highlighting the Headers option in the bottom panel and the button to add a new header

To satisfy our accounts , we need to provide a header called Authorization and a value of Bearer <TOKEN>.

Configuring our header

Let's add our header now: we'll stick with a user ID we know is in our database: eves.

http://localhost:4000

A screenshot of Sandbox, highlighting where a header has been added

Authorization: Bearer eves

Pro tip: Click the little file and pencil icon next to + New Header. Here you can simply copy and paste your headers into place, and they'll be reformatted in the Headers tab automatically!

But if we run this ... we'll still see the same error. This is because we're sending our directly to the running through rover dev; and the doesn't automatically propagate headers to the underlying .

We need to provide specific instruction about which headers it should pass along, and which it should pass them to! Fortunately, our project already includes this configuration in our router/router-config.yaml file (take a peek if you're curious). We just need to revise our rover dev command to include it.

Stop the running rover dev process, and rerun the command, this time providing the --router-config flag shown below. We'll use this flag to specify the path to our router-config.yaml file.

APOLLO_KEY="..." \
APOLLO_GRAPH_REF="..." \
rover dev \
--supergraph-config supergraph.yaml \
--router-config router-config.yaml

Unlike --supergraph-config (which stated where the process could gather up all of our -specific details) this new --router-config flag applies specifically to the process.

Now let's take that for a spin again. And we've got data!

http://localhost:4000

A screenshot of Sandbox, showing a successful data response

Saving operations

Next, let's write out the we'll use for the remainder of the course. Because we'll be using these operations over and over again, it's a good idea to save them in an operation collection.

The first will subscribe us to a particular conversation. Open up a new tab and paste the following .

subscription SubscribeToMessagesInConversation(
$listenForMessageInConversationId: ID!
) {
listenForMessageInConversation(id: $listenForMessageInConversationId) {
text
sentTime
}
}

The second will send a new message to a given conversation. Open up another new tab and paste in the following operation.

mutation SendMessageToConversation($message: NewMessageInput!) {
sendMessage(message: $message) {
id
text
sentTo {
id
name
}
}
}

Just above each , you'll see a Save operation button.

http://localhost:4000

A screenshot of Sandbox, highlighting save operation button

When you click this button, a modal appears prompting you to provide the as well as the collection you want to save it to. We can click the Select a collection dropdown and choose Save to a new default Sandbox collection.

http://localhost:4000

A screenshot of Sandbox, showing the modal to save an operation

Let's take a little time to save these . We can then access them again by clicking on the bookmark icon above the Documentation panel. This opens up all of our saved collections, where we can easily add them back to the Operation panel.

http://localhost:4000

A screenshot of Sandbox, highlighting the bookmark icon where we can access saved operations

Practice

Which of the following are benefits of using rover dev?

Key takeaways

  • With rover dev, we can run a locally.
  • To configure the rover dev process, we can pass a configuration file using the --supergraph-config flag. This file contains details about the that should be composed, as well as where each schema can be found or introspected.
  • We can provide a --router-config flag to the rover dev process to further customize how the locally-running behaves.
  • collections in Explorer allow us to define and save operations we'll need to access again in the future.

Up next

We're schema complete, but we're missing all the logic that will make our work. Before we dive into the plumbing in our , let's make sure our locally-running is set up to understand the subscription we're going to send it.

Previous

Share your questions and comments about this lesson

This course is currently in

beta
. Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.