Jspreadsheet and Express
Jspreadsheet Server can attach its WebSocket traffic to an HTTP server you create, so an Express application and the spreadsheet server share one process and one port.
Full example
Create the Node HTTP server yourself, mount Express on it, and hand the same instance to Jspreadsheet Server through the server option:
const server = require('@jspreadsheet/server');
const adapter = require('@jspreadsheet/server-redis');
const express = require('express');
const http = require('http');
const app = express();
const httpServer = http.createServer(app);
app.get('/test', (req, res) => {
res.send('Hello world!');
});
// Load from ENV
require('dotenv').config();
// Jspreadsheet license
const license = {
clientId: process.env.JSS_CLIENT,
licenseKey: process.env.JSS_LICENSE
};
// Create Jspreadsheet Server
server({
config: {
cors: {
origin: "*"
},
},
port: 3000,
server: httpServer,
beforeConnect: async function(auth) {
// Allow everyone
return true;
},
beforeLoad: async function(guid, auth) {
// Allow everything
return true;
},
beforeChange: async function(guid, changes, auth) {
// Allow everything
return true;
},
load: async function(guid, auth, cachedConfiguration) {
// Load the spreadsheet when is not already in cache
if (! cachedConfiguration) {
// Load the spreadsheet from the adapter
cachedConfiguration = await adapter.load(guid, auth);
}
return cachedConfiguration;
},
change: async function(guid, changes, auth, onerror) {
return await adapter.change(guid, changes, auth, onerror);
},
create: async function(guid, config, auth) {
return await adapter.create(guid, config, auth);
},
destroy: async function(guid, auth) {
return await adapter.destroy(guid, auth);
},
error: function(e) {
console.error('Error', e)
},
license: license
});
How it works
Sharing one HTTP server and port
http.createServer(app) wraps the Express application in a plain Node HTTP server. With the server option set, Jspreadsheet Server attaches Socket.IO to that server and starts it on port. Both answer on port 3000: Express serves /test, Socket.IO handles the WebSocket traffic on its own path.
Do not call listen yourself
Jspreadsheet Server calls listen(port) on the server you pass in. Pass a server that is not yet listening and do not call app.listen() or httpServer.listen(): a second listen throws ERR_SERVER_ALREADY_LISTEN.
Route ownership
Socket.IO claims only its handshake path (/socket.io/ by default). Every other request reaches Express unchanged, so routes, static files and error handling work as usual.
Middleware behavior
Express middleware runs only for HTTP requests that Express handles. WebSocket traffic bypasses it: body parsers, sessions and loggers neither see nor interfere with it. Spreadsheet traffic is authenticated by the server's hooks, not by Express middleware.
Access control hooks
The three before* hooks decide access. The example allows everyone; in a real application, validate the auth object (for example a token issued by your login flow) and return false to reject. The client sends the token in its auth option on every interaction, so an Express login route and the hooks can share the same verification code.
Persistence with the Redis adapter
The load, change, create and destroy handlers delegate to the Redis adapter, so documents survive restarts. cachedConfiguration lets the server skip a storage read when the document is already in memory. Any adapter or custom handlers plug in the same way.
Serving the front end from Express
Express can also serve the pages that host the spreadsheet. The front end connects with the client extension to the same origin:
// Express serves the application pages
app.use(express.static('public'));
// In the front end served from /public
import jspreadsheet from 'jspreadsheet';
import client from '@jspreadsheet/client';
jspreadsheet.setExtensions({ client });
const remote = client.connect({
url: window.location.origin,
auth: { token },
});
Limitations
The REST API extension binds its own HTTP server
The REST API is provided by the api extension, which creates and binds its own HTTP server during license registration, replacing the server option. If you enable extensions: { api } and pass an Express httpServer, the REST server wins and the Express routes are not served.
To use both, run Express on one port and the REST-enabled Jspreadsheet Server on another, and route paths at a reverse proxy such as Nginx. Only the WebSocket-only setup on this page (no api extension) shares a port with Express.
Related pages
- Getting started: the minimal standalone server.
- Nginx: reverse proxy configuration, path prefixes and TLS.
- Authentication: the hooks that guard socket and REST access.
- Server options: the full
server()configuration reference.