Overview
Let's start to bring our subscription operations to life. We'll start by adding our Subscription
type, and finish by running our supergraph locally with rover dev
.
In this lesson, we will:
- Introduce the
Subscription
type in themessages
subgraph - Add a corresponding
Mutation
field to send a new message - Launch our supergraph with
rover dev
- Save the operations we'll use throughout the course
The chat feature and the schema
Before we write our resolver 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.
- We'd first need to create a conversation for all of our messages to go to.
- Next, we'd need a way to send a message to a particular conversation.
- 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 fields to our schema to support this feature. This is a course about subscriptions, 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 operation that will enable us to listen for messages in a particular conversation. We'll define this as our new Subscription
type's very first field—so make some room in the file and add the following code.
type Subscription {listenForMessageInConversation(id: ID!): Message}
As we discussed in the last lesson, this field 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 field to do! For now, we're still missing the resolver 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 field: createConversation
. This field accepts a recipientId
and returns a new Conversation
(between whoever is logged in, and that recipient).
type Mutation {# Initiate a new conversation with a particular usercreateConversation(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
field.
Let's add a new field called sendMessage
.
type Mutation {# Initiate a new conversation with a particular usercreateConversation(recipientId: ID!): Conversation# Submit a new message to a specified conversationsendMessage}
We'll need to send some data along when we call this field, so we'll provide an argument 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 field'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 GraphQL 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 graph up and running. We're in development mode, so let's try things out locally. The Rover CLI 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 subgraphs. Rover takes care of booting up a local router, which we can then execute operations against. It's everything we need to validate the types, fields, 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 Rover with a configuration file that tells it where to find our running subgraphs 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:
federation_version: =2.8.0subgraphs:accounts:routing_url: http://localhost:4002schema:file: ../accounts/src/schema.graphql # Schema provided via filemessages:routing_url: http://localhost:4001schema:file: ../messages/src/schema.graphql # Schema provided via file
It's just a few lines, but this file tells Rover exactly how to run and where to find the subgraphs to compose into a supergraph. 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 graph credentials: this is what connects Rover 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.
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 Rover to our graph 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 Rover should use when composing a local supergraph.
When you run this command, you should see a bunch of output in the terminal:
supergraph config loaded successfullydownloading the 'supergraph' plugin from https://rover.apollo.dev/tar/supergraph/x86_64-apple-darwin/v2.8.0warning: 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 changesWARN: 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 supergraphsuccessfully composed with version =2.8.0
Great! Let's open up http://localhost:4000
in the browser where the router is running.
This places us in the Sandbox Explorer; it's a development environment where we can build operations and run them against our supergraph.
Testing out the supergraph
Our graph is up and running! And because our database has been pre-seeded with some data, we can run a few operations to test it out.
Try the following operation:
query GetMe {me {idnameprofileDescriptionlastActiveTimeisLoggedIn}}
If you run this operation as-is, you'll see an error in the Response panel. This is because we actually need to be authenticated first.
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.
To satisfy our accounts
subgraph, 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
.
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 operation... we'll still see the same error. This is because we're sending our query directly to the router running through rover dev
; and the router doesn't automatically propagate headers to the underlying subgraphs.
We need to provide specific instruction about which headers it should pass along, and which subgraphs 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 Rover process could gather up all of our subgraph-specific details) this new --router-config
flag applies specifically to the router process.
Now let's take that operation for a spin again. And we've got data!
Saving operations
Next, let's write out the operations 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 operation.
subscription SubscribeToMessagesInConversation($listenForMessageInConversationId: ID!) {listenForMessageInConversation(id: $listenForMessageInConversationId) {textsentTime}}
The second operation 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) {idtextsentTo {idname}}}
Just above each operation, you'll see a Save operation button.
When you click this button, a modal appears prompting you to provide the operation name 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.
Let's take a little time to save these operations. We can then access them again by clicking on the bookmark icon above the Documentation panel. This opens up all of our saved operation collections, where we can easily add them back to the Operation panel.
Practice
rover dev
?Key takeaways
- With
rover dev
, we can run a supergraph locally. - To configure the
rover dev
process, we can pass a configuration file using the--supergraph-config
flag. This file contains details about the subgraphs that should be composed, as well as where each schema can be found or introspected. - We can provide a
--router-config
flag to therover dev
process to further customize how the locally-running router behaves. - Operation 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 subscription work. Before we dive into the plumbing in our resolvers, let's make sure our locally-running router is set up to understand the subscription operations we're going to send it.
Share your questions and comments about this lesson
This course is currently in
You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.