π₯ Expose API β Call Node.js from the Browser β
Expose Node.js functions to the browser and call them directly from page JavaScript. The browser calls window.yourFunction(data) and gets back a real return value from Node.
β οΈ Version Requirement: Binary v0.1.14+ | Library v0.0.20+
How it works β
Browser JavaScript Node.js
ββββββββββββββββββ βββββββ
window.saveProduct({...}) βββββββββΊ async (data) => {
savedItems.push(data)
return { success: true, id: "..." }
}
β
βΌ
Promise resolves ββββββββββββββββββ { success: true, id: "..." }Quick Start β
import piggy from "nothing-browser"
import path from "path"
await piggy.launch({ mode: "tab", binary: path.resolve(import.meta.dir, "../a/nothing-browser-headful.exe") })
await piggy.register("app", "https://example.com")
const savedItems: any[] = []
await piggy.app.exposeFunction("saveProduct", async (data: any) => {
savedItems.push({ ...data, id: crypto.randomUUID() })
return { success: true, id: savedItems[savedItems.length - 1].id, total: savedItems.length }
})
await piggy.app.navigate()
await piggy.app.click("#save-btn")
await piggy.close()site.exposeFunction(name, handler) β
Exposes a Node.js function to the browser under window.name.
await piggy.app.exposeFunction("saveProduct", async (data: any) => {
console.log("[Node] saveProduct called with:", data)
savedItems.push({ ...data, id: crypto.randomUUID() })
return { success: true, id: savedItems[savedItems.length - 1].id, total: savedItems.length }
})The browser page can then call:
const result = await window.saveProduct({ title: "Book", price: "$9.99" })
// result β { success: true, id: "uuid...", total: 1 }Return anything serializable β objects, strings, numbers. Throw to reject the promise on the browser side.
site.unexposeFunction(name) β
Removes a previously exposed function.
await piggy.app.unexposeFunction("saveProduct")site.clearExposedFunctions() β
Removes all exposed functions for this site.
await piggy.app.clearExposedFunctions()site.exposeAndInject(name, handler, injectionJs) β
Exposes a function and immediately runs injection JS in the browser in one call.
await piggy.app.exposeAndInject(
"logToServer",
async (data: any) => {
console.log("Browser says:", data)
return { received: true }
},
(fnName) => `
window.${fnName}({ url: window.location.href, timestamp: Date.now() });
`
)injectionJs can be a plain string or a function that receives the name and returns a string.
site.addInitScript(js) β
Injects JS that runs before any page script on every navigation. Use it to set globals or mock APIs before the page loads.
await piggy.app.addInitScript(`
window.__PIGGY_DEMO__ = true;
window.__INJECTED_AT__ = Date.now();
`)
await piggy.app.addInitScript(`
const _origNow = Date.now;
window.__realNow = _origNow;
`)Must be called before
navigate(). Takes a plain string β not{ js: "..." }.
Verify it ran:
const flag = await piggy.app.evaluate(() => window.__PIGGY_DEMO__)
console.log(flag) // trueReal demo output (demo 14) β
Init script registered β
Second init script registered β
Exposed: saveProduct β
Exposed: computeHash β
__PIGGY_DEMO__ flag from initScript: true
[Node] saveProduct called with: { title: "Piggy Demo Book", price: "$9.99", timestamp: 1779101826964 }
Page result text: "Saved: ..."
Saved items in Node: 1
[Node] computeHash called with: "hello piggy"
Hash result on page: "Hash: hash-6a8a5a32"
Direct evaluate β saveProduct result: {}
Total saved items: 1
All saved: [{ title: "Piggy Demo Book", price: "$9.99", timestamp: ..., id: "ba2cdac6-..." }]Error handling β
await piggy.app.exposeFunction("riskyOp", async (data: any) => {
try {
const result = await someAsyncOperation(data)
return { success: true, result }
} catch (err: any) {
return { success: false, error: err.message }
}
})Local file pages β
If you're using a local HTML file, fix the path for Windows or Qt won't load it:
const pageUrl = `file:///${process.cwd().replace(/\\/g, "/")}/local-pages/rpc-test.html`
await piggy.register("app", pageUrl)Plain file:// + backslashes = blank white page.
API Reference β
| Method | Parameters | Returns |
|---|---|---|
exposeFunction(name, handler) | name: string, handler: (data) => any | Promise<site> |
unexposeFunction(name) | name: string | Promise<site> |
clearExposedFunctions() | β | Promise<site> |
exposeAndInject(name, handler, injectionJs) | name, handler, injectionJs: string | (name) => string | Promise<site> |
addInitScript(js) | js: string | () => void | Promise<site> |
Next Steps β
- Iframe API β Interact with iframe content
- Dialog API β Handle JavaScript dialogs
- Intercept API β Intercept and modify network requests
Nothing Ecosystem Β· Ernest Tech House Β· Kenya Β· 2026