Server API Extension
The Server API extension owns the HTTP server. It exposes the REST routes for every document (data, styles, comments, headers, media and the other worksheet modules), history snapshots and image storage, and the registration points other extensions use to add routes.
Documentation
Install
npm install @jspreadsheet/server-api
Settings
Configure the extension by calling it as a function before the server starts:
| Property | Description |
|---|---|
s3: object |
S3 credentials (key, secret, bucket, region, url). Enables the history (snapshot) routes, the image routes and api.setImage. Without it, those routes return S3 not defined. |
Routes
The extension binds an HTTP server under /api. Requests carry the token in an Authorization: Bearer header and pass through your beforeConnect/beforeLoad/beforeChange hooks.
| Route | Description |
|---|---|
POST /api/create |
Create a new document (guid, config in the body). |
GET /api/<guid> |
Full document configuration. |
DELETE /api/<guid> |
Destroy the document. |
GET /api/<guid>/<worksheetIndex> |
One worksheet's configuration. |
/api/<guid>/[<worksheetIndex>/]<module> |
Module routes (data, value, comments, style, header, media, ...). When the worksheet index is omitted, worksheet 0 is used. See the REST API reference. |
GET/POST/DELETE /api/<guid>/history[/<version>] |
List, create, restore and delete snapshots (S3 required, owner only). |
GET /api/<guid>/images/<filename> |
Serve an image stored through api.setImage (S3 required). |
Extension registration points
Other extensions add routes through three static methods, normally from their license hook (see Extensions):
| Method | Description |
|---|---|
api.setAction(fn) |
Registers global actions: fn(actions) adds handlers served at /api/<name>. |
api.setModule(fn) |
Registers document routes: fn(routers) adds handlers served at /api/<guid>/[<worksheetIndex>/]<name>, executed with the document loaded. |
api.setImage(guid, data) |
Stores an image in the configured S3 bucket and returns its URL. Used, for example, to offload base64 images from setMedia changes. |
Usage Example
S3 storage plus a MongoDB adapter wired through the server options:
const server = require('@jspreadsheet/server');
const adapter = require('@jspreadsheet/server-mongodb');
const api = require('@jspreadsheet/server-api');
// License
const license = {
clientId: '356a192b7913b04c54574d18c28d46e6395428ab',
licenseKey: 'MmIyMDhmYmY4NGI1ZDY1ODAwNThjMGZkOTVkNjg2MmQ1NzZmYTFhOTBmZWI3N2M3ZmQ1N2Q3YjMwNDNhMjRhYmViYmRkNGVjZjZlMmNkNDVhODJhYzg1ZmRiY2E3OTJhYjA1ODQzNTliZGZiMmYwNWM4YmRmMjAyZmUwODA1NmEsZXlKamJHbGxiblJKWkNJNklqTTFObUV4T1RKaU56a3hNMkl3TkdNMU5EVTNOR1F4T0dNeU9HUTBObVUyTXprMU5ESTRZV0lpTENKdVlXMWxJam9pU25Od2NtVmhaSE5vWldWMElpd2laR0YwWlNJNk1UYzBNak0wTWpRd01Dd2laRzl0WVdsdUlqcGJJbXB6YUdWc2JDNXVaWFFpTENKamMySXVZWEJ3SWl3aWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0luVmxMbU52YlM1aWNpSXNJbU5rY0c0dWFXOGlMQ0pwYm5SeVlYTm9aV1YwY3k1amIyMGlMQ0p6Wm1OdlpHVmliM1F1WTI5dElpd2lkMlZpSWl3aWJHOWpZV3hvYjNOMElsMHNJbkJzWVc0aU9pSXpOQ0lzSW5OamIzQmxJanBiSW5ZM0lpd2lkamdpTENKMk9TSXNJbll4TUNJc0luWXhNU0lzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5KbGJtUmxjaUlzSW5CaGNuTmxjaUlzSW1sdGNHOXlkR1Z5SWl3aWRtRnNhV1JoZEdsdmJuTWlMQ0pqYjIxdFpXNTBjeUlzSW5ObFlYSmphQ0lzSW1Ob1lYSjBjeUlzSW5CeWFXNTBJaXdpWW1GeUlpd2ljMmhsWlhSeklpd2lZMnh2ZFdRaUxDSnRZWE5ySWl3aWMyaGxaWFJ6SWl3aWMyVnlkbVZ5SWl3aWFXNTBjbUZ6YUdWbGRITWlYWDA9'
}
// Connect API to S3 for history and image upload
api({
s3: {
key: "",
secret: "",
bucket: "",
region: "",
url: "",
},
});
server({
port: 3000,
load: async function(guid, auth, cachedConfiguration) {
return await adapter.load(guid, auth, 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);
},
replace: async function(guid, config, auth) {
return await adapter.replace(guid, config, auth);
},
error: function(e) {
console.error('Error', e)
},
license: license,
extensions: { api },
});
Client API
Any HTTP client can call the REST routes. Write bodies are form-encoded; see HTTP conventions. No additional package is required: the example uses the standard fetch API available in browsers and Node.js.
Example: Updating Comments
POST /api/<guid>/<worksheetIndex>/comments sets comments. Body keys are cell names: a string value creates a simple note, and bracket-notation arrays create threaded comments (each entry requires comments and date, with optional name, user_id and edited):
// API Server
const baseUrl = "http://localhost:8009/api";
// Your authentication token
const token = "eyJhbGciOiJIUzUxMiIsInR5cCJ9.eyJkb21haW4iOiJsb2NhbGhvc3Q6ODAPQSJ9.Xr2Ir2-zEc_tqV5y6i";
// Spreadsheet Guid
const guid = '79b45919-c751-4e2b-a49a-6c1286e2fc03';
// New comments, keyed by cell name
const body = new URLSearchParams();
body.append('A1', 'first comment');
body.append('B3[0][comments]', 'Something');
body.append('B3[0][date]', new Date().toISOString());
body.append('B3[0][name]', 'Random name');
// Set new comments on worksheet 0
fetch(`${baseUrl}/${guid}/0/comments`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
},
body,
})
.then((response) => response.json())
.then((result) => {
// { message: 'Done', rev: <n> }
console.log(result);
})
.catch((err) => {
console.log(err);
});