Docly JavaScript is not Node
What you'll see
Code written for a Docly workspace opens with const fs = require("fs"), calls await fetch(url), reads process.env.API_KEY, or arrives with a package.json and an npm install step. An AI coding agent asked to add a dependency proposes installing a package.
None of it works, and the failure is rarely a clean error — it is an undefined symbol part-way through a page render, so the page just stops producing output at that point.
What's actually happening
Docly's server-side JavaScript engine is HashJS. It runs on V8, so the language is JavaScript, but the surrounding runtime is Docly's, not Node's. Nothing from the Node standard library is present, and there is no package manager: the absence of package.json and node_modules in a Docly workspace is the normal state, not a missing setup step.
The same applies in the browser direction — this is not browser JavaScript either. There is no window, no document, and no fetch on the server side.
One name genuinely collides. docly.process() exists, but it is a script evaluator — it executes a Docly script string with a supplied set of globals, and requires specific access because of the back-door risk. It has nothing to do with Node's process object, and there is no process.env.
What you get instead is a single platform library, docly.*, covering filesystem, HTTP, mail, images, authentication, sharing and output; plus import for your own code. The standard built-ins are there as usual: JSON, Math, String, Array, Date, console.log.
What to do
Reach for the Docly equivalent rather than the Node idiom:
| Instead of | Use |
|---|---|
require("fs"), fs.readFile | Filesystem functions — docly.getFile, docly.getFiles, docly.getJson, docly.saveFile, docly.saveJson |
fetch, axios, node-fetch | Network functions — docly.httpGet (JSON-parsed body), docly.httpPost, docly.httpFetch (full response: status, headers, raw body) |
nodemailer | Mail functions — docly.smtp.send, plus POP3 and IMAP |
process.env.SECRET | A JSON file under #/, read with docly.getJson(). The #/ tree is never served over HTTP — see Keep private data under the # folder |
npm install <pkg> | A Docly package, or your own .js file under #/ |
require("./helpers") | import { helper } from "#/helpers.js" — absolute paths from site root, no relative ./. Named, namespace and default imports all work |
module.exports | export default / named export |
Express req / res | Auto-bound function parameters plus the query and form globals; the returned object is the response. See Reading query and form fields in API calls |
path.join, relative paths | Absolute paths from site root. In anything the browser will execute, use ~/ — see Use tilde paths in JavaScript |
A minimal API endpoint, for orientation:
// #/API/orders/list.js
import { format } from "#/helpers.js";
export default () => {
let rows = docly.getFiles("/Orders", { recursive: true });
return { count: rows.length, items: rows.map(format) };
}; When you are unsure what a function returns, print it rather than assuming a Node-shaped result. A throwaway .hash page is the fastest way — see Use a scratch hash file to test Docly functions.
Start from the JavaScript reference, and read About JavaScript and HASH files before writing much of anything.