Subscriptions
Learn how to handle real-time data updates with subscriptions.
Basic Subscription
Subscriptions enable real-time communication between client and server for chat messages, notifications, live dashboards, and more.
Define a subscription like queries and mutations:
typescript
import { graphql } from '$mearie';
import { useSubscription } from '@mearie/react';
const ChatMessages = ({ chatId }: { chatId: string }) => {
const { data, error } = useSubscription(
graphql(`
subscription MessageAddedSubscription($chatId: ID!) {
messageAdded(chatId: $chatId) {
id
content
author {
id
name
avatar
}
createdAt
}
}
`),
{ chatId },
);
return (
<div>
{data && <div>New message: {data.messageAdded.content}</div>}
{error && <div>Error: {error.message}</div>}
</div>
);
};Client Setup
Configure your client to support subscriptions.
Server-Sent Events (SSE)
Simple HTTP-based protocol. Recommended for most use cases.
typescript
import { createClient, httpExchange, subscriptionExchange } from '@mearie/react'; // or @mearie/vue, @mearie/svelte, @mearie/solid
import { createClient as createSSEClient } from 'graphql-sse';
import { schema } from '$mearie';
export const client = createClient({
schema,
exchanges: [
httpExchange({ url: 'https://api.example.com/graphql' }),
subscriptionExchange({
client: createSSEClient({
url: 'https://api.example.com/graphql',
}),
}),
],
});TIP
Install the graphql-sse package to use Server-Sent Events:
sh
npm install graphql-sseWebSocket
Alternative protocol with lower latency using GraphQL over WebSocket.
typescript
import { createClient, httpExchange, subscriptionExchange } from '@mearie/react'; // or @mearie/vue, @mearie/svelte, @mearie/solid
import { createClient as createWSClient } from 'graphql-ws';
import { schema } from '$mearie';
export const client = createClient({
schema,
exchanges: [
httpExchange({ url: 'https://api.example.com/graphql' }),
subscriptionExchange({
client: createWSClient({
url: 'wss://api.example.com/graphql',
}),
}),
],
});TIP
Install the graphql-ws package to use WebSocket:
sh
npm install graphql-wsLatest State Only
Display only the current state (e.g., online status):
typescript
const UserStatus = ({ userId }: { userId: string }) => {
const { data, loading, error } = useSubscription(
graphql(`
subscription UserStatusSubscription($userId: ID!) {
userStatus(userId: $userId) {
online
lastSeen
typing
}
}
`),
{ userId },
);
if (loading) return <div>Connecting...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<span className={data.userStatus.online ? 'online' : 'offline'}>
{data.userStatus.online ? 'Online' : 'Offline'}
</span>
{data.userStatus.typing && <span>typing...</span>}
</div>
);
};Accumulating Events
Collect events over time (e.g., chat messages):
typescript
import { useState, useCallback } from 'react';
import { useSubscription, type DataOf } from '@mearie/react';
import { graphql } from '$mearie';
import type { MessageAddedSubscription } from '$mearie';
const ChatMessages = ({ chatId }: { chatId: string }) => {
const [messages, setMessages] = useState<Message[]>([]);
const handleMessageAdded = useCallback((data: DataOf<MessageAddedSubscription>) => {
setMessages((prev) => [...prev, data.messageAdded]);
}, []);
useSubscription(
graphql(`
subscription MessageAddedSubscription($chatId: ID!) {
messageAdded(chatId: $chatId) {
id
content
author {
id
name
}
createdAt
}
}
`),
{ chatId },
{
onData: handleMessageAdded,
},
);
return (
<div>
{messages.map((message) => (
<div key={message.id}>{message.content}</div>
))}
</div>
);
};Combining with Queries
Load initial data with a query, then subscribe for updates:
typescript
import { useState, useCallback, useEffect } from 'react';
import { useQuery, useSubscription, type DataOf } from '@mearie/react';
import { graphql } from '$mearie';
import type { MessageAddedSubscription } from '$mearie';
const ChatRoom = ({ chatId }: { chatId: string }) => {
const [messages, setMessages] = useState<Message[]>([]);
// Load initial messages
const { data, loading } = useQuery(
graphql(`
query GetMessagesQuery($chatId: ID!) {
messages(chatId: $chatId) {
id
content
}
}
`),
{ chatId },
);
useEffect(() => {
if (data) {
setMessages(data.messages);
}
}, [data]);
const handleMessageAdded = useCallback((data: DataOf<MessageAddedSubscription>) => {
setMessages((prev) => [...prev, data.messageAdded]);
}, []);
// Subscribe to new messages
useSubscription(
graphql(`
subscription MessageAddedSubscription($chatId: ID!) {
messageAdded(chatId: $chatId) {
id
content
}
}
`),
{ chatId },
{
onData: handleMessageAdded,
},
);
if (loading) return <div>Loading...</div>;
return (
<div>
{messages.map((msg) => (
<div key={msg.id}>{msg.content}</div>
))}
</div>
);
};Subscription Options
Control subscription behavior with options:
typescript
import { useCallback } from 'react';
import { useSubscription, type DataOf } from '@mearie/react';
import { graphql } from '$mearie';
import type { MessageAddedSubscription } from '$mearie';
const handleData = useCallback((data: DataOf<MessageAddedSubscription>) => {
console.log('New message:', data.messageAdded);
}, []);
const handleError = useCallback((error: Error) => {
console.error('Subscription error:', error);
}, []);
const { data, error, loading } = useSubscription(
graphql(`
subscription MessageAddedSubscription($chatId: ID!) {
messageAdded(chatId: $chatId) {
id
content
}
}
`),
{ chatId },
{
// Skip execution conditionally
skip: !chatId,
// Process each event
onData: handleData,
// Handle errors
onError: handleError,
},
);Best Practices
- Name subscriptions with
Subscriptionsuffix (e.g.,MessageAddedSubscription) - Load initial data with queries first, use subscriptions only for updates
- Handle connection errors gracefully
Next Steps
- Subscription Exchange - Learn more about subscription configuration
- Queries - Load initial data
- Exchanges - Learn about the exchange system