Remote Procedure Events (RPE) — A lightweight WebSocket framework for building real-time APIs. Call server functions from the browser like local methods. Get real-time broadcasts with zero setup.
npm install api-apeRequirements: Node.js for the server (tested on 22 and 24) and modern browsers for the client. Only the server-side client (api.connect(host, port) from one Node service to another) needs Node 22 or newer, for its global WebSocket.
Security: use 4.5.0 or later. Every earlier version let any client that could open a socket make the server construct arbitrary globals through a crafted error in a frame (code execution under Bun, outbound connections under Node and Bun). See the CHANGELOG before upgrading: it also lists the breaking changes. (4.4.0 was tagged but never published to npm; its fixes are in 4.5.0.)
const { createServer } = require('http')
const { ape } = require('api-ape')
const server = createServer()
ape(server, { where: 'api' }) // Load controllers from ./api folder
server.listen(3000)const { apeBun } = require('api-ape/bun')
const ape = apeBun({ where: 'api' }) // Origin must equal Host (host:port) by default
Bun.serve({
port: 3000,
fetch: (req, server) => ape.fetch(req, server) ?? new Response('Not found', { status: 404 }),
websocket: ape.websocket,
})A compiled single-file executable has no folder to scan, so pass your imported controllers instead of where: apeBun({ controllers: { 'cards/move': move } }) (see Compiled executables).
Bun is WebSocket-only (the HTTP poll fallback and /ape/data file transfer are Node-only). See Bun, Origin check, Hot reload and Keepalive in the server docs.
For Vite projects, use the built-in plugin for plug-and-play integration:
// vite.config.js
import { defineConfig } from 'vite'
import apiApe from 'api-ape/vite'
export default defineConfig({
plugins: [
apiApe({
where: 'api',
onConnect: './ape/onConnect', // Optional: path to connection handler
logging: false, // Optional: silence api-ape dev-server framework logs
})
]
})Run npm run dev — api-ape runs directly on Vite's dev server. No proxy needed.
Drop a file in your api/ folder — it automatically becomes an endpoint:
// api/hello.js
module.exports = function(name) {
return `Hello, ${name}!`
}
// api/message.js
module.exports = function(text) {
this.broadcastOthers('message', { text, from: this.clientId })
return { sent: true }
}Browser:
<script src="/api/ape.js"></script>
<script>
const result = await api.hello('World') // "Hello, World!"
// Subscribe to messages (pass a callback)
const unsub = api.message(data => console.log(data))
</script>Bundlers (React, Vue, etc.):
import api from 'api-ape'
const result = await api.hello('World')
// Subscribe (pass callback) vs RPC call (pass data)
const unsub = api.message(data => console.log(data)) // Subscribe
await api.message({ text: 'Hello' }) // RPC call- Auto-wiring — Drop JS files in a folder, they become API endpoints
- Real-time broadcasts — Built-in
broadcast()andbroadcastOthers()methods - Pub/Sub channels — Clients subscribe to channels, server publishes updates
- Subscribe authorisation —
onSubscribe(channel, context)decides who may subscribe;clearLastMessage(channel)forgets a channel's retained message - Compiled executables —
controllers: { 'cards/move': fn }in place ofwhere, forbun build --compilebinaries - Clock skew — a client whose clock is a little fast is no longer refused (
maxClockSkewMs, default 5 s); the bundled clients stamp calls in the server's clock, read from__connected__ - Promise-based calls —
api.users.list()maps toapi/users/list.js - Origin check — Exact host:port matching (
origin.mode: 'exact', default on Bun),allowedOrigins,allowedHosts - Hot reload (opt-in) —
watch: truereloads controllers and the services they import together; off by default - Automatic reconnection — Client reconnects with exponential backoff
- HTTP fallback — Falls back to long polling when WebSockets are blocked
- Wire protocol — The frames on the socket, with
api-ape/jssandapi-ape/messageHashexported so other programs can speak it - JSS Encoding — Supports Date, RegExp, Error, Set, Map over the wire
- 🌲 Forest — Distributed mesh for horizontal scaling
- 🔐 Authentication — OPAQUE/PAKE auth with tiered access control
- Framework logging — Silence or redirect internal api-ape diagnostics (
logging/configureApeLogging)
example/ExpressJs/— Simple real-time chatexample/NextJs/— Production chat with React & Dockerexample/Vite/— Vite + Vueexample/Bun/— Bun runtime
cd example/ExpressJs && npm install && npm start| Docs | Description |
|---|---|
| Server README | API reference, lifecycle hooks, auto-routing, file transfers, 🌲 Forest |
| Client README | Client usage, connection states, file transfers, security |
| Auth README | OPAQUE authentication, tiered access, authorization |
| Adapters README | Database adapters for Forest |
- Fork the repository
- Create a branch:
git checkout -b feature/your-feature - Make changes and add tests
- Run tests:
npm test - Push and open a PR
npm test # Run tests
npm run test:watch # Watch mode
npm run test:cover # CoverageMIT — Brian Shannon
Made with 🦍 by the api-ape community
