Typed PubSub System
Baeta provides a type-safe wrapper around graphql-subscriptions
PubSub system. This utility ensures that your subscription channels and their payloads are properly typed, helping catch potential errors at compile time.
tip
If you are using GraphQL Yoga this is package is unnecessary as it already provides a type-safe PubSub system.
Installation
- yarn
- npm
- pnpm
- bun
yarn add @baeta/subscriptions-pubsub
npm install @baeta/subscriptions-pubsub
pnpm add @baeta/subscriptions-pubsub
bun add @baeta/subscriptions-pubsub
Basic Setup
- Define Your Event Map
First, create a type map that defines your subscription channels and their corresponding payload types:
import { createTypedPubSub } from "@baeta/subscriptions-pubsub";
import { PubSub } from "graphql-subscriptions";
import type { User } from "../__generated__/types";
export type PubSubMap = {
"user-updated": User;
[c: `user-updated-${string}`]: User;
// Add more channels as needed:
// 'post-created': Post;
// 'comment-deleted': { postId: string; commentId: string };
};
- Create the Typed PubSub Instance
/**
* For development/simple setups. For production, consider using
* more robust solutions like graphql-redis-subscriptions
*/
export const pubsub = createTypedPubSub<PubSub, PubSubMap>(new PubSub());
- Add to Your Context
export interface Context {
pubsub: typeof pubsub;
// ... other context properties
}
export function createContext(): Context {
return {
pubsub,
// ... other context properties
};
}
Usage
Publishing Events
The typed PubSub system ensures you can only publish to defined channels with the correct payload type:
Mutation.updateUser(({ args, ctx }) => {
const updatedUser = {
id: args.where.id,
email: "[email protected]",
givenName: args.data.givenName ?? "Jon",
lastName: args.data.lastName ?? "Doe",
};
// Type-safe publish - TypeScript will error if channel or payload type is incorrect
ctx.pubsub.publish("user-updated", updatedUser);
return updatedUser;
});
Subscribing to Events
Create subscription resolvers using the typed asyncIterableIterator:
Subscription.userUpdated({
subscribe(params) {
// Type-safe subscription - channel name is autocompleted and type-checked
return params.ctx.pubsub.asyncIterableIterator("user-updated");
},
resolve(params) {
// params.payload is properly inferred as User
return params.payload;
},
});
Using with Redis
For production environments, you might want to use a more robust solution like graphql-redis-subscriptions
:
import { RedisPubSub } from "graphql-redis-subscriptions";
import Redis from "ioredis";
const options = {
host: "redis-server",
port: 6379,
retryStrategy: (times) => Math.min(times * 50, 2000),
};
const pubsub = createTypedPubSub<RedisPubSub, PubSubMap>(
new RedisPubSub({
publisher: new Redis(options),
subscriber: new Redis(options),
}),
{
prefix: "feature-1:", // Optionally supports prefixing channels
},
);