Skip to content
codemeasandwichPublic

About

RPE: Remote procedure events

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

🦍 api-ape

npm version GitHub issues Zero Dependencies Dependabot CSRF protected Bundle Size JSS Encoding license

api-ape logo

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.


Install

npm install api-ape

Requirements: 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.)


Quick Start

Server (ape)

const { createServer } = require('http')
const { ape } = require('api-ape')

const server = createServer()
ape(server, { where: 'api' })  // Load controllers from ./api folder
server.listen(3000)

Server on Bun (api-ape/bun)

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.

Vite Plugin

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.

Controllers

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 }
}

Client (api)

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

Key Concepts

  • Auto-wiring — Drop JS files in a folder, they become API endpoints
  • Real-time broadcasts — Built-in broadcast() and broadcastOthers() 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 of where, for bun build --compile binaries
  • 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 to api/users/list.js
  • Origin check — Exact host:port matching (origin.mode: 'exact', default on Bun), allowedOrigins, allowedHosts
  • Hot reload (opt-in) — watch: true reloads 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/jss and api-ape/messageHash exported 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)

Examples

cd example/ExpressJs && npm install && npm start

Documentation

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

Contributing

  1. Fork the repository
  2. Create a branch: git checkout -b feature/your-feature
  3. Make changes and add tests
  4. Run tests: npm test
  5. Push and open a PR

Tests

npm test            # Run tests
npm run test:watch  # Watch mode
npm run test:cover  # Coverage

License

MIT — Brian Shannon

Made with 🦍 by the api-ape community

About

RPE: Remote procedure events

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages