Skip to content

Client

The client module creates a Socket.IO-backed client for caching, invalidation, and live query support.

createClient returns an extended URQL Client with additional SocketQL methods. The connection is not started automatically — use the SocketQLProvider or call connect() manually.

import { createClient } from '@requence/socketql/client'
const client = createClient({
auth: () => ({ token: getAccessToken() }),
})

The returned client is a standard URQL Client extended with additional methods for connection management, cache invalidation, and event subscriptions. See the createClient reference for the full list.

The SocketQLProvider wraps URQL’s Provider and manages the connection lifecycle. It calls connect() on mount and, by default, propagates connection errors to the nearest React error boundary. Pass onConnectError to handle errors manually instead (e.g. to trigger client.reconnect()).

import { createClient } from '@requence/socketql/client'
import { SocketQLProvider } from '@requence/socketql/client/react'
const client = createClient({
auth: () => ({ token: getAccessToken() }),
})
function App() {
return (
<ErrorBoundary fallback={<LoginPage />}>
<SocketQLProvider client={client}>
<Main />
</SocketQLProvider>
</ErrorBoundary>
)
}

When the server rejects the connection (via ConnectionRejectedError), the error is thrown during render and caught by the error boundary — unless onConnectError is set. The same client instance can be reused after the error boundary resets or after a reconnect() call.

See the SocketQLProvider reference for the full props list.

Provide auth data using the auth option. This function is called on every connection attempt, including reconnects:

const client = createClient({
auth: () => ({
token: getAccessToken(),
}),
})

When using httpOnly cookies, no auth option is needed. The SocketQLProvider handles the connection lifecycle automatically. If the login page is part of the same SPA, only mount the provider after the user has authenticated.

When the server rejects a connection, the error includes an optional data property for structured context:

class ConnectionError extends Error {
data?: Record<string, any>
}

Register a handler at creation time:

const client = createClient({
onConnectError: (err) => {
console.log(err.message) // "Subscription expired"
console.log(err.data) // { code: 'EXPIRED' }
},
})

Subscribe and unsubscribe dynamically:

const unsubscribe = client.onConnectError((err) => {
console.log(err.message, err.data)
})
// Later
unsubscribe()

Manually invalidate queries by operation name or document:

import { MyQueryDocument } from './generated'
// By operation name
client.invalidate('MyQuery')
// By document node
client.invalidate(MyQueryDocument)
// Multiple at once
client.invalidate(['QueryA', 'QueryB'])

The client exposes the underlying Socket.IO manager’s reconnect events. Each method returns an unsubscribe function:

const unsubReconnect = client.onReconnect(() => {
console.log('Reconnected successfully')
})
const unsubAttempt = client.onReconnectAttempt((attempt) => {
console.log(`Reconnect attempt #${attempt}`)
})
const unsubError = client.onReconnectError((err) => {
console.error('Reconnect error:', err.message)
})
const unsubFailed = client.onReconnectFailed(() => {
console.error('All reconnect attempts failed')
})
// Later
unsubReconnect()
unsubAttempt()
unsubError()
unsubFailed()

The socket method exposes the underlying Socket.IO manager’s socket(), allowing you to create additional namespace sockets that share the same connection:

const chatSocket = client.socket('/chat')
chatSocket.on('message', (msg) => {
console.log('New chat message:', msg)
})