Express
Express runs on Node's http API, so it takes toNodeHandler(syncServer): a handler that turns the Node request into a web Request for the server, and writes the answer back. It uses the schema and server of the Quick start.
import express from 'express';
import { toNodeHandler } from 'ferrysync/server';
import { syncServer } from './sync-server';
const app = express();
// Before any global body parser, or with its limit raised: see below.
app.use('/api/sync', toNodeHandler(syncServer));
app.listen(3000);The client's url is the mount path: http({ url: 'https://api.example.com/api/sync' }).
The same handler mounts on Fastify, NestJS and plain node:http: see Node.js.
Auth
auth gets a web Request with the original headers and cookies, so most auth libraries read the
session from it as anywhere else: see Auth.
When the user only exists on the Node request (Passport's req.user, a session middleware's
req.session), a middleware in front of the handler can put it in a header for auth. It must
always remove the header the client sent:
import express from 'express';
import { createSyncServer, memoryStore, toNodeHandler } from 'ferrysync/server';
import { schema } from './schema';
const syncServer = createSyncServer({
schema,
store: memoryStore(),
auth: (request) => request.headers.get('x-sync-user'),
});
const app = express();
app.use(
'/api/sync',
(req, _res, next) => {
// Never trust the client's own: it would let anyone be anyone.
delete req.headers['x-sync-user'];
const userId = userIdOf(req);
if (userId !== null) {
req.headers['x-sync-user'] = userId;
}
next();
},
toNodeHandler(syncServer),
);Gotchas
- Body parsers are fine, with a limit that fits. When
express.json()already read the body, the handler usesreq.body. But it stops at 100 kB by default, and a device that was offline for a while pushes more: mount the sync route before it, or raise itslimit. - CORS. For a web app on another origin, put your CORS middleware before the route. Native apps send no preflight requests.
app.use('/api/sync', …), notapp.post('/api/sync', …). The endpoints are paths under the mount:/api/sync/push,/pull,/bootstrapand/wait.- Errors go to
next, as with any middleware, in the rare case the handler itself fails (a runtime without a globalRequest). Failures of the sync server are its own 500 answers. - Needs Node 18 or later, for the global
Request. - Realtime and timeouts: see Node.js.
Hono
Hono passes web Requests around, so the sync route is one line, on every runtime Hono runs on. It uses the schema and server of the Quick start.
Node.js
No framework at all: node:http, with toNodeHandler. It uses the schema of the Quick start. Frameworks on Node's http API take the same handler; Express has its own page, Fastify and Nest are below.