Three moves: a Storage for files, createEditor to wire it, two routes to serve it.
1. Install
npm install @xtrable-ltd/nanoesis
2. Wire the editor
Give it two folders and a login. folderStorage and devNoAuth are built in.
// editor.js
import { createEditor, devNoAuth } from '@xtrable-ltd/nanoesis/editor-api';
import { folderStorage } from '@xtrable-ltd/nanoesis/adapter-fs';
export const editor = createEditor({
editorFiles: folderStorage('./site'), // your editable files
website: folderStorage('./public'), // where the built site is written
login: devNoAuth(), // dev only; swap for real login
});
3. Serve two routes
Send /api/ requests to the editor; serve everything else as the editor UI.
// server.js
import { createServer } from 'node:http';
import { serveEditorAsset } from '@xtrable-ltd/nanoesis/editor-api';
import { editorDist } from '@xtrable-ltd/nanoesis/editor';
import { editor } from './editor.js';
createServer((req, res) => {
const url = new URL(req.url, 'http://localhost');
(async () => {
if (url.pathname.startsWith('/api/')) {
const chunks = [];
for await (const c of req) chunks.push(c);
const r = await editor.handleApi({
method: req.method,
path: url.pathname,
query: url.searchParams,
getHeader: (n) => req.headers[n.toLowerCase()],
body: async () => new Uint8Array(Buffer.concat(chunks)),
});
res.writeHead(r.status, r.headers);
res.end(typeof r.body === 'string' ? r.body : Buffer.from(r.body ?? ''));
} else {
const a = await serveEditorAsset(editorDist, url.pathname);
res.writeHead(a.status, a.headers);
res.end(typeof a.body === 'string' ? a.body : Buffer.from(a.body));
}
})();
}).listen(3000, () => console.log('http://localhost:3000'));
4. Run it
node server.js
Open http://localhost:3000, create a page, and press Publish. The built site appears in ./public, ready for any static host.
Going further
A Storage is just three methods, so you can point nanoesis at anything, an S3 bucket, a database, a Map:
const myStorage = {
async get(path) { /* return bytes, or undefined if missing */ },
async put(path, bytes) { /* save bytes */ },
async delete(path) { /* remove */ },
};
Before going live, swap devNoAuth() for a real login. The cloud guides show a full setup.