6. The Subscription resolver
15m

Overview

Our is responsible for publishing events; it's our 's job to subscribe to them!

In this lesson, we will:

  • Write our and discuss its requirements

The Subscription resolver

In the messages/src/resolvers folder, create a new file called Subscription.ts.

📂 resolvers
┣ 📄 Conversation.ts
┣ 📄 index.ts
┣ 📄 Message.ts
┣ 📄 Mutation.ts
┣ 📄 Subscription.ts
┣ 📄 Query.ts
┗ 📄 User.ts

We'll start by adding some boilerplate. Paste the following code into your file.

src/resolvers/Subscription.ts
import { Resolvers } from "../__generated__/resolvers-types";
export const Subscription: Resolvers = {
Subscription: {
// TODO
},
};

We'll also need to include this in our full resolvers object. Jump over to src/resolvers/index.ts and uncomment a couple of lines involving the Subscription .

src/resolvers/index.ts
import { Query } from "./Query";
import { Mutation } from "./Mutation";
import { Message } from "./Message";
import { Conversation } from "./Conversation";
import { User } from "./User";
import { Subscription } from "./Subscription";
const resolvers = {
...Query,
...Mutation,
...Conversation,
...Message,
...User,
...Subscription,
};
export default resolvers;

Back in Subscription.ts, we'll finish up our object. As you might expect, we'll provide a key that matches our Subscription type's : messageInConversation.

src/resolvers/Subscription.ts
Subscription: {
messageInConversation: // TODO
}

Unlike our other , however, we won't define a function as this key's value. Instead, we'll open up another object—and define a subscribe key inside of it. The subscribe key is where we'll put our actual function, which has access to the same resolver we're accustomed to using (parent, args, contextValue, and info.)

src/resolvers/Subscription.ts
// ... other resolvers
Subscription: {
messageInConversation: {
subscribe: () => {},
}
}

Note: Our messageInConversation object can define an additional property: resolve. We can use the resolve function to further drill into the payload that we receive from each emitted event. We won't use resolve in this course.

Great! Now what should this subscribe function return? Consulting our schema, we're expected to return a Message type. However, there's one catch: deal with asynchronous data. The most important requirement for the subscribe function is that it must return an AsyncIterator type.

AsyncIterator

AsyncIterator is an interface that allows us to iterate over asynchronous results—exactly the kind of data we'd expect to get from a ! We won't have to define a new class to make this work; the PubSub library has us covered with a special method that handles all the details.

Note: Want to learn more about the AsyncIterator interface? Check out the MDN Web Docs.

The pubsub.asyncIterableIterator method accepts an array, where we can specify the events that the should be listening for, and whose results it should iterate over.

Let's set this up! Back in Subscription.ts, we'll start by accessing pubsub inside of the subscribe by destructuring its third positional arguemnt. Then we'll call the asyncIterableIterator method.

src/resolvers/Subscription.ts
messageInConversation: {
subscribe: (_, __, { pubsub }) => {
return pubsub.asyncIterableIterator();
},
}

Inside the asyncIterableIterator method, we'll define the array of events we want to listen for. We have just a single event—the one we published in our —called "NEW_MESSAGE_SENT".

src/resolvers/Subscription.ts
subscribe: (_, __, { pubsub }) => {
return pubsub.asyncIterableIterator(["NEW_MESSAGE_SENT"])
},

Great! Our subscribe function is returning a PubSubAsyncIterableIterator type (a type that satisfies the required AsyncIterator interface), and we've configured it for our specific "NEW_MESSAGE_SENT" event. But...how does this subscribe function actually return data—specifically, each new message sent to a conversation?

Publishing an event with payload

Let's take our and and look at them side-by-side.

Mutation: {
sendMessage: () => {
// ... resolver logic
await pubsub.publish("NEW_MESSAGE_SENT", {}); 1️⃣
// ... return message
},
},
Subscription: {
messageInConversation: {
subscribe: (_, __, { pubsub }) => {
return pubsub.asyncIterableIterator(["NEW_MESSAGE_SENT"])
},
}
}

Here we can see 1) where an event is published, and 2) where the event is being subscribed to.

But is there actually any message data getting passed along with it? In other words—will our actually return anything remotely like the Message type in our schema?

Well...not yet. There's an important piece that we're missing here, and that's the event payload. Inside our , we passed an empty object ({}) as the payload to our publish call. So anytime a "NEW_MESSAGE_SENT" event is published, an empty object is all that gets passed along as the actual event data!

What we need to do instead is pass along the message that was submitted as part of the . We'll tackle that in the next lesson.

Practice

Which of the following are differences between subscription resolvers and other resolvers?

Key takeaways

  • can be defined by providing an object with a subscribe key. The subscribe function has access to all of the usual (parent, args, contextValue, and info).
  • An important requirement for any subscribe function is that it should return an AsyncIterator type (or a type that satisfies the AsyncIterator interface, such as PubSubAsyncIterableIterator.)
  • The PubSub class gives us a method called asyncIterableIterator, which lets us iterate over asynchronous events (such as sending a new message) as they're dispatched in our system.

Up next

Nearly there! Let's wrap up our publish call and pass along the actual data we want our to return.

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.