Build with Dehook
A function in your browser. The same function from local JavaScript. One backend behind both.
Start with your app
Dehook gives you a backend by exporting ordinary JavaScript functions. The browser and local JavaScript clients share the same app, token pair, functions, and document store.
Your active public token is loading. Use the workspace selector to switch backends. Open Settings for credentials or to create a new backend.
Preparing your snippetDehook is already loaded on this page. Open your browser console to try the examples directly.
Export and run functions
Export a named function once. It stays on your VPS until you overwrite it. Call it by name from either client.
dehook.export(function hello(data) {
return { response: `Hello ${data.name}!` };
});
dehook.run('hello', { name: 'Ada' }, (response, error) => {
if (error) return console.error(error.message);
console.log(JSON.parse(response));
});export() returns { processorURI }. Calls are sent in order, so a run placed after an export waits for it. Callbacks receive the JSON response as a string and an optional error.
dehook.exports((functions, error) => {
if (error) return console.error(error.message);
console.log(Object.keys(functions));
});
dehook.import('hello', (source, error) => {
if (error) return console.error(error.message);
console.log(source);
});Store and query documents
Each token pair has its own persistent JSON document store. Direct client queries require the private token.
dehook.query({ put: { type: 'note', text: 'Hello Dehook', done: false } });
dehook.query({ search: { type: 'note' } }, (response, error) => {
if (error) return console.error(error.message);
console.log(JSON.parse(response));
});
dehook.query({ update: { type: 'note' } }, { done: true });
dehook.query({ remove: { type: 'note', done: true } });Inside a backend function
Use the synchronous query bridge directly. Its results are already JavaScript values.
dehook.export(function addNote(data) {
const text = String(data.text || '').trim().slice(0, 500);
if (!text) throw new Error('A note needs some text');
const id = query.put({ type: 'note', text: text });
return { id: id, notes: query.search({ type: 'note' }) };
});Filter, update, and paginate
dehook.export(function recentTasks() {
query.update({ type: 'task', priority: { gt: 5 } }, { urgent: true });
return query.search({ type: 'task', priority: { gt: 3 } }, { start: 0, limit: 10 });
});Filters support equality values and eq, ne, gt, and lt. Search supports start and limit. Put adds an id and created_at timestamp.
Browser sessions
Dehook resolves a browser session automatically. Backend functions can read and update its session object. Local JavaScript calls use __dehook_local, matching the original local client behavior.
dehook.export(function visit() {
session.visits = (session.visits || 0) + 1;
return { visits: session.visits };
});
dehook.run('visit', {}, (response) => console.log(JSON.parse(response)));dehook.export(function logout() {
session.destroy();
return { response: 'Session cleared' };
});Session identity expires after 48 hours. The server's session registry is in memory and resets on a service restart. Sessions are a building block for your application, not a complete authentication system.
From browser to local JavaScript
Download the client for the active app. It includes your public and private tokens, so it connects to exactly the same backend as this page.
Preparing your downloadconst dehook = require('./dehook.js');
// This is the function you exported from your browser.
dehook.run('hello', { name: 'JavaScript' }, (response, error) => {
if (error) throw error;
console.log(JSON.parse(response));
});
// You can call this from your browser immediately after it is exported.
dehook.export(function local() {
return { response: 'Hello from JavaScript!' };
});Local JavaScript works with Veloce and Node.js with built-in fetch. Choose one runtime.
node app.jsveloce --allow-read=. --allow-net=dehook.me app.jsexport, run, query, exports, and import use the same syntax in both runtimes. The local client needs no Node.js or npm packages.
Native Veloce inside Dehook
Dehook embeds Veloce for every exported function. Your function gets native modules plus the host-provided query and session bridges.
query API uses a Rust bridge to its per-app JSON document store. It does not use Veloce ORM.Native hashes and identifiers
Use Veloce's native crypto functions inside an exported function, then persist the result through Dehook's document bridge.
dehook.export(function fingerprint(data) {
const text = String(data.text || '').slice(0, 8192);
const fingerprint = {
type: 'fingerprint',
traceId: crypto.randomUUID(),
sha256: crypto.sha256Hex(text)
};
const id = query.put(fingerprint);
return { id: id, fingerprint: fingerprint };
});
dehook.run('fingerprint', { text: 'Hello Dehook' }, (response) => {
console.log(JSON.parse(response));
});The built-in crypto module is also available. For example, require('crypto').createHash('sha512').update(text).digest('hex') computes a native SHA-512 digest.
Parse and write YAML
YAML.load and YAML.dump call Veloce's native YAML parser and serializer.
dehook.export(function parseConfig(data) {
const config = YAML.load(String(data.yaml || '').slice(0, 8192));
return { config: config, yaml: YAML.dump(config) };
});
dehook.run('parseConfig', { yaml: 'name: Ada\nrole: builder' }, (response) => {
console.log(JSON.parse(response));
});Native numeric kernels
Veloce's kernel bridges numeric arrays to native code. This example computes a sum and dot product.
dehook.export(function calculate(data) {
const left = data.left;
const right = data.right;
if (!Array.isArray(left) || !Array.isArray(right)
|| left.length !== right.length || left.length > 1000
|| !left.every(Number.isFinite) || !right.every(Number.isFinite)) {
throw new Error('Pass two equally sized numeric arrays, up to 1000 items');
}
return {
sum: Veloce.kernel.sumF64(left),
dot: Veloce.kernel.dot(left, right)
};
});
dehook.run('calculate', { left: [1, 2, 3], right: [4, 5, 6] }, (response) => {
console.log(JSON.parse(response)); // { sum: 6, dot: 32 }
});Can an exported function use Veloce ORM?
Direct ORM connections are currently blocked inside exported functions. The runtime's database bindings need app-owned connections and consistent database permissions before Dehook can expose them safely. Veloce's standalone ORM is separate from Dehook's query store.
Use query for persistent backend data today. For ORM-backed workflows, use it in your local Veloce program as shown below.
Use ORM in your local Veloce app
Your local Veloce program can use its own ORM database under the permissions you grant it. This example copies notes from your Dehook app into a separate local SQLite database.
const dehook = require('./dehook.js');
const orm = require('orm');
const path = require('path');
const notes = orm.table('notes', {
id: orm.serial().primaryKey(),
text: orm.text().notNull()
});
const db = orm.connect({ provider: 'sqlite', path: path.resolve('notes.sqlite') });
orm.migrate(db, { notes });
dehook.query({ search: { type: 'note' } }, (response, error) => {
try {
if (error) throw error;
for (const note of JSON.parse(response)) {
db.insert(notes).values({ text: String(note.text) }).execute();
}
console.log(db.select().from(notes).execute());
} finally {
db.close();
}
});veloce --allow-read=. --allow-write=. --allow-net=dehook.me sync-notes.jsThe local database is independent of the backend store. Running this example again appends another copy of the returned notes.
Other native APIs and permissions
| API | Inside an exported function |
|---|---|
| Crypto hashes and UUIDs | Available; examples above |
| YAML parse and dump | Available |
| Numeric array kernels | Available |
| Dehook query and session | Available through app-scoped bridges |
| Native ORM / SQL connections | Blocked until app-scoped access is implemented |
| Filesystem, network, shell, and servers | Blocked inside exported functions |
Outside exported functions, your local Veloce program can use native APIs under its CLI permissions. That does not grant the remote function those permissions.
// Runs locally in Veloce, outside dehook.export(...).
const response = await fetch('https://dehook.me/dehook.min.js');
console.log(response.status);veloce --allow-net=dehook.me local-fetch.jsCall your function with curl
Publish a function
Use your private token to publish hello. This request is already configured for your active app.
Preparing your publish requestRun the function
Export hello first, then copy this request. It already uses your active public token.
Preparing your requestFor another function, replace hello in the URL and send its input under data. The private token is not required to run public functions.
Claim a backend
Choose the app in Your workspace, then select Claim backend. Sign in with Google, GitHub or an email link and confirm the selected public app token. Your app ID, function URLs, documents and functions stay the same.
The previous anonymous private token stops working after the claim. It is removed from this browser only after the server confirms ownership. Manage the backend at Your account, or download a replacement deploy key there for local JavaScript and agents. Deploy keys grant app administration, so keep them private. Public function calls and public function source remain public.
Your own primary storage
Claiming with email, Google or GitHub keeps storage on the Dehook VPS until you explicitly change it. Claiming does not increase storage limits.
From your account, connect Google Drive or GitHub separately from sign-in. Drive creates a file in the consenting user's own account. GitHub accepts a private repository owned by the connected GitHub user. The 51lab developer account supplies the OAuth application, never shared user storage.
You can also connect a storage client on your own VPS from the account page. It uses outbound HTTPS and needs no public port. Download the configuration and client, start it, then verify and activate it as primary storage. VPS setup guide.
The selected provider stores the live app snapshot. The VPS keeps a working cache, reads the provider before each app operation, and confirms writes there before returning success. Provider outages and revision conflicts return errors; uncertain mutations must be inspected before retrying. Git history retains older snapshots. Existing Dehook limits still apply.
Going live
Use the public-only snippet in pages you share. The private token grants function publishing and direct document access.
Preparing your public snippetKeep your configured local client private: its source contains your private token. Do not put secrets in exported functions; function source is readable using the public token. Implement authorization in exported functions before handling private data.
Changing apps
New backend in Settings creates a separate backend. Existing pairs stay in this browser's app selector. Download the client or save the pair before changing devices or clearing browser storage.
API reference
| Method | What it does | Needs private token |
|---|---|---|
export(fn, callback?) | Publish or overwrite a named function | Yes |
run(name, data, callback) | Run a published function | No |
query(query, options?, callback?) | Read or change app documents | Yes |
exports(callback) | Get function names and source as an object | No |
import(name, callback) | Get one function's source | No |
Run and query callbacks receive (responseText, error). Exports and import callbacks receive (value, error). Export callbacks receive (responseText, error), and export immediately returns the public function URL.
Free apps have an 8 KiB document/session budget and 6 KiB of serialized function code, with bounded metadata bringing the total to 16 KiB. Functions are limited to 2 KiB of source and 8 KiB replies. Execution has a 500 ms JavaScript deadline and a 1-second outer worker deadline. Storage and request limits are enforced server-side. Private credentials can read usage through POST /app/usage and recover data through POST /app/export. Existing larger stores remain exportable and can shrink.