Client guide
Installation
npm install @jcbuisson/express-x-client socket.io-clientExample
import { io } from 'socket.io-client'
import { createClient } from '@jcbuisson/express-x-client'
const socket = io('http://localhost:8000', { transports: ['websocket'] })
const app = createClient(socket)
const user = await app.service('user').findUnique({ where: { id: 22 } })
app.service('user').on('create', (user) => {
console.log('CREATE USER event', user)
})
...Configuration
createClient(socket, options)
Options passed to createClient:
| Option | Type | Default | Description |
|---|---|---|---|
debug | boolean | false | Log socket events and service requests to the console |
Socket.io connection options (path, reconnection delays, extra headers, etc.) are passed directly to io():
const socket = io('http://localhost:8000', {
path: '/myapp-socket.io/',
transports: ['websocket'],
reconnectionDelay: 1000,
reconnectionDelayMax: 10000,
extraHeaders: {
'bearer-token': 'mytoken'
},
})Service options
Options can be passed per-service to app.service(name, options):
timeout (integer)
An exception is thrown if a service request doesn't get an answer after timeout ms. Default: 20000
app.service('mail', { timeout: 10000 }).send(...)volatile (boolean)
When volatile is set, a service request is not buffered if the connection is down — it will simply not be emitted. Default: false
Connection lifecycle
// called when socket connects or reconnects
app.addConnectListener((socket) => { ... })
app.removeConnectListener(func)
// called when socket disconnects
app.addDisconnectListener((socket) => { ... })
app.removeDisconnectListener(func)
// called on connection error
app.addErrorListener((socket) => { ... })
app.removeErrorListener(func)Handling exceptions
When an exception occurs during a service method execution on the server, the error is serialized and sent back to the client so that it can be caught:
try {
const { userId } = await app.service('auth').login(email, password)
...
} catch(err) {
if (err.code === 'wrong-credentials') {
...
}
}Use EXError on the server to attach a structured code to errors.
Publish-subscribe
Service events callbacks
When a service event is sent by the server, the client can set a callback. In the following example, the callback is executed when the client receives a 'create' event from the 'user' service. Its only argument is the data returned by the method called — here the created user.
app.service('user').on('create', (createdUser) => {
...
})Application event callbacks
When an application event is sent by the server, the client can set a callback with app.on(). In the following example, the callback is executed whenever the application-wide event 'expireAt' is sent to the client:
app.on('expireAt', (date) => {
...
})Reload plugin
reloadPlugin preserves socket rooms and socket.data (e.g. auth state) across page reloads. On reconnect, it emits a cnx-transfer message to the server to copy the previous socket's state onto the new connection.
The server-side reloadPlugin must also be configured.
import { createClient, reloadPlugin } from '@jcbuisson/express-x-client'
const socket = io('http://localhost:8000', { transports: ['websocket'] })
const app = createClient(socket)
await reloadPlugin(app)reloadPlugin uses sessionStorage internally to track the previous socket ID across page loads.
Offline plugin
offlinePlugin enables offline-first CRUD: reads and writes work even with no network, and data is automatically synchronised with the server on reconnect.
See the offline guide for full setup and usage.
import { createClient, offlinePlugin } from '@jcbuisson/express-x-client'
const socket = io('http://localhost:8000', { transports: ['websocket'] })
const app = createClient(socket)
offlinePlugin(app)
const post = app.createOfflineModel('post', ['title', 'content'])
// create a record (written locally immediately, synced to server when online)
await post.create({ title: 'Hello', content: 'World' })
// reactive observable — emits on every local change
post.getObservable({ userId: 42 }).subscribe(posts => {
console.log('posts', posts)
})