Skip to content
On this page

Client guide

Installation

bash
npm install @jcbuisson/express-x-client socket.io-client

Example

js
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:

OptionTypeDefaultDescription
debugbooleanfalseLog socket events and service requests to the console

Socket.io connection options (path, reconnection delays, extra headers, etc.) are passed directly to io():

js
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

js
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

js
// 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:

js
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.

js
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:

js
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.

js
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.

js
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)
})